request.urlopen访问FTP含西里尔字符文件报550找不到文件编码问题求解
解决方案
方案1:优先推荐 - 自定义FTPHandler指定编码(无侵入,可灵活传编码参数)
原生urllib未暴露FTP编码配置入口,可通过继承FTPHandler重写连接逻辑实现自定义编码,完全满足你传入编码自动赋值给ftplib.FTP实例的需求:
from urllib.request import FTPHandler, build_opener import ftplib class EncodedFTPHandler(FTPHandler): def __init__(self, ftp_encoding='utf-8', *args, **kwargs): self.ftp_encoding = ftp_encoding super().__init__(*args, **kwargs) def connect_ftp(self, user, passwd, host, port, dirs, timeout=None): ftp = ftplib.FTP(timeout=timeout) # 自定义编码赋值,支持utf-8、cp1251任意切换 ftp.encoding = self.ftp_encoding ftp.connect(host, port) ftp.login(user, passwd) # 对应你之前的UTF8开启逻辑,非UTF8编码可跳过 if self.ftp_encoding == 'utf-8': try: ftp.sendcmd('OPTS UTF8 ON') except: pass ftp.cwd('/'.join(dirs)) return ftp
调用方式如下,返回的响应和原生urlopen完全一致,是addinfourl对象,不影响你后续解析大体积XML:
# 需要cp1251时直接修改ftp_encoding参数即可 opener = build_opener(EncodedFTPHandler(ftp_encoding='utf-8')) url = 'ftp://login:password@ftpaddr:21/folder/Message_БП_TT.xml' # 无需手动做编码转换,直接传入原始URL resp = opener.open(url)
方案2:全局猴子补丁修改默认编码(适合快速验证场景)
如果不需要多编码灵活切换,可直接修改ftpwrapper的全局默认配置:
from urllib.request import ftpwrapper import ftplib original_init = ftpwrapper.__init__ def patched_init(self, host, user, passwd, acct, timeout=None): original_init(self, host, user, passwd, acct, timeout) # 全局修改FTP编码为utf-8 self.ftp.encoding = 'utf-8' try: self.ftp.sendcmd('OPTS UTF8 ON') except: pass ftpwrapper.__init__ = patched_init # 后续调用原生urlopen无需额外处理 from urllib import request url = 'ftp://login:password@ftpaddr:21/folder/Message_БП_TT.xml' resp = request.urlopen(url)
多FTP服务兼容逻辑
你遇到的部分服务正常、部分报错的问题,本质是不同FTP服务端的默认文件名编码不同:开启了UTF8支持的服务用utf-8编码,俄语区未开UTF8的服务默认用cp1251编码,可增加降级逻辑自动适配:
def get_ftp_resp(url, encodings=['utf-8', 'cp1251']): for encoding in encodings: opener = build_opener(EncodedFTPHandler(ftp_encoding=encoding)) try: return opener.open(url) except Exception as e: if '550 The system cannot find the file specified' in str(e): continue raise raise Exception("所有编码均无法匹配当前FTP服务配置")
内容的提问来源于stack exchange,提问作者Skaper1101
相关产品推荐
相关产品推荐

