如何在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
相关产品推荐
相关产品推荐

