增量式爬虫
增量式爬虫¶
需求: 定时 更新程序 以便爬取网站中最近更新的数据¶
一、增量式爬虫¶
- 概念: 通过爬虫程序检测某网站数据更新的情况,以便可以爬取到该网站更新出的新数据
- 如何进行增量式的爬取工作:
- 在发送请求之前判断 此 URL 是不是之前爬过
- 在解析内容之后判断 这部分内容 之前是否爬过
- 在写入 存储介质时 判断 内容是不是已经在介质中存在
- 分析: 不难发现,其实增量式爬取的核心是 去重 ,至于说去重的操作在哪个步骤起作用,只能说各有利弊,在我看来, 前两种思路需要根据实际情况取一个(也可能都用)。
- 第一种思路适合有不断新页面的出现,比如说小说的新章节,每天的最新新闻等等;
- 第二种思路则适合页面内容会不断更新的网站。
- 第三种思路是相当于最后一道防线。这样做可以最大程度上达到去重的目的。
去重方法:¶
- 思路: 通过已有的机制达到去重的目的 1、redis的 set集合。 2、python 的 set 集合
- 我们选择 redis 的 set 集合, 以求最大效率。
- 将爬取过程中产生的 URL 进行存储,存储在 redis 的 set 中,
- 根据 Redis.sadd(key,value) 的返回结果判断此 URL 是否已爬取过,
- 1 表示存在,则发起请求; 0 表示不存在,则不进行请求。
- 对爬取到的 网页内容进行唯一标识的制定, 然后将该唯一标识存储在 redis 的 set 中,当下次爬取到网页数据内容的时候,在进行持久化存储之前, 首先判断一下该 数据的 唯一标识在 redis 的 set 中是否存在,再决定 是否执行入库操作。
案例 一、¶
需求:爬取4567tv网站中所有的 电影名 和 作者名¶
In [ ]:
- 说明 1 、: 项目配置文件 settings.py :
- 默认配置:
# 添加 UA
USER_AGENT = 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.119 Safari/537.36'
# 是否遵循爬虫协议 , 不遵从
ROBOTSTXT_OBEY = False
# 开启 scrapy 默认管道
ITEM_PIPELINES = {
'increment1_Pro.pipelines.Increment1ProPipeline': 300,
}
- 说明 2、: items.py 文件 自己根据代码编写
爬虫文件¶
In [ ]:
# -*- coding: utf-8 -*-
import scrapy
from scrapy.linkextractors import LinkExtractor
from scrapy.spiders import CrawlSpider, Rule
from redis import Redis
from increment1_Pro.items import Increment1ProItem
class MovieSpider(CrawlSpider):
name = 'movie'
# allowed_domains = ['www.xxx.com']
start_urls = ['https://www.4567tv.tv/index.php/vod/show/class/%E6%81%90%E6%80%96/id/9.html']
rules = (
Rule(LinkExtractor(allow=r'/index.php/vod/show/class/%E6%81%90%E6%80%96/id/9/page/\d+\.html'), callback='parse_item', follow=True),
)
def parse_item(self, response):
conn = Redis(host='127.0.0.1',port=6379)
detail_url_list ='https://www.4567tv.tv' + response.xpath('//li[@class="col-md-6 col-sm-4 col-xs-3"]/div/a/@href')
for url in detail_url_list:
# ex 若 为 1 ,表示 为新数据
ex = conn.sadd('movies_url',url)
if ex == 1:
yield scrapy.Request(url=url,callback=self.self.parse_detail)
else:
print("此站<%s>无更新数据,暂无新数据可爬"%url)
def parse_detail(self,response):
item = Increment1ProItem()
item['name'] = response.xpath('//div[@class="stui-content__detail"]/h1/text()').extract_first()
item['actor'] = response.xpath('/html/body/div[1]/div/div/div/div[2]/p[3]/a/text()').extract_first()
yield item
管道文件:¶
In [ ]:
# -*- coding: utf-8 -*-
from redis import Redis
class Increment1ProPipeline(object):
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'],
# 'actor': item['actor'],
# }
print('正在入库!')
self.conn.lpush('movie_data',item)
return item
案例 二、¶
需求:爬取糗事百科中的 段子内容 和 作者名。¶
爬虫文件:¶
In [ ]:
# -*- coding: utf-8 -*-
import scrapy
from scrapy.linkextractors import LinkExtractor
from scrapy.spiders import CrawlSpider, Rule
from increment_data.items import IncrementDataItem
from redis import Redis
import hashlib
class QiubaiSpider(CrawlSpider):
name = 'qiubai'
# allowed_domains = ['www.xx.com']
start_urls = ['https://www.qiushibaike.com/text/']
rules = (
Rule(LinkExtractor(allow=r'/text/page/\d+/'), callback='parse_item', follow=True),
)
def parse_item(self, response):
div_list = response.xpath('//div[@class="article block untagged mb15 typs_hot"]')
conn = Redis(host='127.0.0.1',port=6379)
for div in div_list:
item = IncrementDataItem()
item['author'] = div.xpath('./div[1]/a[2]/h2/text() | ./div[1]/span[2]/h2/text()').extract_first()
# item['content'] = response.xpath('./a[1]/div/span/text()').extract_first()
item['content'] = div.xpath('.//div[@class="content"]/span/text()').extract()
item['content'] = ''.join(item['content'])
source = item['author'] + item['content']
# 自定制 一种形式的 数据指纹, 是数据的唯一标识
hashValue = hashlib.sha256(source.encode()).hexdigest()
ex = conn.sadd("qiubai_hash",hashValue)
if ex == 1:
yield item
else:
print("此数据 已 爬")
管道文件:¶
In [ ]:
# -*- coding: utf-8 -*-
from redis import Redis
##################### 注意 ########################
"""
在这里 向 redis 的列表中 lpush 字典时 ,则python 中 redis 的版本需要为 2.10.6,否则报错
pip3 install redis==2.10.6
"""
class IncrementDataPipeline(object):
conn = Redis(host='127.0.0.1', port=6379)
def process_item(self, item, spider):
dic = {
'author': item['author'],
'content': item['content'],
}
self.conn.lpush('qiubai_data',dic)
print('爬取到一条数据, 正在入库')
return item