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

本地正常的Flask+Pytube程序部署Heroku时触发内部服务器错误

问题:Flask+Pytube程序本地正常,部署Heroku后出现Internal Server Error

我开发了一个基于Flask和Pytube的程序,用于获取YouTube视频流信息,代码如下:

from pytube import YouTube
from flask import request
from flask import Flask
import jsonpickle

app = Flask(__name__)
@app.route('/login', methods=['GET', 'POST'])
def login():
    if request.method == 'POST':
           url = request.form["d_url"]
           yt_video = YouTube(url)
           videos = yt_video.streams
           res_list = list(videos)
           return jsonpickle.encode(res_list)
    else:
        return request.args.get('d_url')
if __name__ == "__main__":
    app.run(debug =True)

该程序在本地运行正常,但部署到Heroku后出现Internal Server Error,日志报错信息如下:

Traceback (most recent call last):
2022-08-07T07:48:45.427991+00:00 app[web.1]:   File "/app/.heroku/python/lib/python3.10/site-packages/flask/app.py", line 2525, in wsgi_app
2022-08-07T07:48:45.427991+00:00 app[web.1]:     response = self.full_dispatch_request()
2022-08-07T07:48:45.427992+00:00 app[web.1]:   File "/app/.heroku/python/lib/python3.10/site-packages/flask/app.py", line 1822, in full_dispatch_request
2022-08-07T07:48:45.427992+00:00 app[web.1]:     rv = self.handle_user_exception(e)
2022-08-07T07:48:45.427993+00:00 app[web.1]:   File "/app/.heroku/python/lib/python3.10/site-packages/flask/app.py", line 1820, in full_dispatch_request
2022-08-07T07:48:45.427993+00:00 app[web.1]:     rv = self.dispatch_request()
2022-08-07T07:48:45.427993+00:00 app[web.1]:   File "/app/.heroku/python/lib/python3.10/site-packages/flask/app.py", line 1796, in dispatch_request
2022-08-07T07:48:45.427994+00:00 app[web.1]:     return self.ensure_sync(self.view_functions[rule.endpoint])(**view_args)
2022-08-07T07:48:45.427994+00:00 app[web.1]:   File "/app/app.py", line 11, in login
2022-08-07T07:48:45.427994+00:00 app[web.1]:     yt_video = YouTube(str(request.form["d_url"]))
2022-08-07T07:48:45.427995+00:00 app[web.1]:   File "/app/.heroku/python/lib/python3.10/site-packages/pytube/__main__.py", line 91, in __init__
2022-08-07T07:48:45.427995+00:00 app[web.1]:     self.prefetch()
2022-08-07T07:48:45.427996+00:00 app[web.1]:   File "/app/.heroku/python/lib/python3.10/site-packages/pytube/__main__.py", line 181, in prefetch
2022-08-07T07:48:45.427996+00:00 app[web.1]:     self.vid_info_raw = request.get(self.vid_info_url)
2022-08-07T07:48:45.427996+00:00 app[web.1]:   File "/app/.heroku/python/lib/python3.10/site-packages/pytube/request.py", line 36, in get
2022-08-07T07:48:45.427997+00:00 app[web.1]:     return _execute_request(url).read().decode("utf-8")
2022-08-07T07:48:45.427997+00:00 app[web.1]:   File "/app/.heroku/python/lib/python3.10/site-packages/pytube/request.py", line 24, in _execute_request
2022-08-07T07:48:45.427998+00:00 app[web.1]:     return urlopen(request)  # nosec
2022-08-07T07:48:45.427998+00:00 app[web.1]:   File "/app/.heroku/python/lib/python3.10/urllib/request.py", line 216, in urlopen
2022-08-07T07:48:45.427998+00:00 app[web.1]:     return opener.open(url, data, timeout)
2022-08-07T07:48:45.427998+00:00 app[web.1]:   File "/app/.heroku/python/lib/python3.10/urllib/request.py", line 525, in open
2022-08-07T07:48:45.427998+00:00 app[web.1]:     response = meth(req, response)
2022-08-07T07:48:45.427999+00:00 app[web.1]:   File "/app/.heroku/python/lib/python3.10/urllib/request.py", line 634, in http_response
2022-08-07T07:48:45.427999+00:00 app[web.1]:     response = self.parent.error(
2022-08-07T07:48:45.427999+00:00 app[web.1]:   File "/app/.heroku/python/lib/python3.10/urllib/request.py", line 557, in error
2022-08-07T07:48:45.428000+00:00 app[web.1]:     result = self._call_chain(*args)
2022-08-07T07:48:45.428000+00:00 app[web.1]:   File "/app/.heroku/python/lib/python3.10/urllib/request.py", line 496, in _call_chain
2022-08-07T07:48:45.428000+00:00 app[web.1]:     result = func(*args)
2022-08-07T07:48:45.428000+00:00 app[web.1]:   File "/app/.heroku/python/lib/python3.10/urllib/request.py", line 749, in http_error_302
2022-08-07T07:48:45.428001+00:00 app[web.1]:     return self.parent.open(new, timeout=req.timeout)
2022-08-07T07:48:45.428001+00:00 app[web.1]:   File "/app/.heroku/python/lib/python3.10/urllib/request.py", line 525, in open
2022-08-07T07:48:45.428001+00:00 app[web.1]:     response = meth(req, response)
2022-08-07T07:48:45.428002+00:00 app[web.1]:   File "/app/.heroku/python/lib/python3.10/urllib/request.py", line 634, in http_response
2022-08-07T07:48:45.428002+00:00 app[web.1]:     response = self.parent.error(
2022-08-07T07:48:45.428002+00:00 app[web.1]:   File "/app/.heroku/python/lib/python3.10/urllib/request.py", line 563, in error
2022-08-07T07:48:45.428002+00:00 app[web.1]:     return self._call_chain(*args)
2022-08-07T07:48:45.428003+00:00 app[web.1]:   File "/app/.heroku/python/lib/python3.10/urllib/request.py", line 496, in _call_chain
2022-08-07T07:48:45.428003+00:00 app[web.1]:     result = func(*args)
2022-08-07T07:48:45.428003+00:00 app[web.1]:   File "/app/.heroku/python/lib/python3.10/urllib/request.py", line 643, in http_error_default
2022-08-07T07:48:45.428003+00:00 app[web.1]:     raise HTTPError(req.full_url, code, msg, hdrs, fp)

原因分析

从报错栈可以定位到,问题出在Pytube请求YouTube视频信息时触发了HTTP错误,核心原因包括:

  • Heroku共享IP被YouTube拦截:YouTube的反爬机制会限制频繁请求的IP,Heroku的共享IP池大概率已被标记,导致请求被拒绝。
  • Pytube版本兼容性问题:旧版本Pytube可能无法适配YouTube的最新API逻辑,本地环境因缓存或网络差异未暴露问题,但在Heroku服务器上触发错误。
  • 请求头未模拟真实浏览器:Pytube默认请求头缺少浏览器标识,被YouTube反爬机制识别为非人类请求。

解决方案

方案1:更换稳定版Pytube

官方Pytube更新不及时时,可使用社区维护的稳定版本,修改requirements.txt:

Flask==2.2.2
jsonpickle==2.2.0
pytube==12.1.0

方案2:添加自定义请求头模拟浏览器

修改代码,为Pytube请求添加浏览器UA,绕过反爬检测:

from pytube import YouTube
from flask import request
from flask import Flask
import jsonpickle

app = Flask(__name__)

@app.route('/login', methods=['GET', 'POST'])
def login():
    if request.method == 'POST':
        url = request.form["d_url"]
        # 配置浏览器请求头
        yt_video = YouTube(
            url,
            use_oauth=False,
            allow_oauth_cache=False,
            headers={
                'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.0.0 Safari/537.36'
            }
        )
        videos = yt_video.streams
        res_list = list(videos)
        return jsonpickle.encode(res_list)
    else:
        return request.args.get('d_url')

if __name__ == "__main__":
    app.run(debug=True)

方案3:禁用Pytube预取功能

预取机制会触发额外请求,可能导致被拦截,可尝试关闭:

yt_video = YouTube(url)
yt_video.prefetch = lambda: None  # 禁用预取逻辑
videos = yt_video.streams

方案4:配置代理请求

如果Heroku IP被彻底屏蔽,可添加代理服务,修改代码并在Heroku设置代理环境变量:

import os
from pytube import YouTube
from flask import request
from flask import Flask
import jsonpickle
import requests

# 替换Pytube默认请求方法为代理请求
proxy_url = os.getenv('PROXY_URL')
if proxy_url:
    def proxied_get(url):
        proxies = {'http': proxy_url, 'https': proxy_url}
        return requests.get(url, proxies=proxies).text
    from pytube import request
    request.get = proxied_get

app = Flask(__name__)

@app.route('/login', methods=['GET', 'POST'])
def login():
    if request.method == 'POST':
        url = request.form["d_url"]
        yt_video = YouTube(url)
        videos = yt_video.streams
        res_list = list(videos)
        return jsonpickle.encode(res_list)
    else:
        return request.args.get('d_url')

if __name__ == "__main__":
    app.run(debug=True)

在Heroku的环境变量中设置PROXY_URL为可用的代理地址即可。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.23 14:33:19