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

如何在Django视图及外部脚本中运行Scrapy爬虫并解决同步问题?

解决方案

问题根源

你的代码里调用process.start()会直接启动Twisted的事件循环并阻塞当前线程,而Django同步视图需要立即返回响应,两者冲突导致视图无法正常工作。同时要兼顾爬虫可独立运行、不依赖DjangoItem管道的需求,可按以下步骤处理:


一、修改Django视图,实现同步调用爬虫并返回结果

核心思路是用线程启动爬虫事件循环,通过Scrapy信号收集结果,避免阻塞视图线程:

# Core imports
import threading
from queue import Queue
from scrapy.crawler import CrawlerRunner
from scrapy.utils.project import get_project_settings
from scrapy import signals
from scrapy.signalmanager import dispatcher
from twisted.internet import reactor

# Third-party imports
from rest_framework.views import APIView
from rest_framework.response import Response

# Local imports
from scrapy_project.spiders.google import GoogleSpider


class ForFunAPIView(APIView):
    def get(self, *args, **kwargs):
        # 线程安全的结果容器和完成信号
        result_queue = Queue()
        crawl_finished = threading.Event()

        # 爬虫关闭时触发,将结果存入队列并标记完成
        def on_spider_closed(spider, reason):
            result_queue.put(spider.results)
            crawl_finished.set()

        # 注册信号
        dispatcher.connect(on_spider_closed, signal=signals.spider_closed)

        # 初始化爬虫运行器
        runner = CrawlerRunner(get_project_settings())
        runner.crawl(GoogleSpider)

        # 用单独线程启动Twisted事件循环,避免阻塞视图
        def run_reactor():
            reactor.run(installSignalHandlers=False)

        crawl_thread = threading.Thread(target=run_reactor)
        crawl_thread.start()

        # 等待爬虫完成
        crawl_finished.wait()
        # 停止事件循环
        if reactor.running:
            reactor.stop()

        # 获取爬取结果
        results = result_queue.get()
        # 注销信号,避免重复注册
        dispatcher.disconnect(on_spider_closed, signal=signals.spider_closed)

        return Response({"results": results})

二、调整爬虫代码,内置结果存储

修改GoogleSpider,让它自己存储爬取结果,方便后续获取:

import scrapy

class GoogleSpider(scrapy.Spider):
    name = "google"
    start_urls = ["https://www.google.com"]

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.results = []  # 用于存储爬取结果

    def parse(self, response):
        # 示例爬取逻辑,根据你的需求修改
        item = {
            "page_title": response.css("title::text").get(),
            "url": response.url
        }
        self.results.append(item)
        yield item

三、实现爬虫独立运行(无需Django环境)

创建单独的运行脚本run_spider.py,动态移除Django相关管道,确保可直接执行:

from scrapy.crawler import CrawlerProcess
from scrapy.utils.project import get_project_settings
from scrapy_project.spiders.google import GoogleSpider

def run_standalone_spider():
    # 获取项目配置
    settings = get_project_settings()
    # 移除DjangoItemPipeline(如果存在)
    django_pipeline_key = "scrapy_djangoitem.pipelines.DjangoItemPipeline"
    if django_pipeline_key in settings.get("ITEM_PIPELINES", {}):
        del settings["ITEM_PIPELINES"][django_pipeline_key]
    # 可选:清空所有管道,或仅保留你需要的自定义管道
    # settings["ITEM_PIPELINES"] = {}

    # 启动爬虫
    process = CrawlerProcess(settings)
    process.crawl(GoogleSpider)
    process.start()

if __name__ == "__main__":
    run_standalone_spider()

直接执行该脚本即可独立运行爬虫,无需依赖Django环境。


关键注意事项

  • 信号注册/注销:每次请求后必须注销信号,避免重复注册导致结果混乱。
  • Reactor管理:确保爬虫完成后停止Twisted事件循环,防止线程残留。
  • 线程安全:使用Queue和Event保证多请求场景下的结果正确性。

内容的提问来源于stack exchange,提问作者Mohsn

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.06 09:50:32