如何使用不同的输入多次运行蜘蛛
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")
【讨论】:
以上是关于如何使用不同的输入多次运行蜘蛛的主要内容,如果未能解决你的问题,请参考以下文章
如何使用不同的方法多次模拟调用 AWS 服务的 Golang 函数的单元测试?