scrapy分布式浅谈+京东示例

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

学习目标:

  1. 分布式概念与使用场景
  2. 浅谈去重
  3. 浅谈断点续爬
  4. 分布式爬虫编写流程
  5. 基于scrapy_redis的分布式爬虫(阳关院务与京东图书案例)

环境准备:

  1. 下载redis-cli(客户端)以及redis-server(服务端)
  2. 安装Another Redis Desktop Manager可视化工具
  3. 链接:pan.baidu.com/s/1rl8IUY7Lq54aePT54... 提取码: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配置文件(配置如下)

    • #所有的JDspider---换成自己的爬虫名称 SPIDER_MODULES = ['JDspider.spiders'] NEWSPIDER_MODULE = 'JDspider.spiders' 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' # 设置重复过滤器的模块 DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter" # 设置调取器,scrap_redis中的调度器具备与数据库交互的功能 SCHEDULER = "scrapy_redis.scheduler.Scheduler" # 设置当爬虫结束的时候是否保持redis数据库中的去重集合与任务队列,程序结束后不清空redis数据库 SCHEDULER_PERSIST = True #SCHEDULER_QUEUE_CLASS = "scrapy_redis.queue.SpiderPriorityQueue" #SCHEDULER_QUEUE_CLASS = "scrapy_redis.queue.SpiderQueue" #SCHEDULER_QUEUE_CLASS = "scrapy_redis.queue.SpiderStack" ITEM_PIPELINES = { # 'JD.pipelines.ExamplePipeline': 300, # 当开启该管道,该管道将会把数据存到Redis数据库中,也可以自己设置数据库 'scrapy_redis.pipelines.RedisPipeline': 400, } # 设置redis数据库 REDIS_URL = "redis://127.0.0.1:6379" # LOG_LEVEL = 'DEBUG' # Introduce an artifical delay to make use of parallelism. to speed up the # crawl. #请求间隔时长 DOWNLOAD_DELAY = 1

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

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

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

编写CrawlSpider:

import scrapy from scrapy.linkextractors import LinkExtractor from scrapy.spiders import CrawlSpider, Rule from sunsite.items import SunsiteItem from scrapy_redis.spiders import RedisCrawlSpider class SunprocrawlSpider(RedisCrawlSpider): name = 'Sunprocrawl' # allowed_domains = ['www.xxx.com'] # start_urls = ['http://wz.sun0769.com/political/index/politicsNewest?id=1&page=1'] redis_key = 'sunurl' rules = ( Rule(LinkExtractor(allow=r'id=1&page=\d+'), callback='parse_item', follow=True), ) def parse_item(self, response): li_list = response.xpath("/html/body/div[2]/div[3]/ul[2]//li") for li in li_list: item = SunsiteItem() item['title'] = li.xpath("./span[3]/a/text()").extract_first() status = li.xpath("./span[2]/text()").extract_first().split('\n ')[1] item['status'] = status.split("\n ")[0] # print(item) yield item

item编写:

import scrapy class SunsiteItem(scrapy.Item): title = scrapy.Field() status = scrapy.Field()

京东图书scrapy-redis:

JDSpider:(基础代码在Github中)

# -*- coding: utf-8 -*- #该spider在基础spider上进行分布式修改 import scrapy from JDspider.items import JdspiderItem import json #-----1导入分布式爬虫类 from scrapy_redis.spiders import RedisSpider class JdproSpider(RedisSpider): #----2继承RedisSpider类方法 name = 'JDpro' # start_urls = ['https://book.jd.com/booksort.html'] # ----4 设置redis-key redis_key = 'tranurl' # ----5 设置__init__ def __init__(self, *args, **kwargs): domain = kwargs.pop('domain', '') self.allowed_domains = list(filter(None, domain.split(','))) super(JdproSpider, self).__init__(*args, **kwargs) def parse(self, response): #获取图书大分类中的列表 big_node_list = response.xpath("//div[@class='mc']//dt/a") # 【:1】切片,先获取一类数据测试 # for big_node in big_node_list[:1]: for big_node in big_node_list: #大分类的名称 big_category = big_node.xpath("./text()").extract_first() #大分类的URL big_category_link = response.urljoin(big_node.xpath("./@href").extract_first()) # print(big_category, big_category_link) # 获取所有图书小分类节点列表 #注意点---获取兄弟节点的xpath语法结构;小分类的整体节点 small_node_list = big_node.xpath("../following-sibling::dd[1]/em/a") #【:1】切片,先获取一类数据测试 for small_node in small_node_list[:1]: temp = {} temp['big_category'] = big_category temp['big_category_link'] = big_category_link #获取小分类的名称 temp['small_category'] = small_node.xpath("./text()").extract_first() #获取小分类的URL temp['small_category_link'] = response.urljoin(small_node.xpath("./@href").extract_first()) # print(temp) #注意点,筛选出来的数据持续传输,meta的使用 yield scrapy.Request( url=temp['small_category_link'], callback= self.parse_book_link, #上面保存的item传递给下一个解析函数 meta = {'data':temp} ) #解析详情 def parse_book_link(self,response): temp = response.meta['data'] #获取到Book的标签 book_list = response.xpath("//*[@id='J_goodsList']/ul/li/div") # print(len(book_list)) #遍历标签页 for book in book_list: item = JdspiderItem() item['big_category'] = temp['big_category'] item['big_category_link'] = temp['big_category_link'] item['small_category'] = temp['small_category'] item['small_category_link'] = temp['small_category_link'] #书的名字 item['bookname'] = book.xpath('./div[3]/a/em/text()|./div/div[2]/div[2]/div[3]/a/em/text()').extract_first() #书的作者 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() #书的URL item['link'] = response.urljoin(book.xpath('./div[1]/a/@href|./div/div[2]/div[2]/div[1]/a/@href').extract_first()) # print(item) # 获取图书编号,目的拼接图书的Price skuid = book.xpath('.//@data-sku').extract_first() # skuid = book.xpath('./@data-sku').extract_first() # print("skuid:",skuid) # 拼接图书价格地址 pri_url = 'https://p.3.cn/prices/mgets?skuIds=J_' + skuid # print(pri_url) yield scrapy.Request(url=pri_url, callback=self.parse_price, meta={'meta_1': item}) #拿到一条数据测试,可以开启 # break def parse_price(self,response): #拿到传递过来的item item = response.meta['meta_1'] #解析json页面 dict_data = json.loads(response.body) #解析价钱,传递到item中 item['price'] = dict_data[0]['p'] # print(item) yield item 

程序运行方式:

  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:github.com/xbhog/scrapyRedis

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

本作品采用《CC 协议》,转载必须注明作者和本文链接
讨论数量: 0
(= ̄ω ̄=)··· 暂无内容!