Python下载文件写入报错:如何仅在文件末尾追加指定字符串
问题说明
使用Python实现文件下载+末尾追加指定行功能时,首次运行抛出如下类型错误:
f.write(data + """<auth-user-pass> TypeError: can't concat str to bytes
将字符串修改为b"""<auth-user-pass>"""格式后类型错误消失,但出现新问题:自定义字符串被重复追加到文件每一段内容后,而非仅出现在整个文件尾部。尝试f.write(str(data) + "<auth-user-pass>")写法仍未达到预期效果。
原实现代码如下:
import requests from multiprocessing.pool import ThreadPool def download_url(url): print("downloading: ", url) # 逻辑说明:取url中最后一个/后的片段作为文件名 # 例如url为abc/xyz/file.txt时,文件名为file.txt file_name_start_pos = url.rfind("/") + 1 file_name = url[file_name_start_pos:] save_path = 'ovpns/' complete_path = os.path.join(save_path, file_name) print(complete_path) r = requests.get(url, stream=True) if r.status_code == requests.codes.ok: with open(complete_path, 'wb') as f: for data in r: f.write(data + """<auth-user-pass> username password </auth-user-pass>""") return url servers = [ "us-ca72.nordvpn.com", "us-ca73.nordvpn.com" ] urls = [] for server in servers: urls.append("https://downloads.nordcdn.com/configs/files/ovpn_legacy/servers/" + server + ".udp1194.ovpn") # 开启5个线程并发执行任务,每次从urls列表取一个元素传入下载函数 results = ThreadPool(5).imap_unordered(download_url, urls) for r in results: print(r)
错误原因
- 类型错误:requests流式下载返回的
data是二进制(bytes)类型,无法直接和字符串(str)类型拼接 - 内容重复追加:追加自定义内容的逻辑写在了响应内容遍历循环内部,每读取一块下载数据就会拼接一次自定义内容写入,导致内容重复出现在文件中
- 原代码缺失
os模块导入,运行时会触发名称错误,且没有提前创建存储目录,目录不存在时会抛出文件写入异常
修复后代码
import os import requests from multiprocessing.pool import ThreadPool def download_url(url): print("downloading: ", url) file_name_start_pos = url.rfind("/") + 1 file_name = url[file_name_start_pos:] save_path = 'ovpns/' # 自动创建存储目录,已存在时不报错 os.makedirs(save_path, exist_ok=True) complete_path = os.path.join(save_path, file_name) print(complete_path) # 待追加内容提前转为bytes类型 append_content = b"""<auth-user-pass> username password </auth-user-pass>""" r = requests.get(url, stream=True) if r.status_code == requests.codes.ok: # 先完整写入下载的原文件内容 with open(complete_path, 'wb') as f: for data in r: f.write(data) # 原文件写入完成后,以二进制追加模式在末尾写入自定义内容 with open(complete_path, 'ab') as f: f.write(append_content) return url servers = [ "us-ca72.nordvpn.com", "us-ca73.nordvpn.com" ] urls = [] for server in servers: urls.append("https://downloads.nordcdn.com/configs/files/ovpn_legacy/servers/" + server + ".udp1194.ovpn") results = ThreadPool(5).imap_unordered(download_url, urls) for r in results: print(r)
关键调整点
- 补全
os模块导入,新增目录自动创建逻辑,避免路径不存在导致的写入失败 - 全程使用二进制模式读写文件,不需要做bytes和str的类型转换,避免编码问题
- 将追加内容的逻辑移出下载循环,等原文件全部写入完成后再执行追加操作,保证自定义内容仅在文件末尾出现一次
内容的提问来源于stack exchange,提问作者Ppap
相关产品推荐
相关产品推荐

