使用PyScript开发YouTube下载器时遭遇URLError问题求助
PyScript 结合 Pytube 出现 URLError 的解决方法
问题重现
使用PyScript开发YouTube下载器时,运行代码触发URLError:
<py-env> - pytube </py-env> <py-script> from pytube import YouTube url="https://www.youtube.com/watch?v=7BXJIjfJCsA&t=111s" my_video=YouTube(url) print(my_video.title) </py-script>
报错信息:
JsException(PythonError: Traceback (most recent call last): File "/lib/python3.10/site-packages/_pyodide/_base.py", line 429, in eval_code .run(globals, locals) File "/lib/python3.10/site-packages/_pyodide/_base.py", line 300, in run coroutine = eval(self.code, globals, locals) File "", line 4, in File "/lib/python3.10/site-packages/pytube/__main__.py", line 341, in title self._title = self.vid_info['videoDetails']['title'] File "/lib/python3.10/site-packages/pytube/__main__.py", line 246, in vid_info innertube_response = innertube.player(self.video_id) File "/lib/python3.10/site-packages/pytube/innertube.py", line 300, in player return self._call_api(endpoint, query, self.base_data) File "/lib/python3.10/site-packages/pytube/innertube.py", line 242, in _call_api response = request._execute_request( File "/lib/python3.10/site-packages/pytube/request.py", line 37, in _execute_request return urlopen(request, timeout=timeout) # nosec File "/lib/python3.10/urllib/request.py", line 216, in urlopen return opener.open(url, data, timeout) File "/lib/python3.10/urllib/request.py", line 519, in open response = self._open(req, data) File "/lib/python3.10/urllib/request.py", line 541, in _open return self._call_chain(self.handle_open, 'unknown', File "/lib/python3.10/urllib/request.py", line 496, in _call_chain result = func(*args) File "/lib/python3.10/urllib/request.py", line 1419, in unknown_open raise URLError('unknown url type: %s' % type) urllib.error.URLError:
问题原因
- 浏览器跨域限制:PyScript运行在浏览器环境中,受同源策略约束,pytube自带的
urllib直接请求YouTube接口会被浏览器拦截。 - Pyodide环境兼容性:Pyodide(PyScript的底层运行环境)的
urllib实现与本地Python存在差异,无法正确处理pytube生成的部分请求URL。
解决方法
方法一:替换pytube的请求逻辑为浏览器Fetch API
通过覆盖pytube的请求方法,使用浏览器原生的Fetch API替代urllib,避开跨域和环境兼容问题:
<py-env> - pytube==15.0.0 </py-env> <py-script> from pytube import YouTube from js import fetch import urllib.request import pytube.request async def custom_execute_request(url, method='GET', headers=None, data=None, timeout=10): headers = headers or {} req_options = { 'method': method, 'headers': headers } if data: req_options['body'] = data response = await fetch(url, **req_options) if not response.ok: raise urllib.error.URLError(f"HTTP error: {response.status} {response.statusText}") return await response.text() # 替换pytube的请求方法 pytube.request._execute_request = custom_execute_request # 简化URL,去掉多余参数测试 url = "https://www.youtube.com/watch?v=7BXJIjfJCsA" # 异步执行需要用async/await async def get_video_info(): my_video = YouTube(url) print(my_video.title) await get_video_info() </py-script>
方法二:使用后端代理转发请求
如果上述方法仍有问题,可搭建一个简单的后端服务(如Flask),由后端调用pytube处理请求,PyScript通过调用后端接口获取数据:
- 后端示例(Flask):
from flask import Flask, request, jsonify from pytube import YouTube from flask_cors import CORS app = Flask(__name__) CORS(app) @app.route('/get-video-title', methods=['GET']) def get_video_title(): video_id = request.args.get('id') if not video_id: return jsonify({'error': 'Missing video ID'}), 400 try: yt = YouTube(f"https://www.youtube.com/watch?v={video_id}") return jsonify({'title': yt.title}) except Exception as e: return jsonify({'error': str(e)}), 500 if __name__ == '__main__': app.run(debug=True)
- PyScript调用后端接口:
<py-script> from js import fetch async def get_title(): video_id = "7BXJIjfJCsA" response = await fetch(f"http://localhost:5000/get-video-title?id={video_id}") data = await response.json() if 'title' in data: print(data['title']) else: print(f"Error: {data['error']}") await get_title() </py-script>
方法三:简化URL参数
尝试去掉URL中的多余参数(如&t=111s),仅保留基础的视频ID部分,部分情况下可避免URL解析异常。
内容的提问来源于stack exchange,提问作者Deuf Story
相关产品推荐
相关产品推荐

