请求协助解密YouTube视频signatureCipher,修复视频链接提取脚本
修复YouTube视频链接解密与脚本失效问题
我编写了一段代码用于提取YouTube视频的最终链接,但该链接被signatureCipher加密,无法解密以获取文件大小:
import requests import json url = 'https://www.youtube.com/watch?v=RgzLnmTaCAU' r = requests.get(url) if r.status_code == 200: start = r.text.find('ytInitialPlayerResponse') + len('ytInitialPlayerResponse') + 3 end = r.text.find('};', start) + 1 json_str = r.text[start:end] data = json.loads(json_str) streaming_data = data['streamingData'] formats = streaming_data['formats'][0] cipher = formats['signatureCipher'] video_url = cipher.split('=')[3] print(video_url)
另外还有一个原本运行正常的脚本,针对上述特定URL突然失效,该脚本用于选择视频质量并查看对应视频数据(包括文件大小):
import requests import json #url = 'https://www.youtube.com/watch?v=EDmfFdAfytM' url = "https://youtu.be/RgzLnmTaCAU" r = requests.get(url) if r.status_code == 200: start = r.text.find('ytInitialPlayerResponse') + len('ytInitialPlayerResponse') + 3 end = r.text.find('};', start) + 1 json_str = r.text[start:end] data = json.loads(json_str) streaming_data = data['streamingData'] formats = streaming_data['formats'] adaptive_formats = streaming_data['adaptiveFormats'] print("================ Formats =================") format_labels = {} format_count = 1 for video_data in formats: quality_label = video_data['qualityLabel'] format_labels[format_count] = quality_label print(format_count, ": ", quality_label) format_count += 1 print("================ Adaptive Formats =================") for adaptive_video_data in adaptive_formats: if 'qualityLabel' in adaptive_video_data: quality_label = adaptive_video_data['qualityLabel'] format_labels[format_count] = quality_label print(format_count, ": ", quality_label) format_count += 1 selected_format = int(input("\nEnter the number corresponding to the quality label you want to view: ")) if selected_format in format_labels: selected_quality_label = format_labels[selected_format] print("\nSelected Quality Label: ", selected_quality_label) print("\nVideo Data for Selected Quality Label:\n") for video_data in formats: if video_data['qualityLabel'] == selected_quality_label: itag = video_data['itag'] url = video_data['url'] height = video_data['height'] width = video_data['width'] quality = video_data['quality'] quality_label = video_data['qualityLabel'] audio_quality = video_data['audioQuality'] bitrate_mbps = round(video_data['bitrate'] / (1024 * 1024), 2) print("itag:", itag) print('URL:', url) print('Height:', height) print('Width:', width) print('Quality:', quality) print('Bitrate:', quality_label, f'({bitrate_mbps} Mbps)') print('Audio Quality:', audio_quality) size = requests.get(url, stream=True) file_size = size.headers['content-length'] or size.headers['Content-Length'] print(f'File Size:, {int(file_size)/1024/1024:.3f} MB') print("\n") for adaptive_video_data in adaptive_formats: if 'qualityLabel' in adaptive_video_data and adaptive_video_data['qualityLabel'] == selected_quality_label: itag = adaptive_video_data['itag'] url = adaptive_video_data['url'] height = adaptive_video_data['height'] width = adaptive_video_data['width'] quality = adaptive_video_data['quality'] quality_label = adaptive_video_data['qualityLabel'] bitrate_mbps = round(adaptive_video_data['bitrate'] / (1024 * 1024), 2) print("itag:", itag) print('URL:', url) print('Height:', height) print('Width:', width) print('Quality:', quality) print('Bitrate:', quality_label, f'({bitrate_mbps} Mbps)') file_lenght = requests.get(url, stream=True) size_of_file = file_lenght.headers['content-length'] or file_lenght.headers['Content-Length'] print(f'File Size:, {int(size_of_file)/1024/1024:.3f} MB') print("\n") else: print("Invalid Selection") else: print('Error: Could not retrieve content from the URL')
问题根源
- signatureCipher处理错误:手动split解析参数不可靠,且YouTube对部分视频链接添加了签名验证,需要执行页面中的JS函数解密签名。
- 页面解析不稳定:用固定索引截取
ytInitialPlayerResponse容易因页面结构变化失效。 - 请求被拦截:无请求头的裸请求容易被YouTube识别为爬虫,导致返回数据异常。
修复方案
依赖安装
先安装所需库:
pip install requests execjs urllib3
同时需要安装Node.js(execjs依赖Node执行JS代码)。
完整修复代码
import requests import json import re import urllib.parse import execjs def get_youtube_video_data(url): # 模拟浏览器请求头,避免被拦截 headers = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36', 'Accept-Language': 'en-US,en;q=0.9', 'Referer': 'https://www.youtube.com/' } r = requests.get(url, headers=headers) if r.status_code != 200: print('Error: Could not retrieve content from the URL') return # 用正则匹配ytInitialPlayerResponse,更稳定 match = re.search(r'ytInitialPlayerResponse\s*=\s*({.*?});', r.text, re.DOTALL) if not match: print('Error: Failed to extract player response') return json_str = match.group(1) data = json.loads(json_str) streaming_data = data.get('streamingData', {}) formats = streaming_data.get('formats', []) adaptive_formats = streaming_data.get('adaptiveFormats', []) # 提取签名解密JS函数 js_func_name_match = re.search(r'sig\s*=\s*(.*?)\(sig\);', r.text) if not js_func_name_match: print('Error: Failed to extract signature decryption function name') return js_func_name = js_func_name_match.group(1) js_func_match = re.search(r'var\s+' + js_func_name + r'\s*=\s*function\(.*?\)\s*({.*?})', r.text, re.DOTALL) if not js_func_match: print('Error: Failed to extract signature decryption function body') return js_func_str = f'{js_func_name}(sig) {js_func_match.group(1)}' ctx = execjs.compile(js_func_str) def decrypt_signature(signature): return ctx.call(js_func_name, signature) # 整理所有格式选项 format_labels = {} format_count = 1 # 处理普通格式 print("================ Formats =================") for video_data in formats: quality_label = video_data.get('qualityLabel', 'Unknown') format_labels[format_count] = { 'label': quality_label, 'data': video_data } print(format_count, ": ", quality_label) format_count += 1 # 处理自适应格式 print("================ Adaptive Formats =================") for adaptive_video_data in adaptive_formats: if 'qualityLabel' in adaptive_video_data: quality_label = adaptive_video_data['qualityLabel'] format_labels[format_count] = { 'label': quality_label, 'data': adaptive_video_data } print(format_count, ": ", quality_label) format_count += 1 # 用户选择格式 try: selected_format = int(input("\nEnter the number corresponding to the quality label you want to view: ")) except ValueError: print("Invalid input, must be a number") return if selected_format not in format_labels: print("Invalid Selection") return selected_item = format_labels[selected_format] selected_quality_label = selected_item['label'] video_data = selected_item['data'] print("\nSelected Quality Label: ", selected_quality_label) print("\nVideo Data for Selected Quality Label:\n") # 处理链接解密 if 'signatureCipher' in video_data: cipher_params = urllib.parse.parse_qs(video_data['signatureCipher']) video_url = cipher_params['url'][0] signature = decrypt_signature(cipher_params['s'][0]) # 拼接签名到URL video_url += '&signature=' + signature elif 'url' in video_data: video_url = video_data['url'] else: print('Error: No valid video URL found') return # 提取视频信息 itag = video_data.get('itag') height = video_data.get('height') width = video_data.get('width') quality = video_data.get('quality') bitrate_mbps = round(video_data.get('bitrate', 0) / (1024 * 1024), 2) audio_quality = video_data.get('audioQuality', 'N/A') print("itag:", itag) print('URL:', video_url) print('Height:', height) print('Width:', width) print('Quality:', quality) print('Bitrate:', selected_quality_label, f'({bitrate_mbps} Mbps)') print('Audio Quality:', audio_quality) # 获取文件大小 try: size_response = requests.head(video_url, headers=headers, allow_redirects=True) file_size = size_response.headers.get('content-length') or size_response.headers.get('Content-Length') if file_size: print(f'File Size:, {int(file_size)/1024/1024:.3f} MB') else: print('File Size: Could not retrieve') except Exception as e: print(f'Error retrieving file size: {str(e)}') print("\n") if __name__ == "__main__": # 测试URL url = "https://youtu.be/RgzLnmTaCAU" get_youtube_video_data(url)
关键修复点说明
- 稳定解析页面数据:用正则表达式匹配
ytInitialPlayerResponse,避免固定索引截取的脆弱性。 - 解密signatureCipher:提取页面中的签名解密JS函数,用execjs在Python环境中执行解密,生成有效视频URL。
- 防拦截请求头:添加模拟浏览器的请求头,降低被YouTube识别为爬虫的概率。
- 优化文件大小获取:用
head请求替代get请求,只获取响应头,减少带宽消耗和请求时间。
内容的提问来源于stack exchange,提问作者Ammad
相关产品推荐
相关产品推荐

