- 概念:检测网站数据更新的情况,只会爬取网站最新更新出来的数据
- 增量式爬取的核心是去重,去重方法如下:
-
思路:通过redis的set集合实现去重的目的
-
将爬取过程中产生的URL存储到redis的set数据结构中
-
根据sadd(key,value) 的返回结果,判断此 URL 是否已爬取过
-
1 表示没有爬取过,则发起请求; 0 表示已爬取过,则不进行请求
-
示例:在redis中输入以下内容,可以得到下图
# 向name(set集)中添加一个'jay' sadd name jay sadd name lily sadd name jay # 再次添加后,会返回0,表示没有添加,因为name中已经有了
-
-
案例
-
需求:增量式爬取电影网站中最近更新的数据
-
分析:
-
指定一个起始的url
-
基于CrawlSpider获取其他页码链接
-
基于Rule将其他页码链接进行请求
-
从每一个页码对应的页面源码中解析出每一个电影详情页的url
-
核心:检测电影详情页的url之前有没有请求过
将爬取过的电影详情页的url存储到Redis的set数据结构,实现去重
-
对详情页的url发起请求,然后解析出电影的名称和简介
-
进行持久化存储
-
-
代码实现
-
启动redis服务和打开redis终端
- 启动redis:双击红框,启动服务
-
打开redis客户端:注意要修改编码格式为’UTF-8‘,不然中文显示出来都是16进制的,具体操作如下:
-
临时修改:只作用于当前窗口,先进入cmd命令窗口(快捷键win键+R)
-
直接输入“chcp 65001”,回车键(Enter键)执行,这时候该窗口编码已经是UTF-8编码了。
-
然后进入redis安装的目录下
- 输入
redis-cli --raw
,进入redis客户端
- 输入
flushall
: 清空redis的数据
-
-
创建一个工程:
scrapy startproject moviePro
-
进入到工程目录中:
cd moviePro
-
创建一个基于CrawlSpider的爬虫文件:
scrapy genspider -t crawl movie www.xxx.com
-
编写爬虫文件movie.py
import scrapy from scrapy.linkextractors import LinkExtractor from scrapy.spiders import CrawlSpider, Rule from moviePro.items import MovieproItem from redis import Redis class MovieSpider(CrawlSpider): name = 'movie' # allowed_domains = ['www.xxx.com'] start_urls = ['https://www.4567kp.com/frim/index1.html'] rules = ( Rule(LinkExtractor(allow=r'/frim/index1-\d+\.html'), callback='parse_item', follow=True), ) # 创建redis链接对象 conn = Redis(host='127.0.0.1', port=6379) # 用于解析每个页码对应页面中的电影详情页的url def parse_item(self, response): li_list = response.xpath('/html//div[1]/div/div/div/div[2]/ul/li') print(len(li_list)) for li in li_list: # 获取详情页的url detail_url = 'https://www.4567kp.com' + li.xpath('./div/a/@href').extract_first() # 将详情页的url存入redis的set中 ex = self.conn.sadd('urls', detail_url) # ex == 1 :字符串插入成功 # ex == 0 :插入的字符串重复了,利用redis的set集合去重 if ex == 1: print("该url没有被爬取过,可以进行数据爬取") yield scrapy.Request(url=detail_url, callback=self.parse_detail) else: print("数据还没有更新,暂无新数据可爬取!") # 解析详情页中的电影名称和简介,进行持久化存储 def parse_detail(self, response): item = MovieproItem() item['name'] = response.xpath('/html//div[1]/div/div/div/div[2]/h1/text()').extract_first() item['desc'] = response.xpath('/html//div[1]/div/div/div/div[2]/p[5]/span[2]/text()').extract() item['desc'] = ''.join(item['desc']) yield item
-
在items.py中创建两个属性
import scrapy class MovieproItem(scrapy.Item): # define the fields for your item here like: name = scrapy.Field() desc = scrapy.Field()
-
在pipelines.py中编写代码
class MovieproPipeline: conn = None def open_spider(self, spider): self.conn = spider.conn def process_item(self, item, spider): dic = { 'name': item['name'], 'desc': item['desc'] } # print(dic) self.conn.lpush('movieData', dic) return item
-
修改配置文件settings.py
USER_AGENT = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.110 Safari/537.36' # Obey robots.txt rules # True:遵从robots协议 ROBOTSTXT_OBEY = False # 显示指定的类型的日志信息 LOG_LEVEL = 'ERROR' ITEM_PIPELINES = { 'moviePro.pipelines.MovieproPipeline': 300, }
-
在终端中输入:
scrapy crawl movie
,即可- 第一次爬取数据的时候,结果如下
- 第二次爬取该网站数据时,结果如下
-
在redis中查看存储情况
-
keys * : 查看数据存储结果列表
-
smembers urls : 查看urls存储内容
-
llen movieData:查看数据大小
-
lrange movieData 0 4:查看前5条样例数据
-
-
-
报错
-
在运行脚本时,出现了如下的一个bug
redis.exceptions.DataError: Invalid input of type: 'dict'. Convert to a bytes, string, int or float first.
- 问题点:是 因为python中的redis版本过高导致的,需降低版本
- 解决方法:
- 查看python中安装的redis的版本,发现是4.0以上的版本
- 将版本降低到2.10.6:
pip install -U redis==2.10.6
- 再次运行脚本,发现运行成功
-
参考链接
如果本文对你有帮助,记得“点赞、收藏”哦~