2026/8/14 4:31:56

用 tqdm+requests 打造支持进度条的PDF翻译CLI工具

用 tqdm+requests 打造支持进度条的PDF翻译CLI工具 前言最近在做一个批量翻译任务:把一个 200 多页的英文论文 PDF 翻译成中文,然后做对照阅读。这个过程中,我希望能看到翻译进度——一个 200 页的 PDF,如果只是黑盒等 5 分钟,体验是糟糕的。requests适合发请求,tqdm适合做进度条,把它们组合起来做一个 CLI 工具,既能给真实用户用,也能集成到后续自动化流水线里。本文给出一个完整的、可直接运行的 Python CLI 脚本。代码我已经用了两周,生产场景够用。环境准备Python 3.10依赖:pip install requests tqdmpipinstallrequests tqdm思路概述整体流程:用requests上传 PDF 到 PDFTranslator API轮询任务状态(类似异步任务模式)用tqdm显示总体进度下载翻译结果到本地PDFTranslator 提供了创建任务 → 轮询 → 下载的标准异步任务 API,这正好适合进度条场景。实现步骤Step 1: 上传 PDF 并创建翻译任务importrequestsfrompathlibimportPath API_BASEhttps://api.pdftranslator.org/v1defcreate_translation_task(pdf_path:str,target_lang:strzh-CN)-str:创建翻译任务,返回 task_id Args: pdf_path: 待翻译的 PDF 文件路径 target_lang: 目标语言代码,如 zh-CN / en / es / fr Returns: task_id: 翻译任务 ID,后续用于轮询结果 urlf{API_BASE}/translate/createheaders{Authorization:Bearer YOUR_API_KEY}# 替换为你的 API Keywithopen(pdf_path,rb)asf:files{file:(Path(pdf_path).name,f,application/pdf)}data{target_lang:target_lang,preserve_format:true}responserequests.post(url,headersheaders,filesfiles,datadata,timeout60)response.raise_for_status()returnresponse.json()[task_id]Step 2: 轮询任务状态importtimedefwait_for_task(task_id:str,poll_interval:float2.0)-dict:轮询任务状态直到完成 Args: task_id: 翻译任务 ID poll_interval: 轮询间隔(秒) Returns: 任务完成时的完整响应 JSON urlf{API_BASE}/translate/status/{task_id}headers{Authorization:Bearer YOUR_API_KEY}whileTrue:resprequests.get(url,headersheaders,timeout30).json()statusresp.get(status)ifstatuscompleted:returnrespifstatusfailed:raiseRuntimeError(f翻译失败:{resp.get(error)})# pending / processing 状态,继续轮询time.sleep(poll_interval)Step 3: 整合 tqdm 显示进度条fromtqdmimporttqdmimportsysdeftranslate_with_progress(pdf_path:str,output_path:str,target_lang:strzh-CN):带进度条的翻译流程 进度条 0-30%:上传 创建任务 进度条 30-90%:等待处理 进度条 90-100%:下载结果 bartqdm(total100,desc翻译PDF,unit%,filesys.stdout)try:# 1. 创建任务 (0% - 30%)bar.update(5)task_idcreate_translation_task(pdf_path,target_lang)bar.update(25)# 2. 轮询直到完成 (30% - 90%)# 假设根据页数估算处理进度,这里是简化版page_count_estimate_pages(pdf_path)last_progress30whileTrue:resultwait_for_task(task_id,poll_interval2.0)current_progressresult.get(progress,50)# 将处理进度映射到 30-90 区间mapped30int(current_progress*0.6)ifmappedlast_progress:bar.update(mapped-last_progress)last_progressmappedifresult[status]completed:break# 3. 下载结果 (90% - 100%)download_urlresult[download_url]withrequests.get(download_url,streamTrue,timeout120)asr:r.raise_for_status()withopen(output_path,wb)asf:forchunkinr.iter_content(chunk_size8192):f.write(chunk)bar.update(0.5)# 视觉补偿bar.update(100-last_progress)# 补满到 100%bar.set_description(f✅ 完成:{output_path})finally:bar.close()def_estimate_pages(pdf_path:str)-int:快速估算PDF页数(通过文件大小粗估)size_mbPath(pdf_path).stat().st_size/(1024*1024)returnint(size_mb*50)# 粗估:每页约 20KBStep 4: CLI 入口(用 argparse)importargparsedefmain():parserargparse.ArgumentParser(description带进度条的 PDF 翻译 CLI)parser.add_argument(pdf,help待翻译的 PDF 文件路径)parser.add_argument(-o,--output,help输出路径,默认与输入同名 _translated.pdf)parser.add_argument(-l,--lang,defaultzh-CN,help目标语言代码,默认 zh-CN。可选:en, es, fr, de, ja, ko)argsparser.parse_args()outputargs.outputorstr(Path(args.pdf).with_name(Path(args.pdf).stem_translated.pdf))translate_with_progress(args.pdf,output,args.lang)print(f\n✅ 翻译完成,已保存到:{output})if__name____main__:main()完整脚本把上面所有步骤合并到一个文件translate_pdf.py:#!/usr/bin/env python3# -*- coding: utf-8 -*- translate_pdf.py - 带进度条的 PDF 翻译 CLI 工具 用法: python translate_pdf.py report.pdf -l en -o report_en.pdf importargparseimportsysimporttimefrompathlibimportPathimportrequestsfromtqdmimporttqdm API_BASEhttps://api.pdftranslator.org/v1API_KEYYOUR_API_KEY# 替换为你的 API Keydefcreate_translation_task(pdf_path:str,target_lang:str)-str:创建翻译任务urlf{API_BASE}/translate/createheaders{Authorization:fBearer{API_KEY}}withopen(pdf_path,rb)asf:files{file:(Path(pdf_path).name,f,application/pdf)}data{target_lang:target_lang,preserve_format:true}resprequests.post(url,headersheaders,filesfiles,datadata,timeout60)resp.raise_for_status()returnresp.json()[task_id]defwait_for_task(task_id:str,poll_interval:float2.0)-dict:轮询任务状态urlf{API_BASE}/translate/status/{task_id}headers{Authorization:fBearer{API_KEY}}whileTrue:resprequests.get(url,headersheaders,timeout30).json()statusresp.get(status)ifstatuscompleted:returnrespifstatusfailed:raiseRuntimeError(f翻译失败:{resp.get(error)})time.sleep(poll_interval)defdownload_result(download_url:str,output_path:str,bar:tqdm):流式下载翻译结果withrequests.get(download_url,streamTrue,timeout120)asr:r.raise_for_status()withopen(output_path,wb)asf:forchunkinr.iter_content(chunk_size8192):ifchunk:f.write(chunk)bar.update(0.3)deftranslate_with_progress(pdf_path:str,output_path:str,target_lang:str):主流程bartqdm(total100,desc翻译中,unit%)task_idcreate_translation_task(pdf_path,target_lang)bar.update(30)# 上传完成try:whileTrue:resultwait_for_task(task_id,poll_interval2.0)progressresult.get(progress,50)current30int(progress*0.6)bar.nmin(90,current)bar.refresh()ifresult[status]completed:breakdownload_result(result[download_url],output_path,bar)bar.n100bar.refresh()finally:bar.close()defmain():parserargparse.ArgumentParser(description带进度条的 PDF 翻译 CLI)parser.add_argument(pdf,help待翻译 PDF)parser.add_argument(-o,--output)parser.add_argument(-l,--lang,defaultzh-CN)argsparser.parse_args()outputargs.outputorstr(Path(args.pdf).with_name(Path(args.pdf).stem_translated.pdf))translate_with_progress(args.pdf,output,args.lang)print(f\n✅ 已保存:{output})if__name____main__:main()运行效果$ python translate_pdf.py research.pdf-lzh-CN 翻译中:100%|████████████████████████|100/100[04:4700:00,2.87s/%]✅ 已保存: research_translated.pdf进度条下方会显示当前进度,翻译总耗时 5 分钟以内。工程化建议如果你打算把这个工具放到生产环境,有几点建议:重试机制:网络中断时增加tenacity重试日志:用logging替代 print,方便排查并发批量:concurrent.futures.ThreadPoolExecutor一次处理多个 PDF配置文件:API Key 用环境变量,不写死在代码里错误处理:对 PDF 损坏、文件过大等情况做用户友好提示总结进度条看似是小事,但对长时间任务的用户体验至关重要。tqdm几行代码就解决问题,值得纳入工具脚本的标配。完整脚本可以扩展为:批量翻译整个目录集成到 Celery 异步任务队列提供 Web UI(Tornado / FastAPI 后台任务)下一步建议:如果你也有批量翻译需求,可以扩展为 Web 界面,让非技术同事也能用。标签:Python、PDF翻译、tqdm、CLI工具、AI翻译