Scrapy-Splash与Scrapy-Redis 结合

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启动


启动Redis

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数据库中显示为乱码,正在查看解决方法。

你可能感兴趣的:(Scrapy-Splash与Scrapy-Redis 结合)