xbhog

scrapy分布式浅谈+京东示例:

学习目标:

  1. 分布式概念与使用场景

  2. 浅谈去重

  3. 浅谈断点续爬

  4. 分布式爬虫编写流程

  5. 基于scrapy_redis的分布式爬虫(阳关院务与京东图书案例)

环境准备:

  1. 下载redis-cli(客户端)以及redis-server(服务端)

  2. 安装Another Redis Desktop Manager可视化工具

  3. 链接:https://pan.baidu.com/s/1rl8IUY7Lq54aePT54LnAkQ 提取码:1234

  4. scrapy-redis源码:git clone https://github.com/rolando/scrapy-redis.git

 

分布式概念与使用场景:

  1. 分布式听起来很高大上,但是它只是提高爬虫功能与运行效率的一个环节,

  2. 当你的数据是海量的时候,或者老板叫你在短时间内获得大量的数据,这时候才是分布式出场的时候,然而当你使用分布式的时候,难点不在于怎么部署以及编写代码;

  3. 爬虫的速度越快,所造成对方的服务器负担越重,这时候反爬才是你所真正考虑以及应对的。

  4. 概念:需要搭建一个分布式机群,然后再机群的每一台电脑中执行同一组程序,让对某一个网站的数据进行联合分布爬取

 

浅谈去重:

  1. 好处:能够减少服务器的压力以及保证数据的准确性;

  2. 每核心次请求的时候,先判断这个请求是否在已经爬取的队列当中,存在舍去,不存在爬取;

  3. 采用scrapy-redis中的set集合做的去重(可做持久化存储)。

 

浅谈断点续爬:

  1. 如果运行爬虫down掉了,在下一次启动的时候可以接入上次end的位置继续。

  2. 断点续爬就是将数据队列 集合以及任务队列实现本地持久化存储

 

分布式爬虫编写流程:

  1. 编写普通scrapy爬虫

    • 创建项目

    • 明确目标

    • 创建爬虫(普通scrapy爬虫以及crawlSpider爬虫)

    • 保存内容

  2. 改造分布式爬虫

    • 导入scrapy-redis中的分布式爬虫类

    • 继承类

    • 注销start_url & allowed-domains

    • 设置redis_key获取start_urls

    • 设置__init__获取允许的域

  3. 改造settings文件

    • copy配置文件(配置如下)

    •  1 #所有的JDspider---换成自己的爬虫名称
       2  SPIDER_MODULES = [\'JDspider.spiders\']
       3  NEWSPIDER_MODULE = \'JDspider.spiders\'
       4  5  USER_AGENT = \'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.89 Safari/537.36 Edg/84.0.522.40\'
       6  7  # 设置重复过滤器的模块
       8  DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter"
       9  # 设置调取器,scrap_redis中的调度器具备与数据库交互的功能
      10  SCHEDULER = "scrapy_redis.scheduler.Scheduler"
      11  # 设置当爬虫结束的时候是否保持redis数据库中的去重集合与任务队列,程序结束后不清空redis数据库
      12  SCHEDULER_PERSIST = True
      13  #SCHEDULER_QUEUE_CLASS = "scrapy_redis.queue.SpiderPriorityQueue"
      14  #SCHEDULER_QUEUE_CLASS = "scrapy_redis.queue.SpiderQueue"
      15  #SCHEDULER_QUEUE_CLASS = "scrapy_redis.queue.SpiderStack"
      16 17  ITEM_PIPELINES = {
      18      # \'JD.pipelines.ExamplePipeline\': 300,
      19      # 当开启该管道,该管道将会把数据存到Redis数据库中,也可以自己设置数据库
      20      \'scrapy_redis.pipelines.RedisPipeline\': 400,
      21  }
      22  # 设置redis数据库
      23  REDIS_URL = "redis://127.0.0.1:6379"
      24 25  # LOG_LEVEL = \'DEBUG\'
      26 27  # Introduce an artifical delay to make use of parallelism. to speed up the
      28  # crawl.
      29  #请求间隔时长
      30  DOWNLOAD_DELAY = 1

       

阳光院务平台scrapy-redis-Crawlspider:

编写Spider:基本代码很好理解就没写注释

 1 import scrapy
 2  from sunsite.items import SunsiteItem
 3  4  class SunproSpider(scrapy.Spider):
 5      name = \'sunpro\'
 6      # allowed_domains = [\'www.xxx.com\']
 7      start_urls = [\'http://wz.sun0769.com/political/index/politicsNewest?id=1&page=1\']
 8  9      def parse(self, response):
10          li_list = response.xpath("/html/body/div[2]/div[3]/ul[2]//li")
11          for li in li_list:
12              item = SunsiteItem()
13              item[\'title\'] = li.xpath("./span[3]/a/text()").extract_first()
14              status= li.xpath("./span[2]/text()").extract_first().split(\'\n                        \')[1]
15 16              item[\'status\'] = status.split("\n                    ")[0]
17              # print(item)
18              yield item

 

编写CrawlSpider:

 1 import scrapy
 2  from scrapy.linkextractors import LinkExtractor
 3  from scrapy.spiders import CrawlSpider, Rule
 4  from sunsite.items import SunsiteItem
 5  from scrapy_redis.spiders import RedisCrawlSpider
 6  7  class SunprocrawlSpider(RedisCrawlSpider):
 8      name = \'Sunprocrawl\'
 9      # allowed_domains = [\'www.xxx.com\']
10      # start_urls = [\'http://wz.sun0769.com/political/index/politicsNewest?id=1&page=1\']
11      redis_key = \'sunurl\'
12      rules = (
13          Rule(LinkExtractor(allow=r\'id=1&page=\d+\'), callback=\'parse_item\', follow=True),
14      )
15 16      def parse_item(self, response):
17          li_list = response.xpath("/html/body/div[2]/div[3]/ul[2]//li")
18          for li in li_list:
19              item = SunsiteItem()
20              item[\'title\'] = li.xpath("./span[3]/a/text()").extract_first()
21              status = li.xpath("./span[2]/text()").extract_first().split(\'\n                        \')[1]
22 23              item[\'status\'] = status.split("\n                    ")[0]
24              # print(item)
25              yield item

item编写:

1 import scrapy
2 3 4  class SunsiteItem(scrapy.Item):
5      title = scrapy.Field()
6      status = scrapy.Field()

 

京东图书scrapy-redis:

JDSpider:(基础代码在Github中)

 1 # -*- coding: utf-8 -*-
 2  #该spider在基础spider上进行分布式修改
 3  import scrapy
 4  from JDspider.items import JdspiderItem
 5  import json
 6  #-----1导入分布式爬虫类
 7  from scrapy_redis.spiders import RedisSpider
 8  9  class JdproSpider(RedisSpider): #----2继承RedisSpider类方法
10      name = \'JDpro\'
11      # start_urls = [\'https://book.jd.com/booksort.html\']
12      # ----4 设置redis-key
13      redis_key = \'tranurl\'
14 15      # ----5 设置__init__
16      def __init__(self, *args, **kwargs):
17          domain = kwargs.pop(\'domain\', \'\')
18          self.allowed_domains = list(filter(None, domain.split(\',\')))
19          super(JdproSpider, self).__init__(*args, **kwargs)
20 21      def parse(self, response):
22          #获取图书大分类中的列表
23          big_node_list = response.xpath("//div[@class=\'mc\']//dt/a")
24 25          # 【:1】切片,先获取一类数据测试
26          # for big_node in big_node_list[:1]:
27          for big_node in big_node_list:
28              #大分类的名称
29              big_category = big_node.xpath("./text()").extract_first()
30              #大分类的URL
31              big_category_link = response.urljoin(big_node.xpath("./@href").extract_first())
32              # print(big_category, big_category_link)
33              # 获取所有图书小分类节点列表
34              #注意点---获取兄弟节点的xpath语法结构;小分类的整体节点
35              small_node_list = big_node.xpath("../following-sibling::dd[1]/em/a")
36              #【:1】切片,先获取一类数据测试
37              for small_node in small_node_list[:1]:
38                  temp = {}
39                  temp[\'big_category\'] = big_category
40                  temp[\'big_category_link\'] = big_category_link
41                  #获取小分类的名称
42                  temp[\'small_category\'] = small_node.xpath("./text()").extract_first()
43                  #获取小分类的URL
44                  temp[\'small_category_link\'] = response.urljoin(small_node.xpath("./@href").extract_first())
45                  # print(temp)
46                  #注意点,筛选出来的数据持续传输,meta的使用
47                  yield scrapy.Request(
48                      url=temp[\'small_category_link\'],
49                      callback= self.parse_book_link,
50                      #上面保存的item传递给下一个解析函数
51                      meta = {\'data\':temp}
52                  )
53 54      #解析详情
55      def parse_book_link(self,response):
56          temp = response.meta[\'data\']
57 58          #获取到Book的标签
59          book_list = response.xpath("//*[@id=\'J_goodsList\']/ul/li/div")
60          # print(len(book_list))
61          #遍历标签页
62          for book in book_list:
63              item = JdspiderItem()
64 65              item[\'big_category\'] = temp[\'big_category\']
66              item[\'big_category_link\'] = temp[\'big_category_link\']
67              item[\'small_category\'] = temp[\'small_category\']
68              item[\'small_category_link\'] = temp[\'small_category_link\']
69              #书的名字
70              item[\'bookname\'] = book.xpath(\'./div[3]/a/em/text()|./div/div[2]/div[2]/div[3]/a/em/text()\').extract_first()
71              #书的作者
72              item[\'author\'] = book.xpath(\'./div[4]/span[1]/a/text()|./div/div[2]/div[2]/div[4]/span[1]/span[1]/a/text()\').extract_first()
73              #书的URL
74              item[\'link\'] = response.urljoin(book.xpath(\'./div[1]/a/@href|./div/div[2]/div[2]/div[1]/a/@href\').extract_first())
75              # print(item)
76              # 获取图书编号,目的拼接图书的Price
77              skuid = book.xpath(\'.//@data-sku\').extract_first()
78              # skuid = book.xpath(\'./@data-sku\').extract_first()
79              # print("skuid:",skuid)
80              # 拼接图书价格地址
81              pri_url = \'https://p.3.cn/prices/mgets?skuIds=J_\' + skuid
82              # print(pri_url)
83 84              yield scrapy.Request(url=pri_url, callback=self.parse_price, meta={\'meta_1\': item})
85              #拿到一条数据测试,可以开启
86              # break
87      def parse_price(self,response):
88          #拿到传递过来的item
89          item = response.meta[\'meta_1\']
90          #解析json页面
91          dict_data = json.loads(response.body)
92          #解析价钱,传递到item中
93          item[\'price\'] = dict_data[0][\'p\']
94          # print(item)
95          yield item
96

程序运行方式:

  1. 打开redis-server.exe

  2. 打开redis-cli.exe

  3. 找到爬虫文件下的spider

  4. scrapy runspider spiderName

  5. 在redis-cli中输入:lpush redis-keyName(spider中定义的redis-key名字) URL(网页的链接)

实现效果:

 

完整项目代码:

Github:https://github.com/xbhog/scrapyRedis

致谢:如果对您有帮助,希望随手一个star,感谢!!

分类:

技术点:

相关文章: