You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何实现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并做单例反应器初始化即可:

  1. 全局只初始化一次爬虫运行器和Twisted反应器,放在Flask实例创建逻辑之前,避免重复加载
  2. 将反应器放在独立守护线程中运行,避免阻塞Flask的请求处理
  3. 每次接口请求时,将收到的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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.30 06:18:17