18.增量式爬蟲
增量式爬蟲
引言:
? 當我們在瀏覽相關網(wǎng)頁的時候會發(fā)現(xiàn),某些網(wǎng)站定時會在原有網(wǎng)頁數(shù)據(jù)的基礎上更新一批數(shù)據(jù),例如某電影網(wǎng)站會實時更新一批最近熱門的電影仓犬。小說網(wǎng)站會根據(jù)作者創(chuàng)作的進度實時更新最新的章節(jié)數(shù)據(jù)等等芒粹。那么,類似的情景着降,當我們在爬蟲的過程中遇到時,我們是不是需要定時更新程序以便能爬取到網(wǎng)站中最近更新的數(shù)據(jù)呢拗军?
一.增量式爬蟲
概念:通過爬蟲程序監(jiān)測某網(wǎng)站數(shù)據(jù)更新的情況任洞,以便可以爬取到該網(wǎng)站更新出的新數(shù)據(jù)。
如何進行增量式的爬取工作:
在發(fā)送請求之前判斷這個URL是不是之前爬取過
在解析內(nèi)容后判斷這部分內(nèi)容是不是之前爬取過
寫入存儲介質(zhì)時判斷內(nèi)容是不是已經(jīng)在介質(zhì)中存在
分析:
? 不難發(fā)現(xiàn)发侵,其實增量爬取的核心是去重交掏, 至于去重的操作在哪個步驟起作用,只能說各有利弊刃鳄。在我看來盅弛,前兩種思路需要根據(jù)實際情況取一個(也可能都用)。第一種思路適合不斷有新頁面出現(xiàn)的網(wǎng)站,比如說小說的新章節(jié)熊尉,每天的最新新聞等等罐柳;第二種思路則適合頁面內(nèi)容會更新的網(wǎng)站。第三個思路是相當于是最后的一道防線狰住。這樣做可以最大程度上達到去重的目的张吉。
去重方法
將爬取過程中產(chǎn)生的url進行存儲,存儲在redis的set中催植。當下次進行數(shù)據(jù)爬取時肮蛹,首先對即將要發(fā)起的請求對應的url在存儲的url的set中做判斷,如果存在則不進行請求创南,否則才進行請求伦忠。
對爬取到的網(wǎng)頁內(nèi)容進行唯一標識的制定,然后將該唯一表示存儲至redis的set中稿辙。當下次爬取到網(wǎng)頁數(shù)據(jù)的時候昆码,在進行持久化存儲之前,首先可以先判斷該數(shù)據(jù)的唯一標識在redis的set中是否存在邻储,在決定是否進行持久化存儲赋咽。
二.項目案例
- 需求:爬取4567tv網(wǎng)站中所有的電影詳情數(shù)據(jù)。
爬蟲文件:
# -- coding: utf-8 --
import scrapy
from scrapy.linkextractors import LinkExtractor
from scrapy.spiders import CrawlSpider, Rule
from redis import Redis
from incrementPro.items import IncrementproItem
class MovieSpider(CrawlSpider):
? name = 'movie'
? # allowed_domains = ['www.xxx.com']
? start_urls = ['http://www.4567tv.tv/frim/index7-11.html']
? rules = (
? Rule(LinkExtractor(allow=r'/frim/index7-\d+.html'), callback='parse_item', follow=True),
? )
? #創(chuàng)建redis鏈接對象
? conn = Redis(host='127.0.0.1',port=6379)
? def parse_item(self, response):
? li_list = response.xpath('//li[@class="p1 m1"]')
? for li in li_list:
? #獲取詳情頁的url
? detail_url = 'http://www.4567tv.tv'+li.xpath('./a/@href').extract_first()
? #將詳情頁的url存入redis的set中
? ex = self.conn.sadd('urls',detail_url)
? if ex == 1:
? print('該url沒有被爬取過吨娜,可以進行數(shù)據(jù)的爬取')
? yield scrapy.Request(url=detail_url,callback=self.parst_detail)
? else:
? print('數(shù)據(jù)還沒有更新脓匿,暫無新數(shù)據(jù)可爬取宦赠!')
? #解析詳情頁中的電影名稱和類型陪毡,進行持久化存儲
? def parst_detail(self,response):
? item = IncrementproItem()
? item['name'] = response.xpath('//dt[@class="name"]/text()').extract_first()
? item['kind'] = response.xpath('//div[@class="ct-c"]/dl/dt[4]//text()').extract()
? item['kind'] = ''.join(item['kind'])
? yield item
管道文件:
# -- coding: utf-8 --
# Define your item pipelines here
#
# Don't forget to add your pipeline to the ITEM_PIPELINES setting
# See: https://doc.scrapy.org/en/latest/topics/item-pipeline.html
from redis import Redis
class IncrementproPipeline(object):
? conn = None
? def open_spider(self,spider):
? self.conn = Redis(host='127.0.0.1',port=6379)
? def process_item(self, item, spider):
? dic = {
? 'name':item['name'],
? 'kind':item['kind']
? }
? print(dic)
? self.conn.lpush('movieData',dic)
? return item
- 需求:爬取糗事百科中的段子和作者數(shù)據(jù)。
爬蟲文件:
# -- coding: utf-8 --
import scrapy
from scrapy.linkextractors import LinkExtractor
from scrapy.spiders import CrawlSpider, Rule
from incrementByDataPro.items import IncrementbydataproItem
from redis import Redis
import hashlib
class QiubaiSpider(CrawlSpider):
? name = 'qiubai'
? # allowed_domains = ['www.xxx.com']
? start_urls = ['https://www.qiushibaike.com/text/']
? rules = (
? Rule(LinkExtractor(allow=r'/text/page/\d+/'), callback='parse_item', follow=True),
? Rule(LinkExtractor(allow=r'/text/$'), callback='parse_item', follow=True),
? )
? #創(chuàng)建redis鏈接對象
? conn = Redis(host='127.0.0.1',port=6379)
? def parse_item(self, response):
? div_list = response.xpath('//div[@id="content-left"]/div')
? for div in div_list:
? item = IncrementbydataproItem()
? item['author'] = div.xpath('./div[1]/a[2]/h2/text() | ./div[1]/span[2]/h2/text()').extract_first()
? item['content'] = div.xpath('.//div[@class="content"]/span/text()').extract_first()
? #將解析到的數(shù)據(jù)值生成一個唯一的標識進行redis存儲
? source = item['author']+item['content']
? source_id = hashlib.sha256(source.encode()).hexdigest()
? #將解析內(nèi)容的唯一表示存儲到redis的data_id中
? ex = self.conn.sadd('data_id',source_id)
? if ex == 1:
? print('該條數(shù)據(jù)沒有爬取過勾扭,可以爬取......')
? yield item
? else:
? print('該條數(shù)據(jù)已經(jīng)爬取過了毡琉,不需要再次爬取了!!!')
管道文件:
# -- coding: utf-8 --
# Define your item pipelines here
#
# Don't forget to add your pipeline to the ITEM_PIPELINES setting
# See: https://doc.scrapy.org/en/latest/topics/item-pipeline.html
from redis import Redis
class IncrementbydataproPipeline(object):
? conn = None
? def open_spider(self, spider):
? self.conn = Redis(host='127.0.0.1', port=6379)
? def process_item(self, item, spider):
? dic = {
? 'author': item['author'],
? 'content': item['content']
? }
? # print(dic)
? self.conn.lpush('qiubaiData', dic)
? return item