在线程中启动scrapy以及多次启动scrapy报错的解决方案

最近项目遇到一个问题,需要设置一个监听线程,来监听消息队列,当收到消息的时候,需要启动相应的爬虫代码,执行抓取操作。

由于监听部分采用的是线程的方式,所以考虑在basic_consume的回调函数中调用scrapy的api启动爬虫,这个时候报错了。

ERROR:root:signal only works in main thread

错误信息提示说只能在主线程运行,这是由于我是在监听线程(主线程)中调用scrapy,是直接使用CrawlerProcess启动的,发生报错是因为CrawlerProcess自带reactor的启动关闭过程,而这个过程不能在子线程中运行。(可能的原因是:twisted.internet.reactor 中使用了python的signal,signal handler只能在Main Thread中运行)

查网上的资料有的说可以使用CrawlRunner来启动爬虫,而不是用CrawlerProcess,我按照官方文档给出的启动方式放在子线程中启动的时候,依然会有这个错误,我依然感觉可能的原因和上面的一样。

官方文档给出的CrawlRunner的启动方法:

from twisted.internet import reactor
import scrapy
from scrapy.crawler import CrawlerRunner
from scrapy.utils.log import configure_logging

class MySpider(scrapy.Spider):
    # Your spider definition
    ...

configure_logging({'LOG_FORMAT': '%(levelname)s: %(message)s'})
runner = CrawlerRunner()

d = runner.crawl(MySpider)
d.addBoth(lambda _: reactor.stop())
reactor.run() # the script will block here until the crawling is finished
See also

但是相对于使用CrawlProcess启动的时候不能启动,使用CrawlRunner启动虽然会有报错,但是爬虫代码在运行了,有了重大的进步!!!(如果有人知道原因,麻烦您讲解一下,谢谢了)

总结上面的报错原因,就是说不能在子线程中运行,只能在主线程中运行,向下的不会改,那就只能向上看看怎么改来解决这个问题了。

我在这里采用的是采用线程+进程的方式,监听线程是主线程,当需要启动爬虫的时候,调用爬虫开启函数startCrawler,在startCrawler中采用多进程的方式来启动爬虫程序,这样就运行下去了,示例代码如下:

import scrapy
from multiprocessing import Process
from scrapy.crawler import CrawlerProcess
from threading import Thread
from scrapy.utils.project import get_project_settings

class MySpider(scrapy.Spider):
    # Your spider definition
    pass

def startCrawler():
    # pre_process code
    run_spider()
    # after_process code
    
def run_spider():
    def f():
        try:    
            process = CrawlerProcess(get_project_settings())
            process.crawl(MySpider)
            process.start()
        except Exception as e:
            print(e)
 
    p = Process(target=f)
    p.start()
    p.join()

class testThread(Thread):
    def __init__(self):
        Thread.__init__(self)

    def run(self):
        startCrawler()
 

if __name__ == "__main__":
    t = testThread()
    t.start()

这样不仅解决了在线程中启动scrapy失败的问题,也解决了重复启动scrapy时抛出异常“ReactorNotRestartable“的问题。

另外,祝大家六一儿童节快乐

你可能感兴趣的:(python爬虫)