如何实现Flask API向Scrapy爬虫传参并解决Reactor报错
解决方案
接口返回空数组、关键词参数未传递问题
核心原因是爬虫未正确接收外部传入的自定义参数,且启动爬虫时未将Flask端收到的关键词透传给爬虫实例。
- 爬虫侧修改:在自定义Spider类中重写构造方法接收
keyword参数,替换硬编码的起始URL,动态生成搜索请求:
class AskSpider(scrapy.Spider): name = "ask_spider" def __init__(self, keyword=None, *args, **kwargs): super().__init__(*args, **kwargs) self.keyword = keyword if not self.keyword: raise ValueError("搜索关键词不能为空") def start_requests(self): # 替换为实际的Ask搜索页URL规则 target_url = f"https://www.ask.com/web?q={self.keyword}" yield scrapy.Request(target_url, callback=self.parse)
- 不要在爬虫类内写死
start_urls,所有依赖请求参数的逻辑都通过实例属性动态生成。
ReactorAlreadyInstalledError 报错问题
核心原因是CrawlerProcess会自动安装Twisted反应器,而Flask服务启动(尤其是debug模式重载、WSGI服务器初始化)时已经提前加载了反应器,重复初始化就会触发该报错。不要在Flask进程内使用CrawlerProcess,换回CrawlerRunner并做单例反应器初始化即可:
- 全局只初始化一次爬虫运行器和Twisted反应器,放在Flask实例创建逻辑之前,避免重复加载
- 将反应器放在独立守护线程中运行,避免阻塞Flask的请求处理
- 每次接口请求时,将收到的
keyword作为启动参数传入爬虫,同时绑定临时回调收集爬取结果
参考Flask端实现代码:
from flask import Flask, request, jsonify from twisted.internet import reactor from scrapy.crawler import CrawlerRunner from scrapy.utils.project import get_project_settings from spiders.ask_spider import AskSpider import threading # 全局单例初始化爬虫运行器,只加载一次Scrapy项目配置 crawl_runner = CrawlerRunner(get_project_settings()) app = Flask(__name__) # 独立线程启动反应器,不阻塞Flask服务 def reactor_worker(): reactor.run(installSignalHandlers=False) # 设为守护线程,随主进程退出自动结束 threading.Thread(target=reactor_worker, daemon=True).start() @app.route("/scrape", methods=["GET"]) def scrape_endpoint(): keyword = request.args.get("keyword") if not keyword: return jsonify({"error": "missing keyword param"}), 400 result_list = [] # 回调收集爬取到的item def collect_item(item, response, spider): result_list.append(dict(item)) # 传入keyword参数启动爬虫,绑定item收集回调 task = crawl_runner.crawl(AskSpider, keyword=keyword, item_scraped=collect_item) # 爬取完成后返回结果 task.addCallback(lambda _: jsonify(result_list)) return task if __name__ == "__main__": # 开发环境关闭Flask自带的reloader,避免重复启动反应器线程 app.run(debug=True, use_reloader=False)
- 生产环境部署时不要用Flask自带的开发服务器,用多进程模式的WSGI服务器时,需要确保每个工作进程只初始化一次反应器,不要在请求生命周期内重复创建Crawler实例。
内容的提问来源于stack exchange,提问作者keni
相关产品推荐
相关产品推荐

