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

FastAPI集成Scrapy后保存脚本致Scrapy关闭的解决方法咨询

问题描述

我用FastAPI和Scrapy写了个集成脚本,POST和GET请求都能正常运行,但本地开发时,修改脚本保存后,Scrapy会自动关闭,说明本地运行时Scrapy没有正常完成关闭流程。

示例代码

路由脚本

from fastapi import APIRouter
from scrapy.crawler import CrawlerProcess
from collections import defaultdict
from spiders.xp import postItem
import sys

rout = APIRouter()

# 全局变量
globalDict = defaultdict(list)

@rout.post("/event")
async def add_todo(tealium: dict = None):
    if tealium is not None:
        # 从POST请求中获取数据并全局存储
        print(tealium)
        
        process = CrawlerProcess(settings={
                "FEEDS": {
                    "post.json": {"format": "json"},
                },
            })
        process.crawl(postItem, tealium=tealium)
        
        process.start(stop_after_crawl=True)

        if "twisted.internet.reactor" in sys.modules:
            del sys.modules["twisted.internet.reactor"]
      
        with open('post.json', 'r') as f:
            value = f.read()
        globalDict['value'].append([value])
        print(globalDict)

        return {'message': 'Post request completed'}

    else:
        print(tealium)
        globalDict['result'] = ['No Post Request Sent']
        return globalDict

@rout.get("/event")
async def retrieve_todos() -> dict:
    return { "todos": globalDict}

应用脚本

from fastapi import FastAPI
from router.router1 import rout

app = FastAPI()

@app.get("/")
async def welcome() -> dict:
    return { "message": 'Hello World!' }

app.include_router(rout)

解决方法

1. 替换CrawlerProcess为CrawlerRunner

你当前每次请求都删除twisted.internet.reactor模块,会导致热重载时Reactor状态混乱。改用CrawlerRunner可以避免自动启动/停止Reactor,更好兼容FastAPI的异步环境:

修改路由脚本的爬虫启动逻辑:

from scrapy.crawler import CrawlerRunner
from twisted.internet import reactor
from twisted.internet.defer import Deferred
from asyncio import Future

# 工具函数:将Twisted Deferred转换为FastAPI支持的异步对象
def twisted_deferred_to_async(deferred: Deferred) -> Future:
    future = Future()
    def callback(result):
        if not future.done():
            future.set_result(result)
    def errback(failure):
        if not future.done():
            future.set_exception(failure.value)
    deferred.addCallback(callback)
    deferred.addErrback(errback)
    return future

# 全局初始化Runner,避免重复创建
runner = CrawlerRunner(settings={
        "FEEDS": {
            "post.json": {"format": "json"},
        },
    })

@rout.post("/event")
async def add_todo(tealium: dict = None):
    if tealium is not None:
        print(tealium)
        
        deferred = runner.crawl(postItem, tealium=tealium)
        # 异步等待爬虫完成,不阻塞FastAPI事件循环
        await twisted_deferred_to_async(deferred)
      
        with open('post.json', 'r') as f:
            value = f.read()
        globalDict['value'].append([value])
        print(globalDict)

        return {'message': 'Post request completed'}

2. 配置热重载排除规则

如果用uvicorn启动服务,热重载会重新加载所有模块,导致Twisted Reactor重复初始化。启动时添加排除参数,避免Scrapy相关模块被热重载:

uvicorn main:app --reload --reload-exclude "spiders/*" --reload-exclude "scrapy/*"

3. 避免重复操作Reactor模块

删除你代码中每次请求后删除twisted.internet.reactor的逻辑,这个操作会破坏Twisted的全局状态,导致热重载时爬虫无法正常重启。

内容的提问来源于stack exchange,提问作者Dollar Tune-bill

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.15 14:30:42