解决Python批量下载音频时的SSL UNSAFE_LEGACY_RENEGOTIATION_DISABLED错误
解决SSL UNSAFE_LEGACY_RENEGOTIATION_DISABLED错误批量下载WAV文件
问题
从conllu文件中提取音频URL后,浏览器可正常下载WAV文件,但使用Python的urllib.request.urlretrieve批量下载时,出现以下错误:
URLError: <urlopen error [SSL: UNSAFE_LEGACY_RENEGOTIATION_DISABLED] unsafe legacy renegotiation disabled (_ssl.c:1007)>
需处理300+音频文件,无法手动操作,寻求解决方案。
原代码
import os import re import urllib.request from conll3 import * import ssl ssl._create_default_https_context = ssl._create_unverified_context def extract_and_save_wav(file_path, output_directory): """ Extracts and saves WAV audio file from a data file. Args: file_path (str): Path of the data file. output_directory (str): Output directory for the WAV audio file. Return: str or None: Path of the extracted and saved WAV audio file, or None if no audio file is found. """ for root, dirs, files in os.walk(directory_path): for file in files: if file.endswith(".conllu"): file_path = os.path.join(root, file) trees = conllFile2trees(file_path) print(file_path) for tree in trees: tree_str = str(tree) sound_url_match = re.search(r'# sound_url = (.+)', tree_str) # 查找音频文件URL if sound_url_match: sound_url = sound_url_match.group(1) print(sound_url) if sound_url.endswith('.wav'): file_name = os.path.basename(file_path) file_name_without_ext = os.path.splitext(file_name)[0] output_file_name = f"{file_name_without_ext}.wav" if output_directory is not None: output_path = os.path.join(output_directory, output_file_name) urllib.request.urlretrieve(sound_url, output_path) # 下载并保存音频文件 return output_path return None directory_path = "../SUD_Naija-NSC-master/non_gold/" output_wav = "../WAV/non_gold/" extract_and_save_wav(directory_path, output_wav)
错误信息
Reloaded modules: conll3 ../SUD_Naija-NSC-master/non_gold/LAG_01_Hairdressing_M.conllu http://naijasyncor.huma-num.fr/carte/mp3/LAG_01_Hairdressing_M.wav Traceback (most recent call last): File ~/miniconda3/envs/tal-ml/lib/python3.10/urllib/request.py:1348 in do_open h.request(req.get_method(), req.selector, req.data, headers, File ~/miniconda3/envs/tal-ml/lib/python3.10/http/client.py:1283 in request self._send_request(method, url, body, headers, encode_chunked) File ~/miniconda3/envs/tal-ml/lib/python3.10/http/client.py:1329 in _send_request self.endheaders(body, encode_chunked=encode_chunked) File ~/miniconda3/envs/tal-ml/lib/python3.10/http/client.py:1278 in endheaders self._send_output(message_body, encode_chunked=encode_chunked) File ~/miniconda3/envs/tal-ml/lib/python3.10/http/client.py:1038 in _send_output self.send(msg) File ~/miniconda3/envs/tal-ml/lib/python3.10/http/client.py:976 in send self.connect() File ~/miniconda3/envs/tal-ml/lib/python3.10/http/client.py:1455 in connect self.sock = self._context.wrap_socket(self.sock, File ~/miniconda3/envs/tal-ml/lib/python3.10/ssl.py:513 in wrap_socket return self.sslsocket_class._create( File ~/miniconda3/envs/tal-ml/lib/python3.10/ssl.py:1071 in _create self.do_handshake() File ~/miniconda3/envs/tal-ml/lib/python3.10/ssl.py:1342 in do_handshake self._sslobj.do_handshake() SSLError: [SSL: UNSAFE_LEGACY_RENEGOTIATION_DISABLED] unsafe legacy renegotiation disabled (_ssl.c:1007) During handling of the above exception, another exception occurred: Traceback (most recent call last): File ~/miniconda3/envs/tal-ml/lib/python3.10/site-packages/spyder_kernels/py3compat.py:356 in compat_exec exec(code, globals, locals) File ~/Desktop/Stage 2023-2024/Python/recuperation_audio.py:47 extract_and_save_wav(directory_path, output_wav) File ~/Desktop/Stage 2023-2024/Python/recuperation_audio.py:39 in extract_and_save_wav urllib.request.urlretrieve(sound_url, output_path) # 下载并保存音频文件 File ~/miniconda3/envs/tal-ml/lib/python3.10/urllib/request.py:241 in urlretrieve with contextlib.closing(urlopen(url, data)) as fp: File ~/miniconda3/envs/tal-ml/lib/python3.10/urllib/request.py:216 in urlopen return opener.open(url, data, timeout) File ~/miniconda3/envs/tal-ml/lib/python3.10/urllib/request.py:525 in open response = meth(req, response) File ~/miniconda3/envs/tal-ml/lib/python3.10/urllib/request.py:634 in http_response response = self.parent.error( File ~/miniconda3/envs/tal-ml/lib/python3.10/urllib/request.py:557 in error result = self._call_chain(*args) File ~/miniconda3/envs/tal-ml/lib/python3.10/urllib/request.py:496 in _call_chain result = func(*args) File ~/miniconda3/envs/tal-ml/lib/python3.10/urllib/request.py:749 in http_error_302 return self.parent.open(new, timeout=req.timeout) File ~/miniconda3/envs/tal-ml/lib/python3.10/urllib/request.py:519 in open response = self._open(req, data) File ~/miniconda3/envs/tal-ml/lib/python3.10/urllib/request.py:536 in _open result = self._call_chain(self.handle_open, protocol, protocol + File ~/miniconda3/envs/tal-ml/lib/python3.10/urllib/request.py:496 in _call_chain result = func(*args) File ~/miniconda3/envs/tal-ml/lib/python3.10/urllib/request.py:1391 in https_open return self.do_open(http.client.HTTPSConnection, req, File ~/miniconda3/envs/tal-ml/lib/python3.10/urllib/request.py:1351 in do_open raise URLError(err) URLError: <urlopen error [SSL: UNSAFE_LEGACY_RENEGOTIATION_DISABLED] unsafe legacy renegotiation disabled (_ssl.c:1007)>
解决方案
问题原因
服务器使用了旧版SSL重协商协议,Python 3.10+默认禁用了这种不安全的协商方式,导致握手失败。另外原代码存在参数错误:函数extract_and_save_wav定义的参数是file_path,但函数内部使用未定义的directory_path,会触发NameError。
修正步骤
- 修复函数参数错误:将函数参数改为
directory_path,匹配内部使用的变量 - 创建兼容旧版SSL的上下文:使用
ssl.SSLContext并启用OP_LEGACY_SERVER_CONNECT选项(Python 3.10+支持),允许与旧版服务器进行SSL协商 - 使用自定义上下文下载:创建自定义opener并安装,让
urlretrieve使用该上下文 - 增强鲁棒性:添加目录自动创建、重复文件检查和错误捕获
完整修正代码
import os import re import urllib.request from conll3 import * import ssl def extract_and_save_wav(directory_path, output_directory): """ Extracts and saves WAV audio file from a data file. Args: directory_path (str): Path of the directory containing data files. output_directory (str): Output directory for the WAV audio file. Return: list: List of saved WAV file paths, or empty list if no files found. """ # 创建支持旧版SSL重协商的上下文 ctx = ssl.create_default_context() ctx.options |= ssl.OP_LEGACY_SERVER_CONNECT # 安装自定义opener opener = urllib.request.build_opener(urllib.request.HTTPSHandler(context=ctx)) urllib.request.install_opener(opener) saved_paths = [] # 确保输出目录存在 os.makedirs(output_directory, exist_ok=True) for root, dirs, files in os.walk(directory_path): for file in files: if file.endswith(".conllu"): file_path = os.path.join(root, file) trees = conllFile2trees(file_path) print(f"Processing: {file_path}") for tree in trees: tree_str = str(tree) sound_url_match = re.search(r'# sound_url = (.+)', tree_str) if sound_url_match: sound_url = sound_url_match.group(1).strip() print(f"Found URL: {sound_url}") if sound_url.endswith('.wav'): file_name = os.path.basename(file_path) file_name_without_ext = os.path.splitext(file_name)[0] output_file_name = f"{file_name_without_ext}.wav" output_path = os.path.join(output_directory, output_file_name) # 避免重复下载 if not os.path.exists(output_path): try: urllib.request.urlretrieve(sound_url, output_path) saved_paths.append(output_path) print(f"Saved to: {output_path}") except Exception as e: print(f"Failed to download {sound_url}: {str(e)}") else: print(f"File already exists: {output_path}") return saved_paths directory_path = "../SUD_Naija-NSC-master/non_gold/" output_wav = "../WAV/non_gold/" saved_files = extract_and_save_wav(directory_path, output_wav) print(f"\nTotal saved files: {len(saved_files)}")
内容的提问来源于stack exchange,提问作者Peilin
相关产品推荐
相关产品推荐

