详解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用GET方法上传文件
Mar 10 Python
连接Python程序与MySQL的教程
Apr 29 Python
Python中利用sqrt()方法进行平方根计算的教程
May 15 Python
对于Python装饰器使用的一些建议
Jun 03 Python
Python操作Excel之xlsx文件
Mar 24 Python
Python在图片中添加文字的两种方法
Apr 29 Python
flask + pymysql操作Mysql数据库的实例
Nov 13 Python
Django中的ajax请求
Oct 19 Python
python 使用 requests 模块发送http请求 的方法
Dec 09 Python
Flask框架学习笔记之消息提示与异常处理操作详解
Aug 15 Python
Python基于execjs运行js过程解析
Nov 27 Python
pytorch finetuning 自己的图片进行训练操作
Jun 05 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
在Zeus Web Server中安装PHP语言支持
2006/10/09 PHP
PHP远程连接MYSQL数据库非常慢的解决方法
2008/07/05 PHP
PHP 面向对象 PHP5 中的常量
2010/05/05 PHP
PHP中3种生成XML文件方法的速度效率比较
2012/10/06 PHP
Django 标签筛选的实现代码(一对多、多对多)
2018/09/05 PHP
判断浏览器的javascript版本的代码
2010/09/03 Javascript
JS动态创建DOM元素的方法
2015/06/09 Javascript
JavaScript简单判断复选框是否选中及取出值的方法
2015/08/13 Javascript
Javascript中Promise的四种常用方法总结
2017/07/14 Javascript
jQuery实现动态添加节点与遍历节点功能示例
2017/11/09 jQuery
layui的table单击行勾选checkbox功能方法
2018/08/14 Javascript
如何使用CSS3+JQuery实现悬浮墙式菜单
2019/06/18 jQuery
[03:30]完美盛典趣味短片 CSGO2019年度名场面
2019/12/07 DOTA
利用python3随机生成中文字符的实现方法
2017/11/24 Python
zookeeper python接口实例详解
2018/01/18 Python
django 发送手机验证码的示例代码
2018/04/25 Python
解决Pycharm无法import自己安装的第三方module问题
2018/05/18 Python
python使用opencv驱动摄像头的方法
2018/08/03 Python
使用python对excle和json互相转换的示例
2018/10/23 Python
python爬虫利器之requests库的用法(超全面的爬取网页案例)
2020/12/17 Python
纯CSS3实现给头像加个光芒四射且旋转的背景动画效果
2014/05/07 HTML / CSS
HTML5之WebGL 3D概述(上)—WebGL原生开发开启网页3D渲染新时代
2013/01/31 HTML / CSS
新加坡网上化妆品店:Best Buy World
2018/05/18 全球购物
计算机专业毕业生求职信分享
2013/12/24 职场文书
2014年高考决心书
2014/03/11 职场文书
师德模范事迹材料
2014/06/03 职场文书
服务标语口号
2014/07/01 职场文书
市场营销毕业求职信
2014/08/07 职场文书
党性教育心得体会
2014/09/03 职场文书
离婚协议书格式
2015/01/26 职场文书
北京大学中文系教授推荐的10本小说
2019/08/08 职场文书
MySQL慢查询的坑
2021/04/28 MySQL
常用的Python代码调试工具总结
2021/06/23 Python
源码分析Redis中 set 和 sorted set 的使用方法
2022/03/22 Redis
vue+iview实现手机号分段输入框
2022/03/25 Vue.js
【海涛dota解说】海涛小满开黑4v5被破两路翻盘潮汐第一视角解说
2022/04/01 DOTA