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

在DRF(Django Rest Framework)视图中调用Scrapy爬虫的最佳方法

DRF 调用 Scrapy 爬虫最优实现方案

方案选型结论

你现有的第一种基于 Scrapyd 的实现方向是正确的,第二种通过 os.system 直接启动爬虫脚本的方案不建议在生产环境使用,存在严重的稳定性、并发问题。

现有实现的问题梳理

废弃 spiding 实现的原因

  • os.system 是同步阻塞调用,接口请求会一直等待爬虫执行完成才返回,并发量稍高就会占满 Django 所有 worker 进程,直接导致服务不可用
  • 通过系统环境变量传递爬取 URL 的方式存在并发冲突,多个请求同时发起时,后设置的 URL 会覆盖之前的参数,导致爬取目标完全错乱
  • 没有任务状态查询、异常处理机制,调用方完全无法感知爬虫执行结果

现有 Scrapyd 实现的可优化点

  • 目前用的是 Django 原生 FBV 写法,改成 DRF 标准的 CBV/序列化器校验逻辑,更符合项目规范,也能减少手动校验参数的冗余代码
  • 爬取结果查询逻辑可以加缓存,不用每次都查库,降低数据库压力
  • 缺少权限控制、接口限流逻辑,生产环境容易被恶意调用

最优实现核心逻辑

业内通用的方案就是基于 Scrapyd 做爬虫任务调度,核心优势如下:

  • 完全异步非阻塞,调用接口后直接返回任务ID,不会阻塞 Django 请求
  • 原生支持多爬虫并发调度、任务队列管理、爬虫版本管理,生产环境可以水平扩容爬虫节点
  • 自带任务状态查询、日志查询能力,不需要自己实现任务管理逻辑

DRF 适配后的参考代码

from uuid import uuid4
from urllib.parse import urlparse
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
from rest_framework.permissions import IsAuthenticated
from scrapyd_api import ScrapydAPI
from .serializers import CrawlRequestSerializer, CrawlStatusQuerySerializer
from scrapy_app.models import QuoteItem

scrapyd = ScrapydAPI('http://localhost:6800')

class CrawlView(APIView):
    # 生产环境可打开权限控制
    # permission_classes = [IsAuthenticated]
    
    def post(self, request):
        serializer = CrawlRequestSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        url = serializer.validated_data['url']
        
        domain = urlparse(url).netloc
        unique_id = str(uuid4())
        settings = {
            'unique_id': unique_id,
            'USER_AGENT': 'Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)'
        }
        
        task_id = scrapyd.schedule('default', 'icrawler', settings=settings, url=url, domain=domain)
        return Response({
            'task_id': task_id,
            'unique_id': unique_id,
            'status': 'started'
        }, status=status.HTTP_201_CREATED)
    
    def get(self, request):
        serializer = CrawlStatusQuerySerializer(data=request.query_params)
        serializer.is_valid(raise_exception=True)
        task_id = serializer.validated_data['task_id']
        unique_id = serializer.validated_data['unique_id']
        
        task_status = scrapyd.job_status('default', task_id)
        if task_status == 'finished':
            try:
                item = QuoteItem.objects.get(unique_id=unique_id)
                return Response({'data': item.to_dict()})
            except QuoteItem.DoesNotExist:
                return Response({'error': '爬取结果不存在'}, status=status.HTTP_404_NOT_FOUND)
        return Response({'status': task_status})

对应的序列化器代码:

from rest_framework import serializers

class CrawlRequestSerializer(serializers.Serializer):
    url = serializers.URLField(required=True, help_text="爬取目标URL")

class CrawlStatusQuerySerializer(serializers.Serializer):
    task_id = serializers.CharField(required=True, help_text="爬虫任务ID")
    unique_id = serializers.CharField(required=True, help_text="结果关联唯一标识")

部署注意事项

  • 提前部署启动 Scrapyd 服务,将你的 Scrapy 项目打包部署到 Scrapyd 节点
  • Scrapy 爬虫要做参数适配,支持接收传入的 url 作为爬取入口,unique_id 作为结果关联字段存入数据库
  • 生产环境建议给 Scrapyd 加身份认证,接口增加限流规则,避免被滥用

内容的提问来源于stack exchange,提问作者Sadegh-khan

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.24 04:15:02