Scrapy爬蟲框架集成selenium及全面詳細(xì)講解
一、架構(gòu)介紹
Scrapy一個(gè)開源和協(xié)作的框架,其最初是為了頁(yè)面抓取 (更確切來說, 網(wǎng)絡(luò)抓取 )所設(shè)計(jì)的,使用它可以以快速、簡(jiǎn)單、可擴(kuò)展的方式從網(wǎng)站中提取所需的數(shù)據(jù)。但目前Scrapy的用途十分廣泛,可用于如數(shù)據(jù)挖掘、監(jiān)測(cè)和自動(dòng)化測(cè)試等領(lǐng)域,也可以應(yīng)用在獲取API所返回的數(shù)據(jù)(例如 Amazon Associates Web Services ) 或者通用的網(wǎng)絡(luò)爬蟲。
Scrapy 是基于twisted框架開發(fā)而來,twisted是一個(gè)流行的事件驅(qū)動(dòng)的python網(wǎng)絡(luò)框架。因此Scrapy使用了一種非阻塞(又名異步)的代碼來實(shí)現(xiàn)并發(fā)。整體架構(gòu)大致如下
IO多路復(fù)用

# 引擎(EGINE)(大總管)
引擎負(fù)責(zé)控制系統(tǒng)所有組件之間的數(shù)據(jù)流,并在某些動(dòng)作發(fā)生時(shí)觸發(fā)事件。有關(guān)詳細(xì)信息,請(qǐng)參見上面的數(shù)據(jù)流部分。
# 調(diào)度器(SCHEDULER)
用來接受引擎發(fā)過來的請(qǐng)求, 壓入隊(duì)列中, 并在引擎再次請(qǐng)求的時(shí)候返回. 可以想像成一個(gè)URL的優(yōu)先級(jí)隊(duì)列, 由它來決定下一個(gè)要抓取的網(wǎng)址是什么, 同時(shí)去除重復(fù)的網(wǎng)址
# 下載器(DOWLOADER)
用于下載網(wǎng)頁(yè)內(nèi)容, 并將網(wǎng)頁(yè)內(nèi)容返回給EGINE,下載器是建立在twisted這個(gè)高效的異步模型上的
# 爬蟲(SPIDERS)
SPIDERS是開發(fā)人員自定義的類,用來解析responses,并且提取items,或者發(fā)送新的請(qǐng)求
# 項(xiàng)目管道(ITEM PIPLINES)
在items被提取后負(fù)責(zé)處理它們,主要包括清理、驗(yàn)證、持久化(比如存到數(shù)據(jù)庫(kù))等操作
# 兩個(gè)中間件
-爬蟲中間件
-下載中間件(用的最多,加頭,加代理,加cookie,集成selenium)
二、安裝創(chuàng)建和啟動(dòng)
# 1 框架 不是 模塊
# 2 號(hào)稱爬蟲界的django(你會(huì)發(fā)現(xiàn),跟django很多地方一樣)
# 3 安裝
-mac,linux平臺(tái):pip3 install scrapy
-windows平臺(tái):pip3 install scrapy(大部分人可以)
- 如果失?。?
1、pip3 install wheel #安裝后,便支持通過wheel文件安裝軟件,wheel文件官網(wǎng):https://www.lfd.uci.edu/~gohlke/pythonlibs
3、pip3 install lxml
4、pip3 install pyopenssl
5、下載并安裝pywin32:https://sourceforge.net/projects/pywin32/files/pywin32/
6、下載twisted的wheel文件:http://www.lfd.uci.edu/~gohlke/pythonlibs/#twisted
7、執(zhí)行pip3 install 下載目錄\Twisted-17.9.0-cp36-cp36m-win_amd64.whl
8、pip3 install scrapy
# 4 在script文件夾下會(huì)有scrapy.exe可執(zhí)行文件
-創(chuàng)建scrapy項(xiàng)目:scrapy startproject 項(xiàng)目名 (django創(chuàng)建項(xiàng)目)
-創(chuàng)建爬蟲:scrapy genspider 爬蟲名 要爬取的網(wǎng)站地址 # 可以創(chuàng)建多個(gè)爬蟲
# 5 命令啟動(dòng)爬蟲
-scrapy crawl 爬蟲名字
-scrapy crawl 爬蟲名字 --nolog # 沒有日志輸出啟動(dòng)
# 6 文件執(zhí)行爬蟲(推薦使用)
-在項(xiàng)目路徑下創(chuàng)建一個(gè)main.py,右鍵執(zhí)行即可
from scrapy.cmdline import execute
# execute(['scrapy','crawl','chouti','--nolog']) # 沒有設(shè)置日志級(jí)別
execute(['scrapy','crawl','chouti']) # 設(shè)置了日志級(jí)別
三、配置文件目錄介紹
-crawl_chouti # 項(xiàng)目名
-crawl_chouti # 跟項(xiàng)目一個(gè)名,文件夾
-spiders # spiders:放著爬蟲 genspider生成的爬蟲,都放在這下面
-__init__.py
-chouti.py # 抽屜爬蟲
-cnblogs.py # cnblogs 爬蟲
-items.py # 對(duì)比django中的models.py文件 ,寫一個(gè)個(gè)的模型類
-middlewares.py # 中間件(爬蟲中間件,下載中間件),中間件寫在這
-pipelines.py # 寫持久化的地方(持久化到文件,mysql,redis,mongodb)
-settings.py # 配置文件
-scrapy.cfg # 不用關(guān)注,上線相關(guān)的
# 配置文件settings.py
ROBOTSTXT_OBEY = False # 是否遵循爬蟲協(xié)議,強(qiáng)行運(yùn)行
USER_AGENT = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.149 Safari/537.36' # 請(qǐng)求頭中的ua,去瀏覽器復(fù)制,或者用ua池拿
LOG_LEVEL='ERROR' # 這樣配置,程序錯(cuò)誤信息才會(huì)打印,
#啟動(dòng)爬蟲直接 scrapy crawl 爬蟲名 就沒有日志輸出
# scrapy crawl 爬蟲名 --nolog # 配置了就不需要這樣啟動(dòng)了
# 爬蟲文件
class ChoutiSpider(scrapy.Spider):
name = 'chouti' # 爬蟲名字
allowed_domains = ['https://dig.chouti.com/'] # 允許爬取的域,想要多爬就注釋掉
start_urls = ['https://dig.chouti.com/'] # 起始爬取的位置,爬蟲一啟動(dòng),會(huì)先向它發(fā)請(qǐng)求
def parse(self, response): # 解析,請(qǐng)求回來,自動(dòng)執(zhí)行parser,在這個(gè)方法中做解析
print('---------------------------',response)

四、爬取數(shù)據(jù),并解析
# 1 解析,可以使用bs4解析
from bs4 import BeautifulSoup
soup=BeautifulSoup(response.text,'lxml')
soup.find_all() # bs4解析
soup.select() # css解析
# 2 內(nèi)置的解析器
response.css
response.xpath
# 內(nèi)置解析
# 所有用css或者xpath選擇出來的都放在列表中
# 取第一個(gè):extract_first()
# 取出所有extract()
# css選擇器取文本和屬性:
# .link-title::text # 取文本,數(shù)據(jù)都在data中
# .link-title::attr(href) # 取屬性,數(shù)據(jù)都在data中
# xpath選擇器取文本和屬性
# .//a[contains(@class,"link-title")/text()]
#.//a[contains(@class,"link-title")/@href]
# 內(nèi)置css選擇期,取所有
div_list = response.css('.link-con .link-item')
for div in div_list:
content = div.css('.link-title').extract()
print(content)
五、數(shù)據(jù)持久化
# 方式一(不推薦)
-1 parser解析函數(shù),return 列表,列表套字典
# 命令 (支持:('json', 'jsonlines', 'jl', 'csv', 'xml', 'marshal', 'pickle')
# 數(shù)據(jù)到aa.json文件中
-2 scrapy crawl chouti -o aa.json
# 代碼:
lis = []
for div in div_list:
content = div.select('.link-title')[0].text
lis.append({'title':content})
return lis
# 方式二 pipline的方式(管道)
-1 在items.py中創(chuàng)建模型類
-2 在爬蟲中chouti.py,引入,把解析的數(shù)據(jù)放到item對(duì)象中(要用中括號(hào))
-3 yield item對(duì)象
-4 配置文件配置管道
ITEM_PIPELINES = {
# 數(shù)字表示優(yōu)先級(jí)(數(shù)字越小,優(yōu)先級(jí)越大)
'crawl_chouti.pipelines.CrawlChoutiPipeline': 300,
'crawl_chouti.pipelines.CrawlChoutiRedisPipeline': 301,
}
-5 pipline.py中寫持久化的類
spider_open # 方法,一開始就打開文件
process_item # 方法,寫入文件
spider_close # 方法,關(guān)閉文件保存到文件
# choutiaa.py 爬蟲文件
import scrapy
from chouti.items import ChoutiItem # 導(dǎo)入模型類
class ChoutiaaSpider(scrapy.Spider):
name = 'choutiaa'
# allowed_domains = ['https://dig.chouti.com/'] # 允許爬取的域
start_urls = ['https://dig.chouti.com//'] # 起始爬取位置
# 解析,請(qǐng)求回來,自動(dòng)執(zhí)行parse,在這個(gè)方法中解析
def parse(self, response):
print('----------------',response)
from bs4 import BeautifulSoup
soup = BeautifulSoup(response.text,'lxml')
div_list = soup.select('.link-con .link-item')
for div in div_list:
content = div.select('.link-title')[0].text
href = div.select('.link-title')[0].attrs['href']
item = ChoutiItem() # 生成模型對(duì)象
item['content'] = content # 添加值
item['href'] = href
yield item # 必須用yield
# items.py 模型類文件
import scrapy
class ChoutiItem(scrapy.Item):
content = scrapy.Field()
href = scrapy.Field()
# pipelines.py 數(shù)據(jù)持久化文件
class ChoutiPipeline(object):
def open_spider(self, spider):
# 一開始就打開文件
self.f = open('a.txt', 'w', encoding='utf-8')
def process_item(self, item, spider):
# print(item)
# 寫入文件的操作
self.f.write(item['content'])
self.f.write(item['href'])
self.f.write('\n')
return item
def close_spider(self, spider):
# 寫入完畢,最后關(guān)閉文件
self.f.close()
# setting.py
ITEM_PIPELINES = {
# 數(shù)字表示優(yōu)先級(jí),越小優(yōu)先級(jí)越高
'chouti.pipelines.ChoutiPipeline': 300,
'chouti.pipelines.ChoutiRedisPipeline': 301,
}保存到redis
# settings.ps
ITEM_PIPELINES = {
# 數(shù)字表示優(yōu)先級(jí),越小優(yōu)先級(jí)越高
'chouti.pipelines.ChoutiPipeline': 300,
'chouti.pipelines.ChoutiRedisPipeline': 301,
}
# pipelines.py
# 保存到redis
from redis import Redis
class ChoutiRedisPipeline(object):
def open_spider(self, spider):
# 不寫參數(shù)就用默認(rèn)配置
self.conn = Redis(password='123') # 一開始就拿到redis對(duì)象
def process_item(self, item, spider):
print(item)
import json
s = json.dumps({'content': item['content'], 'href': item['href']})
self.conn.hset('choudi_article', item['id'], s)
return item
def close_spider(self, spoder):
pass
# self.conn.close()
# chouti.py
import scrapy
from chouti.items import ChoutiItem # 導(dǎo)入模型類
class ChoutiaaSpider(scrapy.Spider):
name = 'choutiaa'
# allowed_domains = ['https://dig.chouti.com/'] # 允許爬取的域
start_urls = ['https://dig.chouti.com//'] # 起始爬取位置
# 解析,請(qǐng)求回來,自動(dòng)執(zhí)行parse,在這個(gè)方法中解析
def parse(self, response):
print('----------------',response)
from bs4 import BeautifulSoup
soup = BeautifulSoup(response.text,'lxml')
div_list = soup.select('.link-con .link-item')
for div in div_list:
content = div.select('.link-title')[0].text
href = div.select('.link-title')[0].attrs['href']
id = div.attrs['data-id']
item = ChoutiItem() # 生成模型對(duì)象
item['content'] = content # 添加值
item['href'] = href
item['id'] = id
yield item # 必須用yield
保存到MongoDB
#一.下載并安裝mongodb pip install pymongo
#二、在settings中打開PIPELINES并把數(shù)據(jù)庫(kù)相應(yīng)配置寫入
ITEM_PIPELINES = {
'<spider_name>.pipelines.ChoutiPipeline': 300,
}
MONGODB_HOST = '127.0.0.1'
# 端口號(hào),默認(rèn)27017
MONGODB_PORT = 27017
# 設(shè)置數(shù)據(jù)庫(kù)名稱
MONGODB_DBNAME = 'Chouti'
# 存放本數(shù)據(jù)的表名稱
MONGODB_DOCNAME = 'Chouti'
#三.修改pipelines文件
import pymongo
from scrapy.utils.project import get_project_settings
settings = get_project_settings()
class DouluodaluPipeline(object):
def __init__(self):
# 獲取setting主機(jī)名、端口號(hào)和數(shù)據(jù)庫(kù)名稱
host = settings['MONGODB_HOST']
port = settings['MONGODB_PORT']
dbname = settings['MONGODB_DBNAME']
# 創(chuàng)建數(shù)據(jù)庫(kù)連接
client = pymongo.MongoClient(host=host,port=port)
# 指向指定數(shù)據(jù)庫(kù)
mdb = client[dbname]
# 獲取數(shù)據(jù)庫(kù)里面存放數(shù)據(jù)的表名
self.post = mdb[settings['MONGODB_DOCNAME']]
def process_item(self, item, spider):
data = dict(item)
# 向指定的表里添加數(shù)據(jù)
self.post.insert(data)
return item
保存到mysql
import pymysql.cursors
class MySQLPipeline(object):
def __init__(self):
# 連接數(shù)據(jù)庫(kù)
self.connect = pymysql.connect(
host='127.0.0.1', # 數(shù)據(jù)庫(kù)地址
port=3306, # 數(shù)據(jù)庫(kù)端口
db='scrapyMysql', # 數(shù)據(jù)庫(kù)名
user='root', # 數(shù)據(jù)庫(kù)用戶名
passwd='root', # 數(shù)據(jù)庫(kù)密碼
charset='utf8', # 編碼方式
use_unicode=True)
# 通過cursor執(zhí)行增刪查改
self.cursor = self.connect.cursor()
def process_item(self, item, spider):
self.cursor.execute(
"""insert into mingyan(tag, cont)
value (%s, %s)""", # 純屬python操作mysql知識(shí),不熟悉請(qǐng)惡補(bǔ)
(item['tag'], # item里面定義的字段和表字段對(duì)應(yīng)
item['cont'],))
# 提交sql語(yǔ)句
self.connect.commit()
return item # 必須實(shí)現(xiàn)返回六、動(dòng)作鏈,控制滑動(dòng)的驗(yàn)證碼
from selenium import webdriver
from selenium.webdriver import ActionChains
import time
bro=webdriver.Chrome(executable_path='./chromedriver')
bro.get('https://www.runoob.com/try/try.php?filename=jqueryui-api-droppable')
bro.implicitly_wait(10)
#切換frame(很少)
bro.switch_to.frame('iframeResult')
div=bro.find_element_by_xpath('//*[@id="draggable"]')
# 1 生成一個(gè)動(dòng)作練對(duì)象
action=ActionChains(bro)
# 2 點(diǎn)擊并夯住某個(gè)控件
action.click_and_hold(div)
# 3 移動(dòng)(三種方式)
# action.move_by_offset() # 通過坐標(biāo)(x,y)
# action.move_to_element() # 到另一個(gè)標(biāo)簽
# action.move_to_element_with_offset() # 到另一個(gè)標(biāo)簽,再偏移一部分
for i in range(5):
action.move_by_offset(10,10)
# 4 真正的移動(dòng)
action.perform()
# 5 釋放控件(松開鼠標(biāo))
action.release()
async def login():
for res in setting.user:
try:
username = res[0]
password = res[1]
# headless參數(shù)設(shè)為False,則變成有頭模式
browser = await launch(
{'headless': False}
)
# 打開一個(gè)頁(yè)面
page = await browser.newPage()
await page.setViewport(viewport={'width': 1280, 'height': 800})
res = await page.goto('https://login.taobao.com/', options={'timeout': 10000})
await page.type('#fm-login-id', username)
await page.type('#fm-login-password', password)
await page.waitFor(1000) # 等待時(shí)間
slider = await page.querySelector('#nc_1_n1z') # 是否有滑塊
if slider:
try:
print('有滑塊')
await page.hover('#nc_1_n1z') # 不同場(chǎng)景的驗(yàn)證碼模塊能名字不同。
await page.mouse.down()
await page.mouse.move(2000, 0, {'delay': random.randint(1000, 2000)})
await page.mouse.up()
except Exception as e:
print(e)
input('驗(yàn)證失敗,人工登錄:')
else:
print('沒有滑塊')
await page.click("#login-form > div.fm-btn > button") # 點(diǎn)擊登錄
input('進(jìn)入登錄成功頁(yè)面后,按回車:')
return page
except Exception as e:
continue七、提高爬取效率
- 在配置文件中進(jìn)行相關(guān)的配置即可:(默認(rèn)還有一套setting) #1 增加并發(fā): 默認(rèn)scrapy開啟的并發(fā)線程為32個(gè),可以適當(dāng)進(jìn)行增加。在settings配置文件中修改CONCURRENT_REQUESTS = 100值為100,并發(fā)設(shè)置成了為100。 #2 提高日志級(jí)別: 在運(yùn)行scrapy時(shí),會(huì)有大量日志信息的輸出,為了減少CPU的使用率。可以設(shè)置log輸出信息為INFO或者ERROR即可。在配置文件中編寫:LOG_LEVEL = ‘INFO' # 3 禁止cookie: 如果不是真的需要cookie,則在scrapy爬取數(shù)據(jù)時(shí)可以禁止cookie從而減少CPU的使用率,提升爬取效率。在配置文件中編寫:COOKIES_ENABLED = False # 4禁止重試: 對(duì)失敗的HTTP進(jìn)行重新請(qǐng)求(重試)會(huì)減慢爬取速度,因此可以禁止重試。在配置文件中編寫:RETRY_ENABLED = False # 5 減少下載超時(shí): 如果對(duì)一個(gè)非常慢的鏈接進(jìn)行爬取,減少下載超時(shí)可以能讓卡住的鏈接快速被放棄,從而提升效率。在配置文件中進(jìn)行編寫:DOWNLOAD_TIMEOUT = 10 超時(shí)時(shí)間為10s
八、fake-useragent池
# pip3 install fake-useragent from fake_useragent import UserAgent ua = UserAgent(verify_ssl=False) print(ua.random) # 隨機(jī)獲取一個(gè)UserAgent
九、中間件配置
#大中間件:下載中間件,爬蟲中間件
# 1 寫在middlewares.py中(名字隨便命名)
# 2 配置生效()
# 爬蟲中間件
SPIDER_MIDDLEWARES = {
'cnblogs_crawl.middlewares.CnblogsCrawlSpiderMiddleware': 543,
}
# 下載中間件
DOWNLOADER_MIDDLEWARES = {
'cnblogs_crawl.middlewares.CnblogsCrawlDownloaderMiddleware': 543,
}
# 下載中間件
# 在cnblogs_crawl.middlewares.CnblogsCrawlDownloaderMiddleware中有五個(gè)方法
# 請(qǐng)求出去的時(shí)候
def process_request(self, request, spider)
# Must either:
# - return None: # 返回none繼續(xù)處理,進(jìn)入下一個(gè)中間件
# - return Response: 當(dāng)次請(qǐng)求結(jié)束,把Response丟給引擎處理(可以自己爬,包裝成Response)
# - return Request : 相當(dāng)于把Request重新給了引擎,引擎再去做調(diào)度
# - 拋異常:執(zhí)行process_exception
# 請(qǐng)求回來的時(shí)候
def process_response(self, request, response, spider)
# - return a Response object :繼續(xù)處理當(dāng)次Response,繼續(xù)走后續(xù)的中間件
# - return a Request object:重新給引擎做調(diào)度
# - 拋異常:執(zhí)行process_exception
# 請(qǐng)求異常的時(shí)候
def process_exception(self, request, exception, spider)
# - return None: 不處理異常,繼續(xù)丟給下面
# - return a Response:停止異常處理,不丟給下面。給引擎。Response給爬蟲分析數(shù)據(jù)
# - return a Request:停止異常處理,不丟給下面。給引擎。Request重新調(diào)度
process_exception 錯(cuò)誤處理
class CnblogsSpider(scrapy.Spider):
name = 'cnblogs4'
allowed_domains = ['www.cnblogs.com']
start_urls = ['http://wwwsadasd.cnblogs.com/'] # 錯(cuò)誤的網(wǎng)址,報(bào)錯(cuò)走異常處理
# 走異常處理,重新返回一個(gè)正確的Request對(duì)象
def process_exception(self, request, exception, spider):
print(request.url) # http://wwwsadasd.cnblogs.com/
from scrapy.http import Request
return Request('http://www.cnblogs.com/',callback=spider.parser_detail)
process_request 加代理,加cookie等
def process_request(self, request, spider):
# 1 加cookie(request.cookies就是訪問該網(wǎng)站的cookie)
print(request.cookies)
request.cookies={'name':"jeff",'age':18} # 從你的cookie池中取出來的, 字典
print(request.cookies)
# 2 加代理
request.meta['proxy']=self.get_proxy() # 從代理池中獲取一個(gè)
print(request.meta['proxy'])
# 3 修改ua
from fake_useragent import UserAgent # ua模塊,隨機(jī)獲取一個(gè)
ua = UserAgent(verify_ssl=False)
request.headers['User-Agent']=ua.random
print(request.headers)
# 代理池
def get_proxy(self):
import requests
ret=requests.get('http://0.0.0.0:5010/get').json()['proxy']
print(ret)
return ret
return None
十、集成selenium
#可在兩個(gè)地方集成。
#1.process_request(請(qǐng)求出去的時(shí)候) # 推薦寫這里,少請(qǐng)求一次。直接集成封裝
#2.process_response(請(qǐng)求回來的時(shí)候) # 不推薦,因?yàn)閵Z走了一次請(qǐng)求,回來再集成封裝
# 方案一:缺點(diǎn)很大。每次一請(qǐng)求都要打開一個(gè)bro瀏覽器
def process_request(self, request, spider):
from selenium import webdriver
from scrapy.http import HtmlResponse
bro = webdriver.Chrome(executable_path='../chromedriver')
bro.get(request.url)
text = bro.page_source
response = HtmlResponse(url=request.url, body=text.encode('utf-8'), status=200)
return response
# 方案二:改進(jìn)為一開始就打開一個(gè)bro瀏覽器,后面都用這一個(gè)bro
class CnblogsSpider(scrapy.Spider):
name = 'cnblogs'
from selenium import webdriver
# 在爬蟲一開始就打開bro對(duì)象
bro = webdriver.Chrome(executable_path='../chromedriver')
# 在爬蟲中新添加的方法:關(guān)閉bro
def close(spider, reason):
spider.bro.close() # 爬蟲結(jié)束關(guān)閉
# 中間件中
def process_request(self, request, spider):
from scrapy.http import HtmlResponse
spider.bro.get(request.url) # 每個(gè)請(qǐng)求使用一個(gè)bro
text = spider.bro.page_source
response = HtmlResponse(url=request.url, body=text.encode('utf-8'), status=200)
return response
十一、指紋和布隆過濾器實(shí)現(xiàn)增量爬取
什么是增量爬???
-增量爬取(100鏈接,150個(gè)鏈接)
- -已經(jīng)爬過的,放到某個(gè)位置(mysql,redis中:集合)
- -如果用默認(rèn)的,爬過的地址,放在內(nèi)存中,只要項(xiàng)目一重啟,就沒了,它也不知道我爬過那個(gè)了,所以要自己重寫去重方案
-你寫的去重方案,占得內(nèi)存空間更小
-bitmap方案
-BloomFilter布隆過濾器
網(wǎng)址指紋
# 一、網(wǎng)址指紋 from scrapy.http import Request from scrapy.utils.request import request_fingerprint # 這種網(wǎng)址是一個(gè) request1 = Request(url='https://www.baidu.com/s?name=jeff&age=18') request2 = Request(url='https://www.baidu.com/s?age=18&name=jeff') ret1=request_fingerprint(requests1) ret2=request_fingerprint(requests2) print(ret1) # 6961985868392ae44c15ada494ddeda856cf75fc print(ret2) # 6961985868392ae44c15ada494ddeda856cf75fc
布隆過濾器
# 安裝 # 1.需要先安裝bitarray #下載地址:https://www.lfd.uci.edu/~gohlke/pythonlibs/ # 2.下載好之后 pip3 install 文件拖進(jìn)去 # 3.pip3 install pybloom_live #ScalableBloomFilter 可以自動(dòng)擴(kuò)容 from pybloom_live import ScalableBloomFilter bloom = ScalableBloomFilter(initial_capacity=100, error_rate=0.001, mode=ScalableBloomFilter.LARGE_SET_GROWTH) url = "https://www.baidu.com/s?name=jeff&age=18" url2 = "https://www.baidu.com/s?age=18&name=jeff" bloom.add(url) print(url in bloom) print(url2 in bloom)
使用一:添加網(wǎng)址(不推薦)
#BloomFilter 是定長(zhǎng)的
from pybloom_live import BloomFilter
bf = BloomFilter(capacity=1000)
url='www.baidu.com'
bf.add(url)
print(url in bf)
print("www.liuqingzheng.top" in bf)
使用二:添加網(wǎng)址指紋(推薦),配合指紋使用
from scrapy.http import Request
from scrapy.utils.request import request_fingerprint
from pybloom_live import BloomFilter
request1 = Request(url='https://www.baidu.com/s?name=jeff&age=18')
request2 = Request(url='https://www.baidu.com/s?age=18&name=jeff')
ret1=request_fingerprint(request1)
ret2=request_fingerprint(request2)
print(ret1) # 6961985868392ae44c15ada494ddeda856cf75fc
print(ret2) # 6961985868392ae44c15ada494ddeda856cf75fc
bf = BloomFilter(capacity=1000) # 1000容量
bf.add(ret2)
if ret1 in bf:
print('已經(jīng)爬過此網(wǎng)站,True')
else:
bf.add(ret1) # 添加
print('還沒有爬過此網(wǎng)站,返回false')
十二、分布式爬蟲
github地址:https://github.com/rmax/scrapy-redis
# 1 安裝pip3 install scrapy-redis
# 源碼部分,不到1000行,
# 1 原來的爬蟲繼承
from scrapy_redis.spiders import RedisSpider
class CnblogsSpider(RedisSpider):
#start_urls = ['http://www.cnblogs.com/']
redis_key = 'myspider:start_urls' # 起始地址為空,在redis中拿
# 2 在setting中配置
SCHEDULER = "scrapy_redis.scheduler.Scheduler"
DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter"
ITEM_PIPELINES = {
'chouti.pipelines.Pipeline': 300, # 用自己的入庫(kù)類,比如mysql中
# 'scrapy_redis.pipelines.RedisPipeline': 300 # 存在別人寫好的redis入庫(kù)類
}
REDIS_PARAMS = {'password':'123'} # 如果redis有密碼就配置
#其他更多配置見github
# 3 多臺(tái)機(jī)器上啟動(dòng)scrapy
# 4 向reids中發(fā)送起始url
redis-cli lpush myspider:start_urls https://www.cnblogs.com
十三、爬蟲框架全站爬取使用案例
可以同時(shí)啟動(dòng)兩個(gè)爬蟲,爬不同的網(wǎng)站。但是建議爬不同的網(wǎng)站新建項(xiàng)目
chouti.py 爬蟲:
import scrapy
from chouti.items import ChoutiItem # 導(dǎo)入模型類
class ChoutiaaSpider(scrapy.Spider):
name = 'choutiaa'
# allowed_domains = ['https://dig.chouti.com/'] # 允許爬取的域
start_urls = ['https://dig.chouti.com//'] # 起始爬取位置
# 解析,請(qǐng)求回來,自動(dòng)執(zhí)行parse,在這個(gè)方法中解析
def parse(self, response):
print('----------------',response)
from bs4 import BeautifulSoup
soup = BeautifulSoup(response.text,'lxml')
div_list = soup.select('.link-con .link-item')
for div in div_list:
content = div.select('.link-title')[0].text
href = div.select('.link-title')[0].attrs['href']
id = div.attrs['data-id']
item = ChoutiItem() # 生成模型對(duì)象
item['content'] = content # 添加值
item['href'] = href
item['id'] = id
yield item # 必須用yield
cnblogs.py 爬蟲:
# -*- coding: utf-8 -*-
import scrapy
from bs4 import BeautifulSoup
from chouti.items import CnblogsItem # 導(dǎo)入模型類
from scrapy.http import Request
class CnblogsSpider(scrapy.Spider):
name = 'cnblogs'
start_urls = ['https://www.cnblogs.com/']
def parse(self, response):
print('------', response)
soup = BeautifulSoup(response.text, 'lxml')
div_list = soup.select('#post_list .post_item')
for div in div_list:
author = div.select('.post_item_foot a')[0].text
content_url = div.select('h3 a')[0].attrs['href']
title = div.select('h3')[0].text
content_summary = div.select('p')[0].text
item = CnblogsItem()
item['author'] = author
item['content_url'] = content_url
item['title'] = title
item['content_summary'] = content_summary
# print(f'''
# 作者:{author}
# 文章地址:{content_url}
# 標(biāo)題:{title}
# 文章內(nèi)容:{content_summary}
# ''')
# 繼續(xù)往深一層爬取,傳遞給content_parse
yield Request(content_url, callback=self.content_parse, meta={'item': item})
# 獲取下一頁(yè)的標(biāo)簽網(wǎng)址
next = soup.select('#paging_block > div > a:nth-last-child(1)')[0].attrs['href']
next = 'https://www.cnblogs.com/'+next
yield Request(next) # 繼續(xù)爬取下一頁(yè)
def content_parse(self, response):
item = response.meta.get('item')
content = response.css('#cnblogs_post_body').extract_first()
if not content:
content = response.css('content').extract_first()
item['content'] = content
# print(item)
yield item
items.py 模型類:
# -*- coding: utf-8 -*-
# Define here the models for your scraped items
# See documentation in:
# https://docs.scrapy.org/en/latest/topics/items.html
import scrapy
class ChoutiItem(scrapy.Item):
content = scrapy.Field()
href = scrapy.Field()
id = scrapy.Field()
class CnblogsItem(scrapy.Item):
author = scrapy.Field()
content_url = scrapy.Field()
title = scrapy.Field()
content_summary = scrapy.Field()
content = scrapy.Field()
pipelines.py 數(shù)據(jù)持久化文件
# -*- coding: utf-8 -*-
# Define your item pipelines here
# Don't forget to add your pipeline to the ITEM_PIPELINES setting
# See: https://docs.scrapy.org/en/latest/topics/item-pipeline.html
# 保存到文件
class Pipeline(object):
def open_spider(self, spider):
# choutiaa爬蟲入庫(kù)前
if spider.name == 'choutiaa':
# 一開始就打開文件
self.f = open('a.txt', 'w', encoding='utf-8')
# cnblog爬蟲入庫(kù)前
elif spider.name == 'cnblogs':
import pymysql
self.conn = pymysql.Connect(host='127.0.0.1', port=3306, db='cnblogs', user='root', password="123",autocommit=True)
def process_item(self, item, spider):
# choutiaa爬蟲入庫(kù)中
if spider.name == 'choutiaa':
# 寫入文件的操作
self.f.write(item['content'])
self.f.write(item['href'])
self.f.write(item['id'])
self.f.write('\n')
return item
# cnblog爬蟲入庫(kù)中
elif spider.name == 'cnblogs':
print('cnblogs入庫(kù)中')
curser = self.conn.cursor()
sql = 'insert into article (author,content_url,title,content_summary,content) values (%s,%s,%s,%s,%s)'
curser.execute(sql, (
item['author'], item['content_url'], item['title'], item['content_summary'], item['content']))
def close_spider(self, spider):
# choutiaa爬蟲入庫(kù)結(jié)束
if spider.name == 'choutiaa':
# 寫入完畢,最后關(guān)閉文件
self.f.close()
# cnblog爬蟲入庫(kù)結(jié)束
elif spider.name == 'cnblogs':
print('cnblogs入庫(kù)完畢')
self.conn.close()
main.py
from scrapy.cmdline import execute # execute(['scrapy','crawl','choutiaa']) execute(['scrapy','crawl','cnblogs'])
以上就是scarpy爬蟲框架集成selenium及詳細(xì)講解的詳細(xì)內(nèi)容,更多關(guān)于scarpy爬蟲框架結(jié)構(gòu)集成selenium的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
python函數(shù)和python匿名函數(shù)lambda詳解
這篇文章主要介紹了python函數(shù)和python匿名函數(shù)lambda,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-09-09
在tensorflow以及keras安裝目錄查詢操作(windows下)
這篇文章主要介紹了在tensorflow以及keras安裝目錄查詢操作(windows下),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2020-06-06
python基于socket函數(shù)實(shí)現(xiàn)端口掃描
這篇文章主要為大家詳細(xì)介紹了python基于socket函數(shù)實(shí)現(xiàn)端口掃描,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-05-05
Python中強(qiáng)大的命令行庫(kù)click入門教程
click是Python的一個(gè)命令行工具,極其好用。不信?一試便知。下面這篇文章主要給大家介紹了Python中強(qiáng)大的命令行庫(kù)click,需要的朋友可以參考學(xué)習(xí),下面來一起看看吧。2016-12-12
詳解python內(nèi)置常用高階函數(shù)(列出了5個(gè)常用的)
這篇文章主要介紹了python內(nèi)置常用高階函數(shù)(列出了5個(gè)常用的),通過實(shí)例代碼給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-02-02
PyCharm+Pipenv虛擬環(huán)境開發(fā)和依賴管理的教程詳解
這篇文章主要介紹了PyCharm+Pipenv虛擬環(huán)境作開發(fā)和依賴管理的教程,本文通過圖文并茂的形式給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-04-04
Python?matplotlib數(shù)據(jù)可視化圖繪制
這篇文章主要介紹了Python?matplotlib數(shù)據(jù)可視化圖繪制,文章圍繞主題展開詳細(xì)的內(nèi)容介紹,具有一定的參考價(jià)值,需要的朋友可以參考一下2022-07-07

