鍍金池/ 問答/人工智能  網(wǎng)絡(luò)安全/ scrapy-redis多臺(tái)機(jī)器部署,但只有一臺(tái)可執(zhí)行

scrapy-redis多臺(tái)機(jī)器部署,但只有一臺(tái)可執(zhí)行

問題描述

使用scrapy-redis進(jìn)行分布式抓取時(shí),遇到了一個(gè)很奇怪的問題,有倆臺(tái)機(jī)器,Windows電腦A,和Ubuntu電腦B,redis server部署在 Windows電腦A上,在電腦A,B啟動(dòng)爬蟲后,倆只爬蟲都進(jìn)入監(jiān)聽狀態(tài),在redis中進(jìn)行 url的lpush操作,奇怪的事情發(fā)生了,電腦A,或者電腦B中只有一臺(tái)電腦能監(jiān)聽到 redis,但是具體哪個(gè)能夠監(jiān)聽到這個(gè)很隨機(jī),有時(shí)是電腦A,有時(shí)是電腦B,我能確保,電腦A和B都是連接上了 redis的

運(yùn)行環(huán)境

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

運(yùn)行截圖

分別啟動(dòng)倆個(gè)spider

啟動(dòng)倆個(gè)spider

第一次進(jìn)行url的lpush操作,結(jié)果如下

圖片描述

這時(shí)只有爬蟲A監(jiān)聽到了 redis的操作,執(zhí)行抓取邏輯,而爬蟲B仍然處于監(jiān)聽狀態(tài)

手動(dòng)停止倆只spider,并清空redis數(shù)據(jù),再次開啟爬蟲,執(zhí)行l(wèi)push操作,結(jié)果如下

圖片描述

這時(shí),爬蟲B卻監(jiān)聽到了redis的操作,執(zhí)行抓取邏輯,而爬蟲A仍處于監(jiān)聽狀態(tài)

還有一張是lpush后 redis中的數(shù)據(jù)情況

被這個(gè)問題困擾了2天了,這倆天一直沒怎么睡,查了好多資料,試了好多辦法,都不行,中間我把redis服務(wù)放在了電腦c上,但是還行不行。

希望前輩們,能指點(diǎn)一二
圖片描述

回答
編輯回答
青黛色
排查了3天,最后可算是把 這個(gè)問題解決了,最后還是有完完全全的刨析了一次,scrapy-redis的源碼才找到問題.排查這個(gè),要從爬蟲運(yùn)行后,Redis中的隊(duì)列情況來入手

排查思路

  • 首先查看Redis中的請求隊(duì)列的情況,發(fā)現(xiàn)在執(zhí)行過程中只有存儲(chǔ)指紋過濾的隊(duì)列和item的隊(duì)列,沒有存儲(chǔ)Request的隊(duì)列
  • 查看源碼發(fā)現(xiàn),scrapy-redis在將請求入隊(duì)列和出隊(duì)列時(shí),采用的是,將請求序列化后進(jìn)行l(wèi)push和lpop后反序列化
  • 然后開啟斷點(diǎn)調(diào)試模式。在request lpush后,查看redis的情況,這時(shí)在redis中會(huì)出現(xiàn)key:reuqest的隊(duì)列,但是只有一個(gè)請求
  • 繼續(xù)斷點(diǎn)發(fā)現(xiàn) 在lpop后,由于只向redis中添加了一個(gè)request,所以在lpop后,list中就沒有元素了,redis會(huì)自動(dòng)把list刪除
  • 所以這時(shí),另一個(gè)爬蟲在查詢主機(jī)的redis請求隊(duì)列時(shí),是查詢不到任何request的,所以是一直等待的狀態(tài)。
  • 這個(gè)跟爬取的數(shù)據(jù)結(jié)構(gòu)有感,爬取的是一個(gè)分頁的列表數(shù)據(jù),是在解析完當(dāng)前頁面后,才會(huì)生成下一頁的請求的。是深度優(yōu)先的模式
  • 每次只生成一個(gè)請求,所以redis請求隊(duì)列維護(hù)的也只有一個(gè)請求。所以就造成這樣的問題
  • 出現(xiàn)問題的代碼如下

        # 單頁面解析完成,開始構(gòu)建下一頁的數(shù)據(jù)
        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)
            #問題出現(xiàn)這這里,每次只生成一個(gè)request,所以 redis隊(duì)列維護(hù)的也只有一個(gè)請求
            yield Request(url=next_page_link)

解決辦法

  • 采用廣度優(yōu)先的模式進(jìn)行抓取
  • 獲取爬取的總頁數(shù),進(jìn)行提取總頁碼,生成請求連接
  • 獲取待爬取的所有頁面的請求,循環(huán)入redis隊(duì)列,可解決這個(gè)問題
  • 代碼如下
        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
2018年5月27日 12:53