<Project-8.1.1 pdf2tx-mm> Python 调用 ChatGPT API 翻译PDF内容 历程心得

原因

用ZhipuAI,测试用的PDF里,有国名+西部省+穆斯林,翻译结果返回 “系统检测到输入或生成内容可能包含不安全或敏感内容,请您避免输入易产生敏感内容的提 示语,感谢您的配合” 。想过先替换掉省名、民族名等,不发送敏感词去翻译。再试下去看到的结果就没剩几个省,内容也是。中文存在的意思都要考虑,伤心一天。

后来用的MyMemory Translater, 测试时翻译达到上限。用deep_translator调用MyMemory,返回竟然是google服务。改用MyMemory API,没几页就limited。浪费时间。 已知,MyMemory 最大的问题,日文到简体中文会是错意,但翻译成老国语你体会一下:日语你好,老国语是问候。 英语到两国文都没问题。 日语到英语也是原来的意思。难到中文就这样了? 

几个小时前,用ChatGPT API试试。

Chatgpt

CSDN里好像谈及它,都是收费的。贫穷的我不想浪费。

1. OpenAI 版本冲突

代码试要有一个过程... 代码总出错, 出错的信息如下:

You tried to access openai.ChatCompletion, but this is no longer supported in openai>=1.0.0 - see the README at https://github.com/openai/openai-python for the API.
You can run openai migrate to automatically upgrade your codebase to use the 1.0.0 interface.
Alternatively, you can pin your installation to the old version, e.g. pip install openai==0.28
A detailed migration guide is available here: https://github.com/openai/openai-python/discussions/742
————————————————版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。原文链接:https://blog.csdn.net/davenian/article/details/142820402

CMD里执行:

CMD: dir /s /b openai.py

蹦出来3个 openai.py 即三个源。 于是想到 python 虚拟环境 VE,写个流水充数,然后就去做饭。于是有了篇文章:

<OS 有关> Python 虚拟环境 用处-CSDN博客  链接可以打开阅读。

2. OpenAI 库升级 类方法改变了

升级PC的openai库,新库里没有这个类:openai.ChatCompletion.create

报错:

 'ChatCompletion' object is not subscriptable

我也是第一次用 API 去调用 chatgpt 模型,受些文启发《【新知】chatGPT 使用笔记(二)——chatGPT API的使用》参考里面的代码,使用的 ChatCompletion 对象...  将近2小时找有关的代码与文章,看官网,如下链接(不全但有线索):

https://stackoverflow.com/questions/77444332/openai-python-package-error-chatcompletion-object-is-not-subscriptable

GitHub - openai/openai-python: The official Python library for the OpenAI API

https://platform.openai.com/docs/quickstart?desktop-os=windows&language-preference=python

Issue with openai.ChatCompletion.create() in Latest OpenAI Python Library - API - OpenAI Developer Forum

直到上下两篇看完,才明白。

https://github.com/openai/openai-python/discussions/884

类改名儿了

新的是 

client.chat.completions.create
或
openai.chat.completions.create

后者是全局函数来调用 OpenAI 的 API,我选择前者方法 客户端实例。在

OpenAI Developer quickstart 示例:

from openai import OpenAI
client = OpenAI()completion = client.chat.completions.create(model="gpt-4o-mini",messages=[{"role": "system", "content": "You are a helpful assistant."},{"role": "user","content": "Write a haiku about recursion in programming."}]
)print(completion.choices[0].message)

示例中,OpenAI 的 API 客户端类,而不只是模块级的调用,这个在代码上更变通些。

3. 没有提供所需的参数

错误信息 “Missing required arguments; Expected either ('messages' and 'model') or ('messages', 'model' and 'stream') arguments to be given

大概意思是: OpenAI 的 ChatCompletion.create() 方法时,没有提供所需的参数。

其实, 调用 OpenAI 的 ChatGPT 模型来生成对话或文本的核心函数。 这类新旧版本没什么变化。在报错的信息,很误导。我遇到时,问题出在两个方面,一个是 类的名字变了,另一个是:

我看到的简中都是 使用字典式访问 message 对象中的 content 字段(从 choices 返回的对象中提取生成的文本。),我也是跟着学的,也中招儿了。

现在是:从 message 对象中直接访问 content 属性

questions = completions.choices[0].message.content.strip().split("\n\n")

下面我放上以前的,这下就弄清楚了。

questions = completions.choices[0].message['content'].strip().split("\n\n")

4. 提示词

要Chatgpt做事,就要给它文字上的引导。不再是喂鸡等蛋或是听打鸣。 他是有智慧的,生命对他来说是无穷无尽的时间。有时想想,那它应该叫什么?

作为翻译的调用,就要让他知道,他的职责是翻译,还要让他知道向哪种文字翻译。前者是固定的角色,后者是变量, 这样就很简单,我的实现方法,都不需要判断自源文字,只想让他知道去翻译,翻译成什么文字就行。

实例是,翻译成老中文。我给的prompt:

chatgpt_prompt = f"translate to {target_language}:\n\n{chunk}"

与 AI 对话,主语宾语都可以略,像老朋友一样。  “ to 啥 chinese ”介词短语,修饰动词 translate ,他就明白。 用 AI 做翻译,都不用改代码, 上传页面,加选项就全搞定。 

upload.html<label for="enginet">选择翻译引擎:</label><select id="enginet" name="enginet"><option value="google">Google 翻译</option><option value="microsoft">Microsoft 翻译</option><option value="chatgpt">ChatGPT 翻译</option><!-- 如有其他翻译引擎,可在此添加 --></select><label for="target_language">选择目标语言:</label><select id="target_language" name="target_language"><option value="zh-cn">简体中文</option><option value="zh-tw">繁体中文(台湾)</option>

看上传页面的代码,后4行,尤其是最后两行。(我有做了一个 字典映射,为了另外两个翻译引擎使用)

    'en': 'English','ja': 'Japanese','zh-cn': 'Simplified Chinese ','zh-tw': 'Traditional Chinese',

代码

其它几个文件都没有改,在这版里,去掉了 ZhipuAI, 去掉了 MyMemory Translator, 只保留Google Translator, Azure Translator, 还有新加入的 Chatgpt.

目录结构

pdf2tx-MM/
├── app.py main #programe
├── config.ini  #api key
├── requirements.txt  #libs
├── Dockerfile #docker building file.
├── templates/ # as names
│   ├── upload.html
│   ├── processing.html
│   └── result.html
└── uploads/  # 用于存储上传的文件 auto create

1. app.py

import os
import uuid
import logging
import configparser
from flask import Flask, render_template, request, redirect, url_for, Response
from threading import Thread, Lock
from werkzeug.utils import secure_filename
from pdf2image import convert_from_path
import pytesseract
from deep_translator import GoogleTranslator, MicrosoftTranslator
from concurrent.futures import ThreadPoolExecutor
from collections import defaultdict
import time # 导入 time 模块, 显示处理时间用
from datetime import timedelta #在结果页面显示处理时间,格式为 HH:MM
import nltk
#try:
#    nltk.data.find('tokenizers/punkt','tokenizers/punkt_tank')
#except LookupError:
#    nltk.download('punkt','punkt_tank', quiet=True)#nltk.download('punkt', quiet=True) # 已经安装,用:python -m nltk.downloader all  
# 但运行还会报错! 还需要安装 unstructured 库,Y TMD在介绍里没说 f!
from functools import lru_cache
from pdfminer.high_level import extract_text as pdf_extract_text
from pdfminer.pdfparser import PDFSyntaxError
from langdetect import detect
import jieba
from janome.tokenizer import Tokenizer
import random
from openai import OpenAI# 定义支持的语言映射
language_mapping = {'en': 'english','fr': 'french','de': 'german','es': 'spanish','it': 'italian','ja': 'japanese','ko': 'korean','ru': 'russian','zh-cn': 'chinese','zh-tw': 'chinese','zh': 'chinese','pt': 'portuguese','ar': 'arabic','hi': 'hindi',# 添加其他语言
}# OCR 语言代码映射
ocr_language_mapping = {'en': 'eng','fr': 'fra','de': 'deu','es': 'spa','it': 'ita','ja': 'jpn','ko': 'kor','ru': 'rus','zh-cn': 'chi_sim','zh-tw': 'chi_tra',# 添加更多语言如有需要
}# Microsoft Translator 语言代码映射
microsoft_language_mapping = {'en': 'en','fr': 'fr','de': 'de','es': 'es','it': 'it','ja': 'ja','ko': 'ko','ru': 'ru','zh-cn': 'zh-hans','zh-tw': 'zh-hant','pt': 'pt','ar': 'ar','hi': 'hi',# 添加更多语言如有需要
}# Google Translator 语言代码映射
google_language_mapping = {'en': 'en','fr': 'fr','de': 'de','es': 'es','it': 'it','ja': 'ja','ko': 'ko','ru': 'ru','zh-cn': 'zh-CN',  # 修正为 Google 支持的简体中文代码'zh-tw': 'zh-TW',  # 修正为 Google 支持的繁体中文代码'zh': 'zh-CN',  # 默认简体中文'pt': 'pt','ar': 'ar','hi': 'hi',# 添加更多语言如有需要
}ChatGPT_language_mapping = {'en': 'English','ja': 'Japanese','zh-cn': 'Simplified Chinese ','zh-tw': 'Traditional Chinese',
}# 初始化 Flask 应用
app = Flask(__name__)
app.config['ALLOWED_EXTENSIONS'] = {'pdf'}
app.config['UPLOAD_FOLDER'] = 'uploads'
app.config['MAX_CONTENT_LENGTH'] = 50 * 1024 * 1024  # 50MB# 确保上传文件夹存在
os.makedirs(app.config['UPLOAD_FOLDER'], exist_ok=True)# 全局变量
progress = defaultdict(int)
results = {}
progress_lock = Lock()# 设置日志 格式
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')# 读取配置文件
config = configparser.ConfigParser()
config_file = 'config.ini'if not os.path.exists(config_file):raise FileNotFoundError(f"配置文件 {config_file} 未找到,请确保其存在并包含必要的配置。")config.read(config_file)try:AZURE_API_KEY = config.get('translator', 'azure_api_key') # Microsoft Azure 需要KEY, 它给了2个,可以循环使用。用一个就行。AZURE_REGION = config.get('translator', 'azure_region') # 还需要 copied: This is the location (or region) of your resource. You may need to use this field when making calls to this API.OPENAI_API_KEY = config.get('translator', 'openai_api_key') # 使用openAI API# 如果有其他 API 密钥,例如 Yandex,可以在此添加# YANDEX_API_KEY = config.get('translator', 'yandex_api_key') 这个是俄国的google,而且是收费的,不想花1分钱给俄罗斯。
except (configparser.NoSectionError, configparser.NoOptionError):raise ValueError("配置文件中缺少必要的配置选项。")# 设置 OpenAI API 密钥
# Initialize the OpenAI clientclient = OpenAI(api_key=OPENAI_API_KEY)# 允许的文件类型检查函数
def allowed_file(filename):return '.' in filename and filename.rsplit('.', 1)[1] in app.config['ALLOWED_EXTENSIONS']# OCR 函数,指定语言
def ocr_image(image, lang='eng'):try:text = pytesseract.image_to_string(image, lang=lang)except Exception as e:logging.error(f"OCR 失败: {e}")text = ''return textdef chinese_sentence_split(text):# 使用 jieba 进行分词并辅助分句sentences = []current_sentence = []for word in jieba.cut(text):current_sentence.append(word)if word in ['。', '!', '?', ';']:sentence = ''.join(current_sentence).strip()if sentence:sentences.append(sentence)current_sentence = []if current_sentence:sentence = ''.join(current_sentence).strip()if sentence:sentences.append(sentence)return sentencesdef japanese_sentence_split(text):# 使用 Janome 进行分词,并按标点符号分割tokenizer = Tokenizer()tokens = tokenizer.tokenize(text, wakati=True)sentences = []current_sentence = []for token in tokens:current_sentence.append(token)if token in ['。', '!', '?']:sentence = ''.join(current_sentence).strip()if sentence:sentences.append(sentence)current_sentence = []if current_sentence:sentence = ''.join(current_sentence).strip()if sentence:sentences.append(sentence)return sentencesdef process_with_chatgpt(prompt, max_tokens=1500):if len(prompt.split()) > 3000:  # Or use a more sophisticated tokenizer to count tokenslogging.error("Prompt is too long to process with the given token limit")return "Prompt too long"try:      response = client.chat.completions.create(model="gpt-3.5-turbo",messages=[{"role": "system", "content": "You are a helpful assistant."},{"role": "user","content": prompt}],#max_tokens=max_tokens#n=1,#stop=None,#temperature=0.7)return response.choices[0].message.content.strip() #Err response.choices[0].message['content'].strip().split("\n\n")except Exception as e:logging.error(f"ChatGPT 处理失败: {e}")return "ChatGPT 处理失败。"@lru_cache(maxsize=512) #缓存常见的翻译请求,减少重复的 API 调用
def cached_translate(text, enginet, source_lang, target_lang):return translate_text(text, enginet, source_lang, target_lang)# 翻译文本函数,支持分段、并行、进度更新、重试和缓存
def translate_text(text, enginet, progress_callback=None, text_lang='en', target_language='en'):global google_language_mappingglobal microsoft_language_mappingglobal ChatGPT_language_mappinglogging.info(f"翻译引擎参数: {enginet}")# 句子分割nltk_lang = language_mapping.get(text_lang, 'english')if nltk_lang in ['english', 'french', 'german', 'spanish', 'italian', 'russian']:try:sentences = nltk.sent_tokenize(text, language=nltk_lang)except Exception as e:logging.error(f"NLTK 分句失败,使用默认分割方法:{e}")sentences = text.split('\n')elif nltk_lang == 'chinese':sentences = chinese_sentence_split(text)elif nltk_lang == 'japanese':sentences = japanese_sentence_split(text)else:sentences = text.split('\n')# 根据翻译引擎设置最大字符长度if enginet == 'google':max_length = random.randint(4200, 4700)elif enginet == 'chatgpt':max_length = 2000  # OpenAI 对单个请求的 token 数有上限,具体取决于模型else: max_length = 5000# 确保 target_language 已被正确设置if not target_language:logging.error("未能正确设置目标语言,使用默认值 'en'")target_language = 'en'# 初始化翻译器translator = Nonesource_language = Noneif enginet == 'google':target_language = google_language_mapping.get(target_language, 'en')  # 使用正确的目标语言translator = GoogleTranslator(source='auto', target=target_language)logging.info(f"初始化翻译器, google Target_language: {target_language}")elif enginet == 'microsoft':# 使用用户提供的目标语言代码进行翻译source_language = microsoft_language_mapping.get(text_lang, 'en')target_language = microsoft_language_mapping.get(target_language, 'en')logging.info(f"初始化翻译器, Azure Source Language: {source_language}, Target Language: {target_language}")translator = MicrosoftTranslator(source=source_language,target=target_language,api_key=AZURE_API_KEY,region=AZURE_REGION)elif enginet == 'chatgpt':target_language=ChatGPT_language_mapping.get(target_language)logging.info(f"初始化翻译器, ChatGPT 使用模型: gpt-3.5-turbo, Target Language: {target_language}")else:raise ValueError(f"没有选择翻译引擎或引擎不工作:{enginet}")# 将句子组合成不超过最大长度的块chunks = []current_chunk = ''for sentence in sentences:if len(current_chunk) + len(sentence) + 1 <= max_length:current_chunk += sentence + ' 'else:chunks.append(current_chunk.strip())current_chunk = sentence + ' 'if current_chunk:chunks.append(current_chunk.strip())translated_chunks = [''] * len(chunks)total_chunks = len(chunks)completed_chunks = 0# 定义翻译单个块的函数,带有重试机制def translate_chunk(index, chunk):nonlocal completed_chunksmax_retries = 3for attempt in range(max_retries):try:if enginet == 'google':translated_chunk = translator.translate(chunk)elif enginet == 'microsoft':translated_chunk = translator.translate(chunk)elif enginet == 'chatgpt': #为 ChatGPT 构建适当的提示chatgpt_prompt = f"translate to {target_language}:\n\n{chunk}"#logging.info(f"chatgpt_prompt:{chatgpt_prompt}")translated_chunk = process_with_chatgpt(prompt=chatgpt_prompt#max_tokens=1500 #根据需要调整)else:translated_chunk = f"翻译引擎不支持:{enginet}"translated_chunks[index] = translated_chunkbreak  # 成功后跳出循环except Exception as e:logging.error(f"翻译块 {index} 失败,尝试次数 {attempt + 1}: {e}")if attempt == max_retries - 1:translated_chunks[index] = chunk  # 最后一次重试失败,使用原文#with progress_lock:completed_chunks += 1if progress_callback:progress = int(100 * completed_chunks / total_chunks)progress_callback(progress)# 使用线程池并行翻译with ThreadPoolExecutor(max_workers=5) as executor:for idx, chunk in enumerate(chunks):executor.submit(translate_chunk, idx, chunk)# 重建翻译后的文本translated_text = ' '.join(translated_chunks)return translated_text.strip()# 后台处理函数 
# 使用 logging.info 在调试模式中输出所使用的翻译引擎和处理时间 
# 在任务开始时,记录开始时间 start_time。
# 在任务结束时,记录结束时间 end_time,计算处理时间 elapsed_time。
# 将 elapsed_time 保存到 results 字典中,以便在结果页面显示
# 加入对pdf file checking. 如果不是Image,跳过OCR. 9oct.24 1230am
def process_file(task_id, filepath, enginet, ocr_language, target_language):global resultsglobal language_mapping  # 声明使用全局变量try:start_time = time.time()  # 记录开始时间logging.info(f"任务 {task_id}: 开始处理文件 {filepath},使用 OCR 语言 {ocr_language},翻译引擎 {enginet}, 目标语言 {target_language}"),  # 输出详细信息with progress_lock:progress[task_id] = 0# 尝试直接提取文本extracted_text = ''try:extracted_text = pdf_extract_text(filepath)if extracted_text.strip():logging.info(f"任务 {task_id}: 成功提取文本,无需 OCR")with progress_lock:progress[task_id] = 90  # 文本提取完成,进度更新为 90%# 在提取文本后,检测语言try:detected_language = detect(extracted_text)logging.info(f"检测到的文本语言:{detected_language}")if detected_language not in language_mapping:logging.warning(f"检测到的语言 '{detected_language}' 不在支持的语言列表中,使用默认语言 'en'")detected_language = 'en'except Exception as e:logging.error(f"语言检测失败,使用默认语言 'en'。错误信息:{e}")detected_language = 'en'else:logging.info(f"任务 {task_id}: 提取到的文本为空,使用 OCR 处理")raise ValueError("Empty text extracted")except Exception as e:  # 如果直接提取文本失败,使用 OCR 处理logging.info(f"任务 {task_id}: 无法直接提取文本,将使用 OCR 处理。原因:{e}")# 将 PDF 转换为图像try:images = convert_from_path(filepath)except PDFSyntaxError as e:logging.error(f"PDF 解析错误: {e}")raise ValueError("PDF 解析错误,无法转换为图像。")total_pages = len(images)total_steps = total_pagesextracted_text = ''for i, image in enumerate(images):text = ocr_image(image, lang=ocr_language_mapping.get(ocr_language,'eng'))extracted_text += text + '\n'with progress_lock:progress[task_id] = int(100 * (i + 1) / total_steps * 0.9)  # OCR 占 90% 进度with progress_lock:progress[task_id] = 90  # OCR 完成,进度更新为 90%# 在 OCR 提取后,检测语言try:detected_language = detect(extracted_text)logging.info(f"检测到的文本语言:{detected_language}")if detected_language not in language_mapping:logging.warning(f"检测到的语言 '{detected_language}' 不在支持的语言列表中,使用默认语言 'en'")detected_language = 'en'except Exception as e:logging.error(f"语言检测失败,使用默认语言 'en'。错误信息:{e}")detected_language = 'en'# 翻译文本,传递 progress_callbackdef progress_callback(p):with progress_lock:progress[task_id] = 90 + int(p * 0.1)  # 翻译占 10% 进度# 将检测到的语言传递给 translate_text 函数,并确保 enginet 是小写translated_text = translate_text(extracted_text, enginet.lower(), progress_callback, detected_language, target_language)# 使用 ChatGPT 对翻译后的文本进行处理#chatgpt_prompt = f"请为以下翻译后的文本生成一个精简摘要:\n\n{translated_text}"#chatgpt_summary = process_with_chatgpt(chatgpt_prompt)with progress_lock:progress[task_id] = 100# 计算处理时间end_time = time.time()elapsed_time = end_time - start_time  # 处理所用的时间,单位为秒# 将处理时间保存到结果中result = {'original': extracted_text,'translated': translated_text,#'chatgpt_summary': chatgpt_summary,'elapsed_time': elapsed_time,  # 添加处理时间'enginet': enginet,           # 添加翻译引擎'ocr_language': ocr_language,        # 添加 OCR 语言'target_language': target_language}results[task_id] = result# 删除上传的文件os.remove(filepath)logging.info(f"任务 {task_id}: 处理完成,耗时 {elapsed_time:.2f} 秒")  # 输出处理时间except Exception as e:logging.error(f"An unexpected error occurred 处理失败: {e}")with progress_lock:progress[task_id] = -1finally:# 确保上传的文件被删除,即使出现异常if os.path.exists(filepath):os.remove(filepath)logging.info(f"任务 {task_id}: 文件已删除")# 文件上传路由
@app.route('/', methods=['GET', 'POST'])
def upload_file():if request.method == 'POST':# 检查请求中是否有文件if 'file' not in request.files:return '请求中没有文件部分', 400file = request.files['file']if file.filename == '':return '未选择文件', 400if file and allowed_file(file.filename):# 安全地保存文件filename = secure_filename(f"{uuid.uuid4().hex}_{file.filename}")filepath = os.path.join(app.config['UPLOAD_FOLDER'], filename)file.save(filepath)# 获取选择的翻译引擎和 OCR 语言,设置默认值enginet = request.form.get('enginet', 'google')ocr_language = request.form.get('ocr_language', 'en')target_language = request.form.get('target_language')# 创建唯一的任务 IDtask_id = str(uuid.uuid4())progress[task_id] = 0# 启动后台处理线程thread = Thread(target=process_file, args=(task_id, filepath, enginet, ocr_language, target_language))thread.start()# 重定向到进度页面return redirect(url_for('processing', task_id=task_id))else:return '文件类型不被允许', 400return render_template('upload.html')# 处理页面路由
@app.route('/processing/<task_id>')
def processing(task_id):return render_template('processing.html', task_id=task_id)# 进度更新路由
@app.route('/progress/<task_id>')
def progress_status(task_id):def generate():while True:with progress_lock:status = progress.get(task_id, 0)yield f"data: {status}\n\n"if status >= 100 or status == -1:breakreturn Response(generate(), mimetype='text/event-stream')# 结果页面路由
@app.route('/result/<task_id>')
def result(task_id):result_data = results.get(task_id)if not result_data:return '结果未找到', 404# 获取处理时间elapsed_time = result_data.get('elapsed_time', 0)# 将处理时间格式化为 HH:MM:SSelapsed_time_str = str(timedelta(seconds=int(elapsed_time)))return render_template('result.html', original=result_data['original'], translated=result_data['translated'], #chatgpt_summary=result_data['chatgpt_summary'],elapsed_time=elapsed_time_str,enginet=result_data['enginet'],ocr_language=result_data['ocr_language'])if __name__ == '__main__':app.run(host='0.0.0.0', port=9006, debug=True)

2. templates 下面的文件

1. upload.html

<!-- templates/upload.html -->
<!DOCTYPE html>
<html lang="zh-CN">
<head><meta charset="UTF-8"><title>PDF翻译器</title>
</head>
<body><h1>上传PDF文件进行翻译</h1><form action="{{ url_for('upload_file') }}" method="post" enctype="multipart/form-data"><div><label for="file">选择PDF文件:</label><input type="file" id="file" name="file" accept=".pdf" required></div><div><label for="ocr_language">选择OCR语言:</label><select id="ocr_language" name="ocr_language"><option value="en">英语</option><option value="fr">法语</option><option value="de">德语</option><option value="es">西班牙语</option><option value="it">意大利语</option><option value="ja">日语</option><option value="ko">韩语</option><option value="ru">俄语</option><option value="zh-cn">简体中文</option><option value="zh-tw">繁体中文</option><!-- 如需更多语言,请在此添加 --></select></div><div><label for="enginet">选择翻译引擎:</label><select id="enginet" name="enginet"><option value="google">Google 翻译</option><option value="microsoft">Microsoft 翻译</option><option value="chatgpt">ChatGPT 翻译</option><!-- 如有其他翻译引擎,可在此添加 --></select><label for="target_language">选择目标语言:</label><select id="target_language" name="target_language"><option value="zh-cn">简体中文</option><option value="zh-tw">繁体中文(中华)</option><option value="ja">日语</option><!-- 其他语言选项 --></select></div><div><button type="submit">开始翻译</button></div></form>
</body>
</html>

added on 11oct.24 1240am

"<option value="ja">日语</option>" ,可以得到 日语翻译。

你还可以加入其它语言 同上,如果不成功,会是字库映射表需要修改。 映射表格式:左 为变量"target-language" , 右 为翻译机“语言代码”。

2. processing.html

<!-- templates/processing.html --><!doctype html>
<html>
<head><title>处理中...</title><style>#progress-bar {width: 50%;background-color: #f3f3f3;margin: 20px 0;}#progress-bar-fill {height: 30px;width: 0%;background-color: #4caf50;text-align: center;line-height: 30px;color: white;}</style>
</head>
<body><h1>文件正在处理中,请稍候...</h1><div id="progress-bar"><div id="progress-bar-fill">0%</div></div><script>var taskId = "{{ task_id }}";var progressBarFill = document.getElementById('progress-bar-fill');var eventSource = new EventSource('/progress/' + taskId);eventSource.onmessage = function(event) {var progress = event.data;if (progress == '-1') {<!-- alert('处理失败,请重试。'); -->eventSource.close();window.location.href = '/';} else {progressBarFill.style.width = progress + '%';progressBarFill.innerText = progress + '%';if (progress >= 100) {eventSource.close();window.location.href = '/result/' + taskId;}}};</script>
</body>
</html>

3. result.html

<!-- templates/result.html -->
<!doctype html>
<html>
<head><title>翻译结果</title><style>.container {display: flex;}.content {width: 50%;padding: 20px;box-sizing: border-box;overflow-y: scroll;height: 80vh;  /* 调整高度,给处理时间留出空间 */}.original {background-color: #f9f9f9;}.translated {background-color: #eef9f1;}pre {white-space: pre-wrap;word-wrap: break-word;}</style>
</head>
<body><h1>翻译结果</h1><p>处理时间:{{ elapsed_time }}</p>  <!-- 显示处理时间 --><p>使用的翻译引擎:{{ enginet|capitalize }}</p>   <!-- 显示翻译引擎 , 使用capitalize过滤器 首字母大字--><p>OCR 语言:{{ ocr_language }}</p>      <!-- 显示OCR 语言 --><!-- <h2>ChatGPT 生成的摘要</h2>--><!--<pre>{{ chatgpt_summary }}</pre>--><!-- CHANGE: 添加下载译文的功能 --><button onclick="downloadTranslatedText()">下载译文</button><button onclick="window.location.href='/'">返回主页</button><div class="container"><div class="content original"><h2>原文</h2><pre>{{ original }}</pre></div><div class="content translated"><h2>译文</h2><pre>{{ translated }}</pre></div></div><script>function downloadTranslatedText() {var element = document.createElement('a');var text = `{{ translated|e }}`;var file = new Blob([text], {type: 'text/plain'});element.href = URL.createObjectURL(file);element.download = 'translated.txt';document.body.appendChild(element);element.click();document.body.removeChild(element);}</script>
</body>
</html>

3. config.ini

[translator]
azure_api_key = 5abb1ab..
azure_region = south..
openai_api_key = sk-proj..9KrfsMyI30Am3..

4. Dockerfile

我重写了Dockerfile, 精简了NLTK,并加入ocr训练文件(中日英)

# 使用官方的 Python 3.12.3 slim 版本作为基础镜像
FROM python:3.12.3-slim# 设置环境变量
ENV PYTHONDONTWRITEBYTECODE=1
ENV PYTHONUNBUFFERED=1# 设置工作目录   #从P8开始,项目文件在container中位置: /app/<project name>
WORKDIR /app/pdf2tx-mm# 复制应用程序代码到容器中  #从P8开始,项目文件在container中位置: /app/<project name>
COPY . /app/pdf2tx-mm# 升级 pip
RUN pip install --upgrade pip# 安装系统依赖项
RUN apt-get update && apt-get install -y --no-install-recommends \build-essential \tesseract-ocr \libtesseract-dev \poppler-utils \libglib2.0-0 \&& rm -rf /var/lib/apt/lists/*# 如果需要特定的 Tesseract 语言包,中文 日文
RUN apt-get update && apt-get install -y --no-install-recommends \tesseract-ocr-chi-sim \tesseract-ocr-chi-tra \tesseract-ocr-jpn\&& rm -rf /var/lib/apt/lists/*# 安装 Python 依赖项
RUN pip install --no-cache-dir -r requirements.txt# 下载 NLTK 数据
RUN python -m nltk.downloader punkt punkt_tab popular# Copy the rest of the application code
COPY ./swap/* /usr/share/tesseract-ocr/5/tessdata/# 暴露应用程序运行的端口
EXPOSE 9006# 设置环境变量以指定Flask运行的主机和端口
ENV FLASK_RUN_HOST=0.0.0.0
ENV FLASK_RUN_PORT=9006# 运行应用程序
CMD ["python", "app.py"]

5 requiretments.txt

Flask
pdf2image
pytesseract
deep_translator
nltk
pdfminer.six
langdetect
jieba
janome
openai
Werkzeug

这一版本加入了openai ,  移除 unstructured 库 发现有用没到。

Docker 部署

运行下面命令:
第一个创建Image: pdf2tx-mm
第二个创建Container: pdf2tx-mm_container  并指定 9006 端口运行

# docker build -t pdf2tx-mm .# docker run -d -p 9006:9006 --name pdf2tx-mm_container pdf2tx-mm

测试结果,因为是多进程,加入了cache, 测试使用的是 1 页 image pdf

分析

从结果上看,ocr 2 tx 2 字

1. Google OCR 2 简中: 用时 9.42 秒

2024-10-11 03:57:32,953 - INFO - 任务 80d38634-09fc-4d7f-ba14-2edd1e201dde: 提取到的文本为空,使用 OCR 处理
2024-10-11 03:57:32,953 - INFO - 任务 80d38634-09fc-4d7f-ba14-2edd1e201dde: 无法直接提取文本,将使用 OCR 处理。原因:Empty text extracted
2024-10-11 03:57:33,043 - INFO - 192.168.1.72 - - [11/Oct/2024 03:57:33] "GET /progress/80d38634-09fc-4d7f-ba14-2edd1e201dde HTTP/1.1" 200 -
2024-10-11 03:57:41,106 - INFO - 检测到的文本语言:en
2024-10-11 03:57:41,107 - INFO - 翻译引擎参数: google
2024-10-11 03:57:41,152 - INFO - 初始化翻译器, google Target_language: zh-CN
2024-10-11 03:57:42,359 - INFO - 任务 80d38634-09fc-4d7f-ba14-2edd1e201dde: 处理完成,耗时 9.42 秒

2. Google OCR 2 中华:用时 15.78 秒

2024-10-11 03:58:19,553 - INFO - 任务 b7a6fd8b-af27-4aa5-ab78-fca71480bf43: 提取到的文本为空,使用 OCR 处理
2024-10-11 03:58:19,553 - INFO - 任务 b7a6fd8b-af27-4aa5-ab78-fca71480bf43: 无法直接提取文本,将使用 OCR 处理。原因:Empty text extracted
2024-10-11 03:58:19,627 - INFO - 192.168.1.72 - - [11/Oct/2024 03:58:19] "GET /progress/b7a6fd8b-af27-4aa5-ab78-fca71480bf43 HTTP/1.1" 200 -
2024-10-11 03:58:33,760 - INFO - 检测到的文本语言:en
2024-10-11 03:58:33,761 - INFO - 翻译引擎参数: google
2024-10-11 03:58:33,761 - INFO - 初始化翻译器, google Target_language: zh-TW
2024-10-11 03:58:35,320 - INFO - 任务 b7a6fd8b-af27-4aa5-ab78-fca71480bf43: 处理完成,耗时 15.78 秒

3. Azure OCR 2 简中:用时 7.85 秒

024-10-11 03:58:50,302 - INFO - 任务 9aea6094-f0a2-43a5-97cc-4f5b5a839313: 提取到的文本为空,使用 OCR 处理
2024-10-11 03:58:50,302 - INFO - 任务 9aea6094-f0a2-43a5-97cc-4f5b5a839313: 无法直接提取文本,将使用 OCR 处理。原因:Empty text extracted
2024-10-11 03:58:50,303 - INFO - 192.168.1.72 - - [11/Oct/2024 03:58:50] "GET /processing/9aea6094-f0a2-43a5-97cc-4f5b5a839313 HTTP/1.1" 200 -
2024-10-11 03:58:50,378 - INFO - 192.168.1.72 - - [11/Oct/2024 03:58:50] "GET /progress/9aea6094-f0a2-43a5-97cc-4f5b5a839313 HTTP/1.1" 200 -
2024-10-11 03:58:57,001 - INFO - 检测到的文本语言:en
2024-10-11 03:58:57,001 - INFO - 翻译引擎参数: microsoft
2024-10-11 03:58:57,002 - INFO - 初始化翻译器, Azure Source Language: en, Target Language: zh-hans
2024-10-11 03:58:58,143 - INFO - 任务 9aea6094-f0a2-43a5-97cc-4f5b5a839313: 处理完成,耗时 7.85 秒

4. Azure OCR 2 中华:用时 7.91 秒

2024-10-11 03:59:20,171 - INFO - 任务 329142fd-dcf9-44fc-b5a6-ae5d5bac5a83: 提取到的文本为空,使用 OCR 处理
2024-10-11 03:59:20,171 - INFO - 任务 329142fd-dcf9-44fc-b5a6-ae5d5bac5a83: 无法直接提取文本,将使用 OCR 处理。原因:Empty text extracted
2024-10-11 03:59:20,247 - INFO - 192.168.1.72 - - [11/Oct/2024 03:59:20] "GET /progress/329142fd-dcf9-44fc-b5a6-ae5d5bac5a83 HTTP/1.1" 200 -
2024-10-11 03:59:27,243 - INFO - 检测到的文本语言:en
2024-10-11 03:59:27,243 - INFO - 翻译引擎参数: microsoft
2024-10-11 03:59:27,244 - INFO - 初始化翻译器, Azure Source Language: en, Target Language: zh-hant
2024-10-11 03:59:28,070 - INFO - 任务 329142fd-dcf9-44fc-b5a6-ae5d5bac5a83: 处理完成,耗时 7.91 秒

5. Chatgpt OCR 2 简中:用时 13.31 秒

2024-10-11 03:59:36,856 - INFO - 任务 3a50f668-b497-41e2-b8b1-f9265b5afbfe: 提取到的文本为空,使用 OCR 处理
2024-10-11 03:59:36,856 - INFO - 任务 3a50f668-b497-41e2-b8b1-f9265b5afbfe: 无法直接提取文本,将使用 OCR 处理。原因:Empty text extracted
2024-10-11 03:59:36,859 - INFO - 192.168.1.72 - - [11/Oct/2024 03:59:36] "GET /processing/3a50f668-b497-41e2-b8b1-f9265b5afbfe HTTP/1.1" 200 -
2024-10-11 03:59:36,940 - INFO - 192.168.1.72 - - [11/Oct/2024 03:59:36] "GET /progress/3a50f668-b497-41e2-b8b1-f9265b5afbfe HTTP/1.1" 200 -
2024-10-11 03:59:48,477 - INFO - 检测到的文本语言:en
2024-10-11 03:59:48,477 - INFO - 翻译引擎参数: chatgpt
2024-10-11 03:59:48,478 - INFO - 初始化翻译器, ChatGPT 使用模型: gpt-3.5-turbo, Target Language: Simplified Chinese 
2024-10-11 03:59:50,002 - INFO - HTTP Request: POST https://api.openai.com/v1/chat/completions "HTTP/1.1 200 OK"
2024-10-11 03:59:50,157 - INFO - 任务 3a50f668-b497-41e2-b8b1-f9265b5afbfe: 处理完成,耗时 13.31 秒

6. Chatgpt OCR 2 中华:用时 17.21 秒

2024-10-11 04:00:04,240 - INFO - 任务 dd2474f4-9ab3-40c2-8508-345f2ab4b8fc: 提取到的文本为空,使用 OCR 处理
2024-10-11 04:00:04,241 - INFO - 任务 dd2474f4-9ab3-40c2-8508-345f2ab4b8fc: 无法直接提取文本,将使用 OCR 处理。原因:Empty text extracted
2024-10-11 04:00:04,243 - INFO - 192.168.1.72 - - [11/Oct/2024 04:00:04] "GET /processing/dd2474f4-9ab3-40c2-8508-345f2ab4b8fc HTTP/1.1" 200 -
2024-10-11 04:00:04,376 - INFO - 192.168.1.72 - - [11/Oct/2024 04:00:04] "GET /progress/dd2474f4-9ab3-40c2-8508-345f2ab4b8fc HTTP/1.1" 200 -
2024-10-11 04:00:20,225 - INFO - 检测到的文本语言:en
2024-10-11 04:00:20,225 - INFO - 翻译引擎参数: chatgpt
2024-10-11 04:00:20,226 - INFO - 初始化翻译器, ChatGPT 使用模型: gpt-3.5-turbo, Target Language: Traditional Chinese
2024-10-11 04:00:21,484 - INFO - HTTP Request: POST https://api.openai.com/v1/chat/completions "HTTP/1.1 200 OK"
2024-10-11 04:00:21,493 - INFO - 任务 dd2474f4-9ab3-40c2-8508-345f2ab4b8fc: 处理完成,耗时 17.27 秒

Azure 用的服器虽然距离近,但不会有 10 秒这么夸张。 可见大厂的算力,网络性能是真的强大。

测试用的是相同的 一页 image PDF 文件, 在QNAP TS-453D NAS 上跑的结果。

这个时间与我的 Laptop 用时有 -2 to 2 秒范围,看来 OCR 对硬件要求不高,NAS的 J4125 够用。如果是多页就是核数量上吃亏。翻译机占用的时间,已经改为进度10%.

更新

Modified on 双10.24.11pm  添upload.html processing.html result.html config.ini Dockerfile requiretments.txt 使程序完整。

所有的 Code 按指导部署,程序是以执行,除了 api 

Modified on 双十.24 1141pm 

知识是有价的,但分享是乐趣。 这些最基本的知识,也要去收费,设置门槛浏览,你们吃相很难看。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/56126.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

计量校准工作中的误差评定与不确定度

计量校准的目的是确定测量仪器的误差和不确定度&#xff0c;以评估其测量结果的可靠性。误差评定和不确定度计算是计量校准过程中的重要步骤。 误差评定是指通过比较测量仪器的测量结果与已知标准值之间的差异&#xff0c;确定仪器的准确性和精度。误差可以分为系统误差和随机误…

【Java】I/O 操作详解

&#x1f4c3;个人主页&#xff1a;island1314 ⛺️ 欢迎关注&#xff1a;&#x1f44d;点赞 &#x1f442;&#x1f3fd;留言 &#x1f60d;收藏 &#x1f49e; &#x1f49e; &#x1f49e; 目录 1. 引言 &#x1f680; 2. File 类 &#x1f4d5; 2.1 创建 File 对象 …

wpf实现新用户页面引导

第一步 第二部 部分代码: private void show(int xh, FrameworkElement fe, string con, Visibility vis Visibility.Visible) {Point point fe.TransformToAncestor(Window.GetWindow(fe)).Transform(new Point(0, 0));//获取控件坐标点RectangleGeometry rg new Rectangl…

精益与数字化的融合:制造业的创新之路

回望过去&#xff0c;精益管理作为制造业的瑰宝&#xff0c;以其“消除浪费、持续改进、顾客至上”的核心理念&#xff0c;引领了无数企业走向成功。从丰田生产方式到全球范围内的广泛实践&#xff0c;精益管理不仅提升了生产效率&#xff0c;更重塑了企业的文化和价值观。它教…

3.计算机网络_端口号

端口号的由来 运输层的作用&#xff1a; 在计算机网络中&#xff0c;运输层处在用户功能的最底层、通信部分的最高层的位置&#xff0c;也就是说运输层是用户数据和实际网络通信的桥梁。因此运输层屏蔽了网络的实现部分&#xff0c;以协议的方式向用户层提供了接口&#xff…

文章解读与仿真程序复现思路——电力自动化设备EI\CSCD\北大核心《计及状态量平均超限比的综合能源系统动态能量流双层优化》

本专栏栏目提供文章与程序复现思路&#xff0c;具体已有的论文与论文源程序可翻阅本博主免费的专栏栏目《论文与完整程序》 论文与完整源程序_电网论文源程序的博客-CSDN博客https://blog.csdn.net/liang674027206/category_12531414.html 电网论文源程序-CSDN博客电网论文源…

【千图网-登录_注册安全分析报告】

前言 由于网站注册入口容易被黑客攻击&#xff0c;存在如下安全问题&#xff1a; 1. 暴力破解密码&#xff0c;造成用户信息泄露 2. 短信盗刷的安全问题&#xff0c;影响业务及导致用户投诉 3. 带来经济损失&#xff0c;尤其是后付费客户&#xff0c;风险巨大&#xff0c;造…

【Linux进程间通信】Linux信号机制深度解析:保存与处理技巧

&#x1f4dd;个人主页&#x1f339;&#xff1a;Eternity._ ⏩收录专栏⏪&#xff1a;Linux “ 登神长阶 ” &#x1f339;&#x1f339;期待您的关注 &#x1f339;&#x1f339; ❀Linux进程间通信 &#x1f4d2;1. 信号的保存&#x1f30a;在内核中的表示&#x1f342;sigs…

Python OpenCV精讲系列 - 目标检测与识别深入理解(二十)

&#x1f496;&#x1f496;⚡️⚡️专栏&#xff1a;Python OpenCV精讲⚡️⚡️&#x1f496;&#x1f496; 本专栏聚焦于Python结合OpenCV库进行计算机视觉开发的专业教程。通过系统化的课程设计&#xff0c;从基础概念入手&#xff0c;逐步深入到图像处理、特征检测、物体识…

【QT】常用控件(一)

个人主页~ 常用控件 一、控件是什么二、QWidget核心属性1、enabled2、geometry3、windowTitle4、windowIcon5、windowOpacity6、cursor7、font8、toolTip9、focusPolicy10、styleSheet 一、控件是什么 ui设计界面左边的这些都叫控件&#xff0c;除了这些以外&#xff0c;QT还允…

竹壳天气时钟(二)第二阶段任务已完成

一、简介 准备用基于esp8266的nodemcu开发板做一个天气时钟。 一步一步记录代码编写过程。 竹壳天气时钟 Bamboo shell weather clock 使用基于esp8266的NodeMCU制作。 计划用竹子做最后成品的外壳&#xff0c;所以才有了这个名称。 第一阶段任务&#xff1a; 1.开启混合模式&…

2025推荐选题|基于MVC的农业病虫害防治平台的设计与实现

作者简介&#xff1a;Java领域优质创作者、CSDN博客专家 、CSDN内容合伙人、掘金特邀作者、阿里云博客专家、51CTO特邀作者、多年架构师设计经验、多年校企合作经验&#xff0c;被多个学校常年聘为校外企业导师&#xff0c;指导学生毕业设计并参与学生毕业答辩指导&#xff0c;…

Golang | Leetcode Golang题解之第477题汉明距离总和

题目&#xff1a; 题解&#xff1a; func totalHammingDistance(nums []int) (ans int) {n : len(nums)for i : 0; i < 30; i {c : 0for _, val : range nums {c val >> i & 1}ans c * (n - c)}return }

tkinter库的应用小示例:文本编辑器

tkinter库的应用小示例&#xff1a;文本编辑器 要 求&#xff1a; 创建一个文本编辑器&#xff0c;功能包括&#xff0c;创建、打开、编辑、保存文件。一个Button小组件&#xff0c;命名为btn_open,用于打开要编辑的文件&#xff0c;一个Button小组件&#xff0c;命名为btn_s…

【Ubuntu】“Linux版PhotoShop”绘图软件的安装和汉化

【Ubuntu】“Linux版PhotoShop”绘图软件的安装和汉化 零、前言 最近换了Linux系统&#xff0c;但是写教程做PPT的时候还是得用到绘图软件&#xff0c;上网一查&#xff0c;总结对比之后发现Krita比较好用&#xff0c;故此讲解一下如何安装和汉化Krita。 壹、安装 安装很简…

Unity中搜索不到XR Interaction Toolkit包解决方法

问题&#xff1a; 针对Unity版本2020.3在中PackageManager可能搜素不到XR Interaction Toolkit包 在Package Manager中未显示XR Interaction Toolkit包 解决方法&#xff1a; Package manager左上角&#xff0c;点加号&#xff0c;选择 Add package from git URL..&#xff0c;…

Mysql(2)—SQL语法详解(通俗易懂)

一、关于SQL 1.1 简介 SQL&#xff08;Structured Query Language&#xff0c;结构化查询语言&#xff09;是一种用于管理关系型数据库的标准编程语言。它主要用于数据的查询、插入、更新和删除等操作。SQL最初在1970年代由IBM的研究人员开发&#xff0c;旨在处理关系数据模型…

Pytorch基础:设置随机种子

相关阅读 Pytorch基础https://blog.csdn.net/weixin_45791458/category_12457644.html?spm1001.2014.3001.5482 有时候&#xff0c;如果需要代码在多个运行中具有可重复性&#xff0c;可以通过以下方式来设置随机种子&#xff1a; import torch import numpy as np import r…

qt+opengl 实现纹理贴图,平移旋转,绘制三角形,方形

1 首先qt 已经封装了opengl&#xff0c;那么我们就可以直接用了&#xff0c;这里面有三个函数需要继承 virtual void initializeGL() override; virtual void resizeGL(int w,int h) override; virtual void paintGL() override; 这三个函数是实现opengl的重要函数。 2 我们…

E: Unable to locate package:无法定位包的完美解决方法 ️

博主 默语带您 Go to New World. ✍ 个人主页—— 默语 的博客&#x1f466;&#x1f3fb; 《java 面试题大全》 《java 专栏》 &#x1f369;惟余辈才疏学浅&#xff0c;临摹之作或有不妥之处&#xff0c;还请读者海涵指正。☕&#x1f36d; 《MYSQL从入门到精通》数据库是开…