如何使用不同的输入多次运行蜘蛛

Posted

技术标签:

【中文标题】如何使用不同的输入多次运行蜘蛛【英文标题】: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 文件或其他文件。任何建议将不胜感激!

【问题讨论】:

【参考方案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")

【讨论】:

以上是关于如何使用不同的输入多次运行蜘蛛的主要内容,如果未能解决你的问题,请参考以下文章

如何在单个 Scrapy 项目中为不同的蜘蛛使用不同的管道

如何使用不同的方法多次模拟调用 AWS 服务的 Golang 函数的单元测试?

使用多个蜘蛛无头运行 Selenium

如何使用 UNIX shell 脚本并行运行一个进程多次?

Scrapy运行2个蜘蛛,使用一个进程将输出到2个不同的文件(AWS Lambda)

通过不同目录多次运行python脚本的最佳方法?使用视窗