scrapy-redis多台机器部署,但只有一台可执行

问题描述

使用scrapy-redis进行分布式抓取时,遇到了一个很奇怪的问题,有俩台机器,Windows电脑A,和Ubuntu电脑B,redis server部署在 Windows电脑A上,在电脑A,B启动爬虫后,俩只爬虫都进入监听状态,在redis中进行 url的lpush操作,奇怪的事情发生了,电脑A,或者电脑B中只有一台电脑能监听到 redis,但是具体哪个能够监听到这个很随机,有时是电脑A,有时是电脑B,我能确保,电脑A和B都是连接上了 redis的

运行环境

  • scrapy 1.5.0
  • scrapy-redis 0.6.8
  • redis.py 2.10.6
  • redis-server(windows x64) 3.2.100

运行截图

分别启动俩个spider

启动俩个spider

第一次进行url的lpush操作,结果如下

图片描述

这时只有爬虫A监听到了 redis的操作,执行抓取逻辑,而爬虫B仍然处于监听状态

手动停止俩只spider,并清空redis数据,再次开启爬虫,执行lpush操作,结果如下

图片描述

这时,爬虫B却监听到了redis的操作,执行抓取逻辑,而爬虫A仍处于监听状态

还有一张是lpush后 redis中的数据情况

被这个问题困扰了2天了,这俩天一直没怎么睡,查了好多资料,试了好多办法,都不行,中间我把redis服务放在了电脑c上,但是还行不行。

希望前辈们,能指点一二
图片描述

阅读 5.5k
2 个回答
排查了3天,最后可算是把 这个问题解决了,最后还是有完完全全的刨析了一次,scrapy-redis的源码才找到问题.排查这个,要从爬虫运行后,Redis中的队列情况来入手

排查思路

  • 首先查看Redis中的请求队列的情况,发现在执行过程中只有存储指纹过滤的队列和item的队列,没有存储Request的队列
  • 查看源码发现,scrapy-redis在将请求入队列和出队列时,采用的是,将请求序列化后进行lpush和lpop后反序列化
  • 然后开启断点调试模式。在request lpush后,查看redis的情况,这时在redis中会出现key:reuqest的队列,但是只有一个请求
  • 继续断点发现 在lpop后,由于只向redis中添加了一个request,所以在lpop后,list中就没有元素了,redis会自动把list删除
  • 所以这时,另一个爬虫在查询主机的redis请求队列时,是查询不到任何request的,所以是一直等待的状态。
  • 这个跟爬取的数据结构有感,爬取的是一个分页的列表数据,是在解析完当前页面后,才会生成下一页的请求的。是深度优先的模式
  • 每次只生成一个请求,所以redis请求队列维护的也只有一个请求。所以就造成这样的问题
  • 出现问题的代码如下

        # 单页面解析完成,开始构建下一页的数据
        next_page_link = response.xpath('//div[@class="grid-8"]/div[@class="navigation margin-20"]/a[@class="next page-numbers"]/@href').extract_first()

        if next_page_link is None or len(next_page_link) == 0:
            log.logger.info('completed all page request')

        else:
            log.logger.info('will request next page and request url is %s'%next_page_link)
            #问题出现这这里,每次只生成一个request,所以 redis队列维护的也只有一个请求
            yield Request(url=next_page_link)

解决办法

  • 采用广度优先的模式进行抓取
  • 获取爬取的总页数,进行提取总页码,生成请求连接
  • 获取待爬取的所有页面的请求,循环入redis队列,可解决这个问题
  • 代码如下
        if self.already_push_all_request is not True:
            page_list_html_a = response.xpath('//div[@class="grid-8"]/div[@class="navigation margin-20"]/a[@class="page-numbers"]')
            last_page_list_html_a = page_list_html_a[-1]
            last_page_index = last_page_list_html_a.xpath('text()').extract_first()
            print(type(last_page_index))
            last_index_number = int(last_page_index)
            print last_index_number
            format_url = 'http://python.jobbole.com/all-posts/page/{0}/'
            next_page_index = 2
            while next_page_index <= last_index_number:
                next_page_request_url = format_url.format(next_page_index)
                print(' will lpush to redis and url is %s'%next_page_request_url)
                yield Request(url=next_page_request_url)
                next_page_index += 1
            self.already_push_all_request = True
撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
宣传栏