1. 程式人生 > 實用技巧 >CrawlSpider、分散式、增量式

CrawlSpider、分散式、增量式

ImagesPipeline

ImagesPipeline:專門用作於二進位制資料下載和持久化儲存的管道類。建議在爬蟲檔案中進行資料解析,不建議在爬蟲檔案中直接進行資料儲存。

圖片懶載入:應用到標籤的偽屬性,資料捕獲的時候一定是基於偽屬性進行。

#-*-coding:utf-8-*-
importscrapy
fromimgPro.itemsimportImgproItem

classImgSpider(scrapy.Spider):
name='img'
start_urls=['http://sc.chinaz.com/tupian/meinvtupian.html']

defparse(self,response)
:

div_list=response.xpath('//*[@id="container"]/div')
fordivindiv_list:
#偽屬性(反爬機制,也是網站優化的一種方法):不是src,而是src2,但在瀏覽器中隨著滑鼠的拖動會變為src
img_src=div.xpath('./div/a/img/@src2').extract_first()
item=ImgproItem()
item['img_src']=img_src
#將圖片地址提交給ImagesPipeline的管道類
yielditem

----------------------

#-*-coding:utf-8-*-

#匯入ImagesPipeline管道類

fromscrapy.pipelines.imagesimportImagesPipeline
#要匯入scrapy進行傳送請求
importscrapy

#要重寫父類方法
classImgproPipeline(ImagesPipeline):

#是用來對媒體資源進行請求的(資料下載),引數item就是接收到的爬蟲類提交的item物件
defget_media_requests(self,item,info):
#不需要傳入回撥函式
yieldscrapy.Request(item['img_src'])

#指明資料儲存的路徑,只能指定圖片的名稱,圖片的具體路徑需在settings.py中指定
deffile_path
(self,request,response=None,info=None):

returnrequest.url.split('/')[-1]

#將item傳遞個下一個即將被執行的管道類
defitem_completed(self,results,item,info):
returnitem

-------------------
#settings.py
...
#配置圖片儲存資料夾的路徑
IMAGES_STORE='./imgLibs'


-------------------

CrawlSpider

一種基於scrapy進行全站資料爬取的一種新的技術手段。
CrawlSpider就是Spider的一個子類

  • 連結提取器:LinkExtractor,連結提取器可以遞迴提取網站內的所有連結(follow=True:將符合規則的連結作為首頁連結繼續爬取),自動去重且只能提取連結。
  • 規則解析器:Rule,將連結提取器提取到的連結進行請求傳送且根據指定規則對請求到的資料進行資料解析 。

使用流程:

  1. 新建一個工程
  2. cd 工程中
  3. 新建一個爬蟲檔案:scrapy genspider -t crawl spiderName www.xxx.com
#-*-coding:utf-8-*-
importscrapy
fromscrapy.linkextractorsimportLinkExtractor#連線提取器
fromscrapy.spidersimportCrawlSpider,Rule#規則解析器


fromsunCrawlPro.itemsimportSuncrawlproItem,Detail_item#手動匯入

classSunSpider(CrawlSpider):
name='sun'
start_urls=['http://wz.sun0769.com/index.php/question/questionType?type=4&page=']

#例項化了一個連結提取器物件,作用:根據指定規則(allow=r’正則表示式‘)進行指定連結的提取。
link=LinkExtractor(allow=r'type=4&page=\d+')#獲取頁碼連連結

#獲取新聞詳情頁的連線
link_detail=LinkExtractor(allow=r"question/\d+/\d+\.shtml")

rules=(
#將link作用到了Rule構造方法的引數1中,作用:將連結提取器提取到的連結進行請求傳送且根據指定規則對請求到的資料進行資料解析
Rule(link,callback='parse_item',follow=False),#follow=False,只取首頁符合要求的連結併發送請求。
#follow=True:將連結提取器繼續作用到連結提取器提取到的連結所對應的頁面中,從而獲取整個網站中全部符合規則的連結。
Rule(link_detail,callback='parse_detail'),#follow預設為False
)

defparse_item(self,response):
#xpath表示式中不可以出現tbody標籤
tr_list=response.xpath('//*[@id="morelist"]/div/table[2]//tr/td/table//tr')
fortrintr_list:
title=tr.xpath('./td[2]/a[2]/text()').extract_first()
num=tr.xpath('./td[1]/text()').extract_first()
item=SuncrawlproItem()
item['title']=title
item['num']=num

yielditem

defparse_detail(self,response):
content=response.xpath('/html/body/div[9]/table[2]//tr[1]/td/text()').extract_first()
num=response.xpath('/html/body/div[9]/table[1]//tr/td[2]/span[2]/text()').extract_first()
num=num.split(':')[-1]
item=Detail_item()
item['content']=content
item['num']=num
yielditem


-----------------
#-*-coding:utf-8-*-

#Defineyouritempipelineshere

classSuncrawlproPipeline(object):
defprocess_item(self,item,spider):
ifitem.__class__.__name__=='Detail_item':
content=item['content']
num=item['num']
print(item)
else:
title=item['title']
num=item['num']
print(item)
returnitem

分散式

概念:需要搭建一個分散式的機群,然後在機群的每一臺電腦中執行同一組程式,讓其對某一個網站的資料進行聯合分佈爬取。

原生的scrapy框架不可以實現分散式:因為排程器不可以被共享;管道不可以被共享。

實現分散式:scrapy+scrapy_redis實現分散式

scrapy-redis元件:

  • 作用:可以提供可被共享的排程器和管道
  • 特性:資料只可以儲存到redis資料庫。

分散式的實現流程

第一步:pip install scrapy-redis

第二步:建立工程.

第三步:cd 工程目錄中.

第四步,建立爬蟲檔案(兩種選擇):

  • 建立基於Spider的爬蟲檔案
  • 建立CrawlSpider的爬蟲檔案

第五步,修改爬蟲類:

  • 導包:from scrapy_redis.spiders import RedisCrawlSpider(from scrapy_redis.spiders import RedisSpider)
  • 修改當前爬蟲類的父類為RedisCrawlSpider(RedisSpider)
  • 將allowed_domains和start_urls刪除
  • 新增一個新屬性:redis_key = 'fbsQueue'(任意字串),表示的是可以被共享的排程器佇列的名稱
  • 編寫爬蟲類的其他操作(常規操作)

第六步,settings配置檔案的配置:

UA偽裝、Robots.

管道的指定:ITEM_PIPELINES = {'scrapy_redis.pipelines.RedisPipeline': 400}

指定排程器:

  • 增加了一個去重容器類的配置, 作用使用Redis的set集合來儲存請求的指紋資料, 從而實現請求去重的持久化DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter"
  • 使用scrapy-redis元件自己的排程器SCHEDULER = "scrapy_redis.scheduler.Scheduler"
  • 配置排程器是否要持久化, 也就是當爬蟲結束了, 要不要清空Redis中請求佇列和去重指紋的set。如果是True, 就表示要持久化儲存, 就不清空資料, 否則清空資料SCHEDULER_PERSIST = True

指定redis資料庫:

REDIS_HOST = 'redis服務的ip地址'

REDIS_PORT = 6379

第七步,redis的配置檔案(redis.windows.conf)進行配置:

  • 關閉預設繫結:# bind 127.0.0.1
  • 關閉保護模式:protected-mode no

第八步,啟動redis的服務端和客戶端:

  • redis-server.exe redis.windows.conf(一定要攜帶配置檔案進行啟動)
  • redis-cli

第九步,啟動程式:scrapy runspider xxx.py

第十步,向排程器的佇列中仍入一個起始的url:

  • 佇列是存在於redis中
  • 開啟redis的客戶端: lpush fbsQueue http://wz.sun0769.com/index.php/question/questionType?type=4&page= [value …]
#-*-coding:utf-8-*-
#scrapy/fbs.py
importscrapy
fromscrapy.linkextractorsimportLinkExtractor
fromscrapy.spidersimportCrawlSpider,Rule

fromscrapy_redis.spidersimportRedisCrawlSpider#手動匯入
fromfbsPro.itemsimportFbsproItem
classFbsSpider(RedisCrawlSpider):
name='fbs'
#allowed_domains=['www.xxx.com']
#start_urls=['http://www.xxx.com/']
redis_key='fbsQueue'#表示的是可以被共享的排程器佇列的名稱
rules=(
Rule(LinkExtractor(allow=r'type=4&page=\d+'),callback='parse_item',follow=True),
)

defparse_item(self,response):
tr_list=response.xpath('//*[@id="morelist"]/div/table[2]//tr/td/table//tr')
fortrintr_list:
title=tr.xpath('./td[2]/a[2]/text()').extract_first()
status=tr.xpath('./td[3]/span/text()').extract_first()

item=FbsproItem()
item['title']=title
item['status']=status

yielditem
#settings.py
BOT_NAME='fbsPro'
USER_AGENT='Mozilla/5.0(WindowsNT10.0;Win64;x64)AppleWebKit/537.36(KHTML,likeGecko)Chrome/76.0.3809.132Safari/537.36'
SPIDER_MODULES=['fbsPro.spiders']
NEWSPIDER_MODULE='fbsPro.spiders'

#Obeyrobots.txtrules
ROBOTSTXT_OBEY=False

#ConfiguremaximumconcurrentrequestsperformedbyScrapy(default:16)
CONCURRENT_REQUESTS=3


ITEM_PIPELINES={
'scrapy_redis.pipelines.RedisPipeline':400
}

#增加了一個去重容器類的配置,作用使用Redis的set集合來儲存請求的指紋資料,從而實現請求去重的持久化
DUPEFILTER_CLASS="scrapy_redis.dupefilter.RFPDupeFilter"
#使用scrapy-redis元件自己的排程器
SCHEDULER="scrapy_redis.scheduler.Scheduler"
#配置排程器是否要持久化,也就是當爬蟲結束了,要不要清空Redis中請求佇列和去重指紋的set。
#如果是True,就表示要持久化儲存,就不清空資料,否則清空資料,可實現增量式。
SCHEDULER_PERSIST=True

REDIS_HOST='192.168.18.36'
REDIS_PORT=6379
#items.py
importscrapy
classFbsproItem(scrapy.Item):
#definethefieldsforyouritemherelike:
title=scrapy.Field()
status=scrapy.Field()

增量式

概念:用於監測網站資料更新的情況。

核心機制:去重。可以使用redis的set實現去重。

#settings.py
#-*-coding:utf-8-*-

BOT_NAME='zjsPro'
USER_AGENT='Mozilla/5.0(WindowsNT10.0;Win64;x64)AppleWebKit/537.36(KHTML,likeGecko)Chrome/76.0.3809.132Safari/537.36'
SPIDER_MODULES=['zjsPro.spiders']
NEWSPIDER_MODULE='zjsPro.spiders'

#Obeyrobots.txtrules
ROBOTSTXT_OBEY=False
LOG_LEVEL='ERROR'

#Configureitempipelines
#Seehttps://docs.scrapy.org/en/latest/topics/item-pipeline.html
ITEM_PIPELINES={
'zjsPro.pipelines.ZjsproPipeline':300,
}
#scrapy/zjs.py
#-*-coding:utf-8-*-
importscrapy
fromscrapy.linkextractorsimportLinkExtractor
fromscrapy.spidersimportCrawlSpider,Rule


fromzjsPro.itemsimportZjsproItem
fromredisimportRedis

classZjsSpider(CrawlSpider):
conn=Redis(host='127.0.0.1',port=6379)
name='zjs'
#allowed_domains=['www.xxx.com']
start_urls=['https://www.4567tv.tv/index.php/vod/show/class/%E7%88%B1%E6%83%85/id/1.html']

rules=(
Rule(LinkExtractor(allow=r'/page/\d+\.html'),callback='parse_item',follow=False),
)

defparse_item(self,response):
li_list=response.xpath('/html/body/div[1]/div/div/div/div[2]/ul/li')
forliinli_list:
name=li.xpath('./div/a/@title').extract_first()
detail_url='https://www.4567tv.tv'+li.xpath('./div/a/@href').extract_first()
item=ZjsproItem()
item['name']=name
#可以將爬過的電影的詳情頁的url記錄起來
#ex==0:資料插入失敗ex==1:資料插入成功
ex=self.conn.sadd('movie_detail_urls',detail_url)
ifex:
print('捕獲到最新更新出來的資料.')
yieldscrapy.Request(detail_url,callback=self.parse_detail,meta={'item':item})
else:
print('暫無資料的更新.')

defparse_detail(self,response):
desc=response.xpath('/html/body/div[1]/div/div/div/div[2]/p[5]/span[2]/text()').extract_first()
item=response.meta['item']
item['desc']=desc

yielditem
#items.py
#-*-coding:utf-8-*-

importscrapy


classZjsproItem(scrapy.Item):
#definethefieldsforyouritemherelike:
name=scrapy.Field()
desc=scrapy.Field()
#pipelines.py
#-*-coding:utf-8-*-

classZjsproPipeline(object):
defprocess_item(self,item,spider):
conn=spider.conn
conn.lpush('moiveData',item)
returnitem

常見反爬機制:

  • robots
  • UA偽裝
  • 驗證碼
  • 代理
  • cookie
  • 動態變化的請求引數
  • js加密
  • js混淆
  • 圖片懶載入
  • 動態資料的捕獲
  • seleium:規避檢測