前言
最近在做一个批量翻译任务:把一个 200 多页的英文论文 PDF 翻译成中文,然后做对照阅读。这个过程中,我希望能看到翻译进度——一个 200 页的 PDF,如果只是黑盒等 5 分钟,体验是糟糕的。
requests 适合发请求,tqdm 适合做进度条,把它们组合起来做一个 CLI 工具,既能给真实用户用,也能集成到后续自动化流水线里。
本文给出一个完整的、可直接运行的 Python CLI 脚本。代码我已经用了两周,生产场景够用。
环境准备
- Python 3.10+
- 依赖:
pip install requests tqdm
pip install requests tqdm
思路概述
整体流程:
- 用
requests上传 PDF 到 PDFTranslator API - 轮询任务状态(类似异步任务模式)
- 用
tqdm显示总体进度 - 下载翻译结果到本地
PDFTranslator 提供了创建任务 → 轮询 → 下载的标准异步任务 API,这正好适合进度条场景。
实现步骤
Step 1: 上传 PDF 并创建翻译任务
import requests
from pathlib import Path
API_BASE = "https://api.pdftranslator.org/v1"
def create_translation_task(pdf_path: str, target_lang: str = "zh-CN") -> str:
"""创建翻译任务,返回 task_id
Args:
pdf_path: 待翻译的 PDF 文件路径
target_lang: 目标语言代码,如 zh-CN / en / es / fr
Returns:
task_id: 翻译任务 ID,后续用于轮询结果
"""
url = f"{API_BASE}/translate/create"
headers = {"Authorization": "Bearer YOUR_API_KEY"} # 替换为你的 API Key
with open(pdf_path, "rb") as f:
files = {"file": (Path(pdf_path).name, f, "application/pdf")}
data = {"target_lang": target_lang, "preserve_format": "true"}
response = requests.post(url, headers=headers, files=files, data=data, timeout=60)
response.raise_for_status()
return response.json()["task_id"]
Step 2: 轮询任务状态
import time
def wait_for_task(task_id: str, poll_interval: float = 2.0) -> dict:
"""轮询任务状态直到完成
Args:
task_id: 翻译任务 ID
poll_interval: 轮询间隔(秒)
Returns:
任务完成时的完整响应 JSON
"""
url = f"{API_BASE}/translate/status/{task_id}"
headers = {"Authorization": "Bearer YOUR_API_KEY"}
while True:
resp = requests.get(url, headers=headers, timeout=30).json()
status = resp.get("status")
if status == "completed":
return resp
if status == "failed":
raise RuntimeError(f"翻译失败: {resp.get('error')}")
# pending / processing 状态,继续轮询
time.sleep(poll_interval)
Step 3: 整合 tqdm 显示进度条
from tqdm import tqdm
import sys
def translate_with_progress(pdf_path: str, output_path: str, target_lang: str = "zh-CN"):
"""带进度条的翻译流程
进度条 0-30%:上传 + 创建任务
进度条 30-90%:等待处理
进度条 90-100%:下载结果
"""
bar = tqdm(total=100, desc="翻译PDF", unit="%", file=sys.stdout)
try:
# 1. 创建任务 (0% -> 30%)
bar.update(5)
task_id = create_translation_task(pdf_path, target_lang)
bar.update(25)
# 2. 轮询直到完成 (30% -> 90%)
# 假设根据页数估算"处理进度",这里是简化版
page_count = _estimate_pages(pdf_path)
last_progress = 30
while True:
result = wait_for_task(task_id, poll_interval=2.0)
current_progress = result.get("progress", 50)
# 将处理进度映射到 30-90 区间
mapped = 30 + int(current_progress * 0.6)
if mapped > last_progress:
bar.update(mapped - last_progress)
last_progress = mapped
if result["status"] == "completed":
break
# 3. 下载结果 (90% -> 100%)
download_url = result["download_url"]
with requests.get(download_url, stream=True, timeout=120) as r:
r.raise_for_status()
with open(output_path, "wb") as f:
for chunk in r.iter_content(chunk_size=8192):
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_mb = Path(pdf_path).stat().st_size / (1024 * 1024)
return int(size_mb * 50) # 粗估:每页约 20KB
Step 4: CLI 入口(用 argparse)
import argparse
def main():
parser = argparse.ArgumentParser(description="带进度条的 PDF 翻译 CLI")
parser.add_argument("pdf", help="待翻译的 PDF 文件路径")
parser.add_argument("-o", "--output", help="输出路径,默认与输入同名 _translated.pdf")
parser.add_argument("-l", "--lang", default="zh-CN",
help="目标语言代码,默认 zh-CN。可选:en, es, fr, de, ja, ko")
args = parser.parse_args()
output = args.output or str(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
"""
import argparse
import sys
import time
from pathlib import Path
import requests
from tqdm import tqdm
API_BASE = "https://api.pdftranslator.org/v1"
API_KEY = "YOUR_API_KEY" # 替换为你的 API Key
def create_translation_task(pdf_path: str, target_lang: str) -> str:
"""创建翻译任务"""
url = f"{API_BASE}/translate/create"
headers = {"Authorization": f"Bearer {API_KEY}"}
with open(pdf_path, "rb") as f:
files = {"file": (Path(pdf_path).name, f, "application/pdf")}
data = {"target_lang": target_lang, "preserve_format": "true"}
resp = requests.post(url, headers=headers, files=files, data=data, timeout=60)
resp.raise_for_status()
return resp.json()["task_id"]
def wait_for_task(task_id: str, poll_interval: float = 2.0) -> dict:
"""轮询任务状态"""
url = f"{API_BASE}/translate/status/{task_id}"
headers = {"Authorization": f"Bearer {API_KEY}"}
while True:
resp = requests.get(url, headers=headers, timeout=30).json()
status = resp.get("status")
if status == "completed":
return resp
if status == "failed":
raise RuntimeError(f"翻译失败: {resp.get('error')}")
time.sleep(poll_interval)
def download_result(download_url: str, output_path: str, bar: tqdm):
"""流式下载翻译结果"""
with requests.get(download_url, stream=True, timeout=120) as r:
r.raise_for_status()
with open(output_path, "wb") as f:
for chunk in r.iter_content(chunk_size=8192):
if chunk:
f.write(chunk)
bar.update(0.3)
def translate_with_progress(pdf_path: str, output_path: str, target_lang: str):
"""主流程"""
bar = tqdm(total=100, desc="翻译中", unit="%")
task_id = create_translation_task(pdf_path, target_lang)
bar.update(30) # 上传完成
try:
while True:
result = wait_for_task(task_id, poll_interval=2.0)
progress = result.get("progress", 50)
current = 30 + int(progress * 0.6)
bar.n = min(90, current)
bar.refresh()
if result["status"] == "completed":
break
download_result(result["download_url"], output_path, bar)
bar.n = 100
bar.refresh()
finally:
bar.close()
def main():
parser = argparse.ArgumentParser(description="带进度条的 PDF 翻译 CLI")
parser.add_argument("pdf", help="待翻译 PDF")
parser.add_argument("-o", "--output")
parser.add_argument("-l", "--lang", default="zh-CN")
args = parser.parse_args()
output = args.output or str(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 -l zh-CN
翻译中: 100%|████████████████████████| 100/100 [04:47<00: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翻译

414

被折叠的 条评论
为什么被折叠?



