Scrapy 本事并不能分布式爬取,但是在某些时候,需要爬取大量数据时,就必须要用分布式去处理,这里就必须借用第三方库去扩展分布式爬取功能,Scrapy-Redis就是一个很好的分布式爬取框架,看名字就知道分布式功能是利用Redis数据库共享来实现的,整个框架对dupefilter.py,pipeline.py等模块进行了重写。以满足分布式功能。
本篇是在上一篇实现Scrapy-Splash的基础上进行添加Scrapy-Redis。
Scrapy-Redis github地址:https://github.com/rmax/scrapy-redis
1 开发环境
Windows
VScode
python 3
2 Windows 安装Redis
Redis官网:https://redis.io/download
官网上已经说明不支持Windows,但是Microsoft Open Tech group开发了一个64位版本,今天安装的版本就是这个。
github地址:https://github.com/MicrosoftArchive/redis
上面提供了两个安装方法熟悉的Nuget 和Chocolatey。Nuget没安装成功,直接采用的Chocolatey安装。Chocolatey是一个Windows下的软件管理工具,安装好Chocolatey后直接用命令安装Redis
Chocolatey安装Redis-64地址:https://chocolatey.org/packages/redis-64
安装命令:
choco install redis-64
安装完成后exe文件存在 C:\ProgramData\chocolatey\lib\redis-64 下,配置到path中
打开cmd,直接运行redis-server启动
3 配置Scrapy-redis
安装包:
pip install scrapy-redis
官方配置说明很详细,这里就是说明一下基本配置。在settings.py 添加
# Specify the full Redis URL for connecting (optional).
# If set, this takes precedence over the REDIS_HOST and REDIS_PORT settings.
#REDIS_URL = 'redis://user:pass@hostname:9001'
配置Redis数据库,没有加账号
REDIS_URL = 'redis://10.17.254.20:6379'
# Enables scheduling storing requests queue in redis.
#替换原来的调度器
SCHEDULER = "scrapy_redis.scheduler.Scheduler"
# Ensure all spiders share same duplicates filter through redis.
#去重,记得要把Scrapy_splash的注释掉
DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter"
# Store scraped item in redis for post-processing.
#数据处理,保存数据到Redis中
ITEM_PIPELINES = {
'scrapy_redis.pipelines.RedisPipeline': 300
}
修改jd.py,导入RedisSpider以及修改代码使用,注意要注释掉start_urls。修改后源码:
# -*- coding: utf-8 -*-
import scrapy
from scrapy import Request
from scrapy_splash import SplashRequest
from scrapy_redis.spiders import RedisSpider
from splashdemo.items import SplashdemoItem
import re
#设置加载完整的页面
lua_loadall="""
function main(splash)
splash:go(splash.args.url)
splash:wait(10)
splash:runjs('document.getElementById("J-global-toolbar").scrollIntoView()')
splash:wait(10)
return splash:html()
end
"""
#跳转下一页,返还下一页的url地址
def Getlua_next(pageNum):
lua_next= """
function main(splash)
splash:go(splash.args.url)
splash:wait(2)
splash:runjs("SEARCH.page(%s, true)")
splash:wait(2)
return splash:url()
end
"""%(str(pageNum))
return lua_next
#修改继承RedisSpider
# class JdSpider(scrapy.Spider):
class JdSpider(RedisSpider):
name = "jd"
allowed_domains = ["jd.com"]
#使用spider_redis 要注释掉start_urls
# start_urls = ['https://search.jd.com/Search?keyword=Python&enc=utf-8&wq=Python']
#开始页面 初始化page参数为1
def start_requests(self):
for url in self.start_urls:
#开始
yield Request(url,callback=self.parse_url,meta={'page':1},dont_filter=True)
#第一次单独处理
def parse_url(self,response):
url =response.url
metadata={'page':response.meta["page"]}
#第一次加载剩余item-
yield SplashRequest(url,meta=metadata,endpoint='execute',args={'lua_source':lua_loadall},cache_args=['lua_source'])
#根据跳转页面返回回来的url地址进行页面完整加载
def parse_url2(self,response):
url=response.body_as_unicode()
#加载剩余item
yield SplashRequest(url,meta={'page':response.meta['page']},endpoint='execute',args={'lua_source':lua_loadall},cache_args=['lua_source'])
#处理数据并判断是否继续爬取
def parse(self, response):
#处理数据
pagenum=int(response.meta['page'])
for book in response.xpath('.//li[@class="gl-item"]'):#获取所有item
url=book.xpath('.//div[@class="p-name"]/a/@href').extract_first()#获取item的url
bookname = book.xpath('.//div[@class="p-name"]/a/em').extract_first()
#正则提取替换得到书名
rebookname=re.compile(r'<.*?>')
price =book.xpath('.//div[@class="p-price"]/strong/i/text()').extract_first()#获取价格
if 'https'not in url:#争对自营的部分 添加http
url=response.urljoin(url)
item= SplashdemoItem()
item['BookName']=rebookname.sub('',bookname)#根据正则进行反向替换,去掉书名中的一些元素
item['Price']=price
item['BuyLink']=url
yield item
#翻页 判断是否到最后一页
if len(response.xpath('.//div[@class="pn-next disabled"]/em/b/text()').extract())<= 0 :
yield SplashRequest(response.url,meta={'page':pagenum+1},callback=self.parse_url2,endpoint='execute',args={'lua_source': Getlua_next(2*pagenum+1)},cache_args=['lua_source'],dont_filter=True)
4 爬取数据
运行爬虫
scrapy crawl jd
在输出中会看到如下信息:
此时Scrapy处于监听状态,等待输入爬取地址。
打开cmd,在cmd中输入redis-cli无账号登录本地Redis。输入需要爬取的地址
lpush jd:start_urls 'https://search.jd.com/Search?keyword=Python&enc=utf-8&wq=Python'
RedisStudio登录Redis数据查看爬取结果:
未处理的问题,中文在Redis数据库中显示为乱码,正在查看解决方法。