【问题标题】:how to run spider multiple times with different input如何使用不同的输入多次运行蜘蛛
【发布时间】:2020-11-13 00:25:09
【问题描述】:

我正在尝试从不同网站上抓取有关某些产品的信息。这是我的程序的结构:

product_list = [iPad, iPhone, AirPods, ...]

def spider_tmall:
    self.driver.find_element_by_id('searchKeywords').send_keys(inputlist[a])

# ...


def spider_jd:
    self.driver.find_element_by_id('searchKeywords').send_keys(inputlist[a])

# ...

if __name__ == '__main__':

    for a in range(len(inputlist)):
        process = CrawlerProcess(settings={
            "FEEDS": {
                "itemtmall.csv": {"format": "csv",
                                  'fields': ['product_name_tmall', 'product_price_tmall', 'product_discount_tmall'], },
                "itemjd.csv": {"format": "csv",
                               'fields': ['product_name_jd', 'product_price_jd', 'product_discount_jd'], },
        })

        process.crawl(tmallSpider)
        process.crawl(jdSpider)
        process.start()

基本上,我想为product_list 中的所有输入运行所有蜘蛛。现在,我的程序只运行一次所有蜘蛛(在这种情况下,它为 iPad 完成了这项工作)然后出现 ReactorNotRestartable 错误并且程序终止。有人知道如何解决吗? 另外,我的总体目标是多次运行蜘蛛,输入不一定是列表。它可以是 CSV 文件或其他文件。任何建议将不胜感激!

【问题讨论】:

    标签: python selenium web-scraping scrapy web-crawler


    【解决方案1】:

    这个过程应该在所有的蜘蛛都设置好后开始,就像在这里看到的那样:

    https://docs.scrapy.org/en/latest/topics/practices.html#running-multiple-spiders-in-the-same-process

    在您的情况下,多一点代码会有所帮助,但我想为所有产品的两个蜘蛛设置所有爬网过程,然后启动 start() 函数。

    if __name__ == '__main__':
    
        for a in range(len(inputlist)):
            process = CrawlerProcess(settings={
                "FEEDS": {
                    "itemtmall.csv": {"format": "csv",
                                      'fields': ['product_name_tmall', 'product_price_tmall', 'product_discount_tmall'], },
                    "itemjd.csv": {"format": "csv",
                                   'fields': ['product_name_jd', 'product_price_jd', 'product_discount_jd'], },
            })
    
            process.crawl(tmallSpider)
            process.crawl(jdSpider)
        process.start()
    

    【讨论】:

    • 谢谢,但我用 inputlist = [iPhone, iPad] 试过了。没有错误被抛出,程序能够完成。但是,该程序以“iPad”作为输入运行了蜘蛛两次,而不是 iPhone 和 iPad。你知道怎么解决吗?
    • 不确定,您在生成蜘蛛时是否正确设置了变量?在提供的代码中,“a”变量完全未使用,这就是参考。您可以将参数传递给蜘蛛,然后构造 url 并从那里爬取,例如:process.crawl(spider, input='inputargument', first='James', last='Bond')
    【解决方案2】:

    当你调用process.start() 时,Scrapy 的CrawlerProcess 将启动一个 Twisted reactor,默认情况下它将在爬虫完成时停止并且不应该重新启动。您可以尝试的一种可能的解决方案是将stop_after_crawl param 设置为False

     process.start(stop_after_crawl=False)
    

    这将防止反应堆停止,绕过重启问题。虽然我不能说它不会进一步导致其他问题,所以你应该测试它以确保。

    documentation 中还有一个在同一进程中运行多个蜘蛛的示例,其中一个主动运行/停止反应器,但它使用CrawlerRunner 而不是CrawlerProcess

    最后,如果上述解决方案没有帮助,我建议尝试以下方法:

    if __name__ == '__main__':
    
        process = CrawlerProcess(settings={
            "FEEDS": {
                "itemtmall.csv": {"format": "csv",
                                  'fields': ['product_name_tmall', 'product_price_tmall', 'product_discount_tmall'], },
                "itemjd.csv": {"format": "csv",
                               'fields': ['product_name_jd', 'product_price_jd', 'product_discount_jd'], },
        })
        for a in range(len(inputlist)):
            process.crawl(tmallSpider)
            process.crawl(jdSpider)
        process.start()
    

    这里的重点是进程只在循环外启动一次,CrawlerProcess实例化也在循环外,否则每次迭代都会覆盖之前的CrawlerProcess实例.

    【讨论】:

      【解决方案3】:

      使用应该很容易的类, 使用scrapy.Request时有一些注意事项

      callback是处理响应的方法

      dont_filter=True 允许您多次请求相同的页面

      errback 是一种处理错误响应的方法

      您可以随时生成Request,它将被添加到池中

      import scrapy
      
      class GSMArenaSpider(scrapy.Spider):
          name = "smartmania"
          main_url = ['https://smartmania.cz/zarizeni/telefony/']  # you can put as many starting links as you want
          
      
          def start_requests(self):        
              for url in GSMArenaSpider.main_url:
                  self.my_logger.debug(f"Starting Scrapy @ {url}")
                  yield scrapy.Request(url=url, callback=self.parse_pages, errback=self.errback_httpbin)  # You can bind any parsing method you need
                  yield scrapy.Request(url=url, callback=self.parse_ipads, errback=self.errback_httpbin)  # You can bind any parsing method you need
                  yield scrapy.Request(url=url, callback=self.parse_iphones, errback=self.errback_httpbin)  # You can bind any parsing method you need
      
          def parse_pages(self, response):
              # parsing results
              #
              for url in result:
                  self.my_logger.info(f"Found pages: {url}")
                  yield scrapy.Request(url=url, callback=self.parse_phone_links, errback=self.errback_httpbin,
                                       dont_filter=True)
      
                  yield scrapy.Request(url=url, callback=self.parse_pages, errback=self.errback_httpbin, dont_filter=False)  # Be careful when doing recursion requests and not using filter
      
          def errback_httpbin(self, failure):
              """ Handling Errors """
              url = failure.request.url
              callback = failure.request.callback
              errback = failure.request.errback  # should work same way as callback... ?
              status = failure.value.response.status
              self.my_logger.error(f"Fail status: {status} @: {url}")
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-03-29
        • 1970-01-01
        • 2016-06-17
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多