如何在Django后端添加可运行Python脚本的API端点
实现方案
直接按下面的步骤开发即可,不需要依赖django-extensions的runscript功能,就是常规的Django API开发逻辑:
1. 整理现有脚本与公共配置
先在需要新增接口的Django app下新建services目录,把你写的爬取函数存为reddit_tracker.py放入该目录。
PRAW的连接实例不要每次接口请求都新建,把凭证配置统一放到settings.py,生产环境从环境变量读取,禁止硬编码提交到代码仓库:
# settings.py 追加配置 import os PRAW_CLIENT_ID = os.getenv("PRAW_CLIENT_ID") PRAW_CLIENT_SECRET = os.getenv("PRAW_CLIENT_SECRET") PRAW_USER_AGENT = os.getenv("PRAW_USER_AGENT", "django-reddit-tracker/1.0")
2. 编写接口视图
前后端分离场景推荐直接用DRF(Django REST Framework)写接口,核心逻辑就是:接收前端传入的自定义参数,调用你已写好的爬取函数,把结果格式化后返回。
在app的views.py中添加如下代码:
from rest_framework.views import APIView from rest_framework.response import Response from rest_framework import status import praw from django.conf import settings from .services.reddit_tracker import track_mentions_in_past_24_hours # 全局初始化一次PRAW实例,复用连接减少重复建连开销 reddit = praw.Reddit( client_id=settings.PRAW_CLIENT_ID, client_secret=settings.PRAW_CLIENT_SECRET, user_agent=settings.PRAW_USER_AGENT ) # 替换为你自己的初始活跃股票字典 BASE_ACTIVE_STOCKS = {} class RedditMentionAPIView(APIView): def get(self, request): # 从请求参数读取前端传值,未传则使用默认值 subname = request.query_params.get("subname", "wallstreetbets") time_filter = request.query_params.get("time_filter", "day") limit = request.query_params.get("limit", None) # 基础参数校验 if limit is not None: try: limit = int(limit) except ValueError: return Response({"error": "limit参数必须传入整数"}, status=status.HTTP_400_BAD_REQUEST) if time_filter not in ["hour", "day", "week", "month", "year", "all"]: return Response({"error": "time_filter参数不合法"}, status=status.HTTP_400_BAD_REQUEST) try: # *注意:copy一份基础字典传入,避免爬取过程修改全局变量,导致不同请求之间数据污染* mention_result = track_mentions_in_past_24_hours( reddit=reddit, subname=subname, active_stocks=BASE_ACTIVE_STOCKS.copy(), time_filter=time_filter, limit=limit ) # 转换为你需要的(条目, 提及次数)元组列表格式 format_result = list(mention_result.items()) return Response({ "code": 0, "data": format_result, "params": { "subname": subname, "time_filter": time_filter, "limit": limit } }) except Exception as e: return Response({"error": f"数据获取失败: {str(e)}"}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
如果项目没有安装DRF,用Django原生JsonResponse编写逻辑完全一致,参数从request.GET中读取即可。
3. 绑定路由
在app的urls.py中给新增的视图绑定路由地址:
from django.urls import path from .views import RedditMentionAPIView urlpatterns = [ # 保留你原有的其他路由 path("api/reddit/mentions/", RedditMentionAPIView.as_view(), name="reddit-mention-api"), ]
如果当前app的路由没有被项目根urls.py引入,记得在根路由文件中用include把app的路由规则添加进去。
4. Heroku部署适配
- 把
praw、djangorestframework(如果用了DRF)加到项目根目录的requirements.txt中,Heroku构建时会自动安装对应依赖。 - 进入Heroku后台的项目Config Vars配置页,把
PRAW_CLIENT_ID、PRAW_CLIENT_SECRET两个凭证值填入环境变量,不要硬编码在代码中。 - 注意:Heroku普通web请求的默认超时时间是30秒,如果你的爬取逻辑耗时可能超过30秒,建议给接口加缓存:相同参数的请求结果缓存5-10分钟,不用每次都实时爬,既能提速也能避免触发Reddit的接口频率限制。如果确实需要长时间爬取,就把逻辑改成Celery异步任务,前端先提交任务,再轮询拿结果即可。
5. 前端调用方式
前端直接发GET请求到接口地址,自定义参数拼在URL query中即可,比如要爬取r/stocks板块过去一周的提及数据,请求地址就是https://你的heroku应用域名/api/reddit/mentions/?subname=stocks&time_filter=week,返回的data字段就是需要的(条目, 提及次数)元组列表。
内容的提问来源于stack exchange,提问作者chiralcentre
相关产品推荐
相关产品推荐

