详解Scrapy Redis入门实战


Posted in Python onNovember 18, 2020

简介

scrapy-redis是一个基于redis的scrapy组件,用于快速实现scrapy项目的分布式部署和数据爬取,其运行原理如下图所示。

详解Scrapy Redis入门实战

Scrapy-Redis特性

分布式爬取

你可以启动多个共享同一redis队列的爬虫实例,多个爬虫实例将各自提取到或者已请求的Requests在队列中统一进行登记,使得Scheduler在请求调度时能够对重复Requests进行过滤,即保证已经由某一个爬虫实例请求过的Request将不会再被其他的爬虫实例重复请求。

分布式数据处理

将scrapy爬取到的items汇聚到同一个redis队列中,意味着你可以根据你的需要启动尽可能多的共享这个items队列的后处理程序。

Scrapy即插即用组件

Scheduler调度器 + Duplication重复过滤器、Item Pipeline、基础Spider爬虫

Scrapy-Redis示例

本文将以爬取京东所有图书分类下的图书信息为例对Scrapy-Redis的用法进行示例。

开发环境

  • Python 3.7
  • Redis 3.2.100

下面列举出了 Python 中 Scrapy-Redis 所需要的各个模块及其版本:

  • redis 2.10.6
  • redis-py-cluster 1.3.6
  • scrapy-redis 0.6.8
  • scrapy-redis-cluster 0.4

在开发之前需要先安装好以上模块,以scrapy-redis-cluster模块为例,使用pip进行安装的命令如下:

pip install scrapy-redis-cluster # 安装模块
pip install scrapy-redis-cluster==0.4 # 安装模块时指定版本
pip install --upgrade scrapy-redis-cluster # 升级模块版本

创建项目

在Windows命令行执行如下命令完成项目创建:

d:\scrapy>scrapy startproject jd_book

执行完该命令后,将会在当前目录下创建包含下列内容的 jd_book 目录:

详解Scrapy Redis入门实战

定义Item

在items.py中把我们将要爬取的图书字段预先定义好。

# -*- coding: utf-8 -*-

import scrapy

class JdBookItem(scrapy.Item):
  b_cate = scrapy.Field() # 图书所属一级分类名称
  s_cate = scrapy.Field() # 图书所属二级分类名称
  s_href = scrapy.Field() # 图书所属二级分类地址
  book_name = scrapy.Field() # 名称
  book_img = scrapy.Field() # 封面图片地址
  book_author = scrapy.Field() # 作者
  book_press = scrapy.Field() # 出版社
  book_publish_date = scrapy.Field() # 出版日期
  book_sku = scrapy.Field() # 商品编号
  book_price = scrapy.Field() # 价格

创建Spider

在Windows命令行执行如下命令完成Spider创建:

d:\scrapy\jd_book>cd jd_book
d:\scrapy\jd_book>scrapy genspider jdbook jd.com

执行完该命令后,将会在 jd_book 的 spiders 目录下生成一个 jdbook.py 文件 :

详解Scrapy Redis入门实战

 jdbook.py的完整爬虫代码如下。

# -*- coding: utf-8 -*-
import scrapy
import json
import urllib
from copy import deepcopy
from jd_book.items import JdBookItem

class JdbookSpider(scrapy.Spider):
  name = 'jdbook'
  allowed_domains = ['jd.com','3.cn']
  start_urls = ['https://book.jd.com/booksort.html']

  def parse(self, response): # 处理图书分类页
    dt_list = response.xpath("//div[@class='mc']/dl/dt") # 提取一级分类元素
    for dt in dt_list:
      item = JdBookItem()
      item["b_cate"] = dt.xpath("./a/text()").extract_first() # 提取一级分类名称
      em_list = dt.xpath("./following-sibling::dd[1]/em") # 提取二级分类元素
      for em in em_list:
        item["s_cate"] = em.xpath("./a/text()").extract_first() # 提取二级分类名称
        item["s_href"] = em.xpath("./a/@href").extract_first() # 提取二级分类地址
        if item["s_href"] is not None:
          item['s_href'] = "https:" + item['s_href'] # 补全二级分类地址
          yield scrapy.Request(item['s_href'], callback=self.parse_book_list, meta={"item": deepcopy(item)})

  def parse_book_list(self, response): # 处理二级分类下图书列表页
    item = response.meta['item']
    li_list = response.xpath("//div[@id='plist']/ul/li") # 提取所有的图书元素
    for li in li_list:
      item["book_img"] = li.xpath(".//div[@class='p-img']//img/@data-lazy-img").extract_first()
      if item["book_img"] is None:
        item["book_img"] = li.xpath(".//div[@class='p-img']//img/@src").extract_first()
      if item["book_img"] is not None:
        item["book_img"] = "https:"+item["book_img"]
      item["book_name"] = li.xpath(".//div[@class='p-name']/a/em/text()").extract_first().strip()
      item["book_author"] = li.xpath(".//span[@class='author_type_1']/a/text()").extract()
      item["book_press"] = li.xpath(".//span[@class='p-bi-store']/a/@title").extract_first()
      item["book_publish_date"] = li.xpath(".//span[@class='p-bi-date']/text()").extract_first().strip()
      item["book_sku"] = li.xpath("./div/@data-sku").extract_first()
      price_url = "https://p.3.cn/prices/mgets?skuIds=j_{}".format(item["book_sku"]) # 提取图书价格请求地址
      yield scrapy.Request(price_url, callback=self.parse_book_price, meta={"item": deepcopy(item)})

    # 提取列表页下一页地址
    next_url = response.xpath("//a[@class='pn-next']/@href").extract_first()
    if next_url is not None:
      next_url = urllib.parse.urljoin(response.url, next_url)
      # yield scrapy.Request(next_url,callback=self.parse_book_list,meta={"item":item})

  def parse_book_price(self, response):
    item = response.meta['item']
    item["book_price"] = json.loads(response.body.decode())[0]["op"]
    yield item

修改配置

在settings.py 中增加Scrapy-Redis相关配置。

# -*- coding: utf-8 -*-

BOT_NAME = 'jd_book'

SPIDER_MODULES = ['jd_book.spiders']
NEWSPIDER_MODULE = 'jd_book.spiders'

# Crawl responsibly by identifying yourself (and your website) on the user-agent
USER_AGENT = 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36'

# Obey robots.txt rules
ROBOTSTXT_OBEY = False


######################################################
##############下面是Scrapy-Redis相关配置################
######################################################

# 指定Redis的主机名和端口
REDIS_HOST = 'localhost'
REDIS_PORT = 6379

# 调度器启用Redis存储Requests队列
SCHEDULER = "scrapy_redis.scheduler.Scheduler"

# 确保所有的爬虫实例使用Redis进行重复过滤
DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter"

# 将Requests队列持久化到Redis,可支持暂停或重启爬虫
SCHEDULER_PERSIST = True

# Requests的调度策略,默认优先级队列
SCHEDULER_QUEUE_CLASS = 'scrapy_redis.queue.PriorityQueue'

# 将爬取到的items保存到Redis 以便进行后续处理
ITEM_PIPELINES = {
  'scrapy_redis.pipelines.RedisPipeline': 300
}

启动爬虫

至此京东图书项目就算配置完成了,你可以将项目部署到多台服务器中去,并使用如下命令来启动爬虫:

d:\scrapy\jd_book>scrapy crawl jdbook

爬取到的图书数据结构如下:

详解Scrapy Redis入门实战

相应地,在Redis数据库中同时生成了如下3个键:

详解Scrapy Redis入门实战

其中,jdbook:requests 中保存了待爬取的Request对象;jdbook:dupefilter 中保存了已经爬取过的Request对象的指纹;jdbook:items中保存了爬取到的Item对象。

 通过上述京东图书项目不难看出,scrapy-redis项目与普通的scrapy项目相比,除了在settings.py配置时额外增加了一些scrapy-redis的专属配置外,其他环节完全相同。

参考文章

 https://scrapy-redis.readthedocs.io/en/stable/index.html

到此这篇关于详解Scrapy Redis入门实战的文章就介绍到这了,更多相关Scrapy Redis入门内容请搜索三水点靠木以前的文章或继续浏览下面的相关文章希望大家以后多多支持三水点靠木!

Python 相关文章推荐
Python减少循环层次和缩进的技巧分析
Mar 15 Python
用python记录运行pid,并在需要时kill掉它们的实例
Jan 16 Python
Python编程实现粒子群算法(PSO)详解
Nov 13 Python
numpy实现合并多维矩阵、list的扩展方法
May 08 Python
Python实现简单的列表冒泡排序和反转列表操作示例
Jul 10 Python
Python绘制股票移动均线的实例
Aug 24 Python
Python正则表达式学习小例子
Mar 03 Python
Python 实现打印单词的菱形字符图案
Apr 12 Python
python和js交互调用的方法
Jun 23 Python
Django serializer优化类视图的实现示例
Jul 16 Python
Python钉钉报警及Zabbix集成钉钉报警的示例代码
Aug 17 Python
python获取天气接口给指定微信好友发天气预报
Dec 28 Python
如何在scrapy中集成selenium爬取网页的方法
Nov 18 #Python
Python 实现键盘鼠标按键模拟
Nov 18 #Python
如何向scrapy中的spider传递参数的几种方法
Nov 18 #Python
python更新数据库中某个字段的数据(方法详解)
Nov 18 #Python
Python下载的11种姿势(小结)
Nov 18 #Python
Python监听键盘和鼠标事件的示例代码
Nov 18 #Python
Opencv python 图片生成视频的方法示例
Nov 18 #Python
You might like
PHP 5.3新特性命名空间规则解析及高级功能
2010/03/11 PHP
学习php设计模式 php实现合成模式(composite)
2015/12/08 PHP
ThinkPHP框架使用redirect实现页面重定向的方法实例分析
2018/04/12 PHP
PHP实现的解汉诺塔问题算法示例
2018/08/06 PHP
CI框架网页缓存简单用法分析
2018/12/26 PHP
php实现银联商务公众号+服务窗支付的示例代码
2019/10/12 PHP
input+select(multiple) 实现下拉框输入值
2009/05/21 Javascript
Javascript生成json的函数代码(可以用php的json_decode解码)
2012/06/11 Javascript
jquery插件制作 自增长输入框实现代码
2012/08/17 jQuery
JS操作CSS随机改变网页背景实现思路
2014/03/10 Javascript
javascript弹出页面回传值的方法
2015/01/28 Javascript
javascript实时显示北京时间的方法
2015/03/12 Javascript
AngularJs Javascript MVC 框架
2016/06/20 Javascript
angular+ionic 的app上拉加载更新数据实现方法
2017/01/16 Javascript
jquery实现input框获取焦点的方法
2017/02/06 Javascript
jQuery插件HighCharts绘制2D柱状图、折线图的组合双轴图效果示例【附demo源码下载】
2017/03/09 Javascript
基于bootstrap按钮式下拉菜单组件的搜索建议插件
2017/03/25 Javascript
妙用Angularjs实现表格按指定列排序
2017/06/23 Javascript
Vue v-model组件封装(类似弹窗组件)
2020/01/08 Javascript
[01:01:42]Secret vs Optic Supermajor 胜者组 BO3 第二场 6.4
2018/06/05 DOTA
Python中join和split用法实例
2015/04/14 Python
python先序遍历二叉树问题
2017/11/10 Python
python3写的简单本地文件上传服务器实例
2018/06/04 Python
Python面向对象之继承和组合用法实例分析
2018/08/27 Python
Flask框架工厂函数用法实例分析
2019/05/25 Python
python Django框架实现web端分页呈现数据
2019/10/31 Python
python给图像加上mask,并提取mask区域实例
2020/01/19 Python
Python表达式的优先级详解
2020/02/18 Python
Python实现动态循环输出文字功能
2020/05/07 Python
Python异常处理机制结构实例解析
2020/07/23 Python
HTML5 b和i标记将被赋予真正的语义
2009/07/16 HTML / CSS
企业后勤岗位职责
2014/02/28 职场文书
食品委托检验协议书范本
2014/09/12 职场文书
建议书的格式及范文
2015/09/14 职场文书
Sql Server 行数据的某列值想作为字段列显示的方法
2022/04/20 SQL Server
Win11如何默认打开软件界面最大化?Win11默认打开软件界面最大化的方法
2022/07/15 数码科技