【桌面软件开发案例】用Python的PySide6开发聚合翻译软件


软件特点和优势

1、无需逆向解密、完全模拟人来操作浏览器

2、支持4个翻译网站同时进行翻译(中翻英、英译中)

3、解压即用,无需浏览器驱动

4、图形化界面,更易操作

一、聚合翻译软件效果展示

因为平时我们经常会用到翻译,各网站的翻译效果可能不太一样,有的时候想筛选一个更符合语境的翻译结果,于是我用python的pyside6库开发了一个聚合翻译软件,实现了一键同时获取4个翻译工具网站的结果,包括:

有道翻译、百度翻译、腾讯翻译、360翻译

老规矩!咱们以目标为驱动,先来看下软件的实际效果:

那代码是如何实现PySide6开发聚合翻译工具的了?下面我们来进一步分析网站及代码的实现方式。

二、翻译网站分析

咱们打开浏览器看一下翻译网站的API调用,发现都是加密的,既然是加密的,逆向的话其实会涉及一些风险问题的,那我直接模拟浏览器进行人工操作,既不需要逆向,也很方便的能获取到翻译的结果。

既然知道了原理是使用模拟人去操作浏览器,那咱们接下来开始撸代码

三、python核心代码讲解

首先我们使用Qt设计师(Qt Designer)绘制一个图形界面,这个软件相当好用,实现了UI和我们业务代码的解耦,且很方便的能够转换成python代码导入到程序里。

接着我们运行如下命令将window.ui文件转换成window.py文件

pyside6-uic window.ui -o window.py

然后编写我们的main.py主窗口程序,导入需要用到的包

from PySide6.QtCore import Slot, QSharedMemory
from PySide6.QtWidgets import QApplication, QMainWindow, QMessageBox
from PySide6.QtGui import QIcon, QPixmap
from window import Ui_MainWindow
from concurrent.futures import ThreadPoolExecutor
from threads import TranslateThread
from threading import Thread
import base64
from config import *
from utils import get_edge_path

初始化浏览器参数,并默认开启4个翻译网站的窗口,方便我们后续进行翻译操作。

class MainWindow(QMainWindow, Ui_MainWindow):
    def __init__(self):
        super().__init__()
        self.setupUi(self)  # 初始化 UI
        self.setWindowTitle(f"聚合翻译 {version}(公众号:程序员王哪跑)")

        # 设置图标
        icon_data = base64.b64decode(image_data["fav.png"])
        pixmap = QPixmap()
        pixmap.loadFromData(icon_data)
        icon = QIcon(pixmap)
        self.setWindowIcon(icon)

        self.btn_translate.setText("翻译引擎启动中,请稍后...")
        self.btn_translate.setEnabled(False)
        # 初始化浏览器
        init_thread = Thread(target=self.init_browser)
        init_thread.start()
        # self.init_browser()

        # 绑定事件
        self.btn_translate.clicked.connect(self.translate)
        # 翻译结果
        self.results = {
            '有道': self.txt_translate_youdao,
            '腾讯': self.txt_translate_qq,
            '百度': self.txt_translate_baidu
        }

    def init_browser(self):
        edge_path = get_edge_path()
        if not edge_path:
            edge_path = browser_path

        if not edge_path:
            self.btn_translate.setText("未能自动获取浏览器路径,请在config.ini中配置浏览器路径")

        co = ChromiumOptions()
        co = co.headless()      # 无头模式
        co = co.set_browser_path(rf'{edge_path}')  # 浏览器路径

        self.browser = Chromium(co)  # 创建浏览器对象
        self.browser.set.retry_times(10)  # 设置整体运行参数

        # 同时启动3个窗口
        with ThreadPoolExecutor(3) as executor:
            future1 = executor.submit(self.browser.new_tab, 'https://fanyi.youdao.com/#/TextTranslate', )
            future2 = executor.submit(self.browser.new_tab, 'https://fanyi.qq.com/', )
            future3 = executor.submit(self.browser.new_tab, 'https://fanyi.baidu.com/mtpe-individual/multimodal#/"')
            future4 = executor.submit(self.browser.new_tab, 'https://fanyi.so.com/')

            self.tab_youdao = future1.result()
            self.tab_qq = future2.result()
            self.tab_baidu = future3.result()
            self.tab_360 = future4.result()

            self.btn_translate.setText("翻译")
            self.btn_translate.setEnabled(True)

定义一个thread.py的线程操作文件,在这里我们对几个网站分别进行翻译操作,并获得翻译之后的结果,关键代码如下:

    def translate_youdao(self, tab):
        """
        有道翻译
        :param tab:
        :return:
        """
        self.result.emit("youdao", "正在翻译,请稍后...")
        tab.ele('#js_fanyi_input').input(self.text, clear=True)
        result = tab.ele('#js_fanyi_output_resultOutput', timeout=10).text
        if result:
            result = result.strip()
        # print(f"有道翻译结果: {result}")
        self.result.emit("youdao", result)
        # 清空输入框
        tab.ele('#js_fanyi_input').input("", clear=True)
        return result

    def translate_qq(self, tab):
        """
        腾讯翻译
        :param tab:
        :return:
        """
        self.result.emit("qq", "正在翻译,请稍后...")
        tab.ele('xpath://div[@class="tea-textarea-group"]/textarea').input(self.text, clear=True)

        result = []
        text_eles = tab.eles('.target-text-list', timeout=10)
        for text_ele in text_eles:
            result.append(text_ele.text)

        if result:
            result = "\n".join(result)
        # print(f"腾讯翻译结果: {result}")
        self.result.emit("qq", result)
        # 清空输入框
        tab.ele('xpath://div[@class="tea-textarea-group"]/textarea').input("", clear=True)
        return result

    def translate_baidu(self, tab):
        """
        百度翻译
        :param tab:
        :return:
        """
        self.result.emit("baidu", "正在翻译,请稍后...")
        tab.ele('xpath://div[@role="textbox"]').input(self.text, clear=True)
        result = tab.ele('#trans-selection', timeout=10).text
        if result:
            result = result.strip()
        # print(f"百度翻译结果: {result}")
        self.result.emit("baidu", result)
        # 清空输入框
        tab.ele('xpath://div[@role="textbox"]').input("", clear=True)
        return result

    def translate_bing(self, tab):
        """
        必应翻译
        :param tab:
        :return:
        """
        self.result.emit("bing", "正在翻译,请稍后...")
        tab.ele('#tta_input_ta').input(self.text, clear=True)
        result = ""
        for i in range(10):
            result = tab.ele('#tta_output_ta', timeout=10).text
            if result and result != "...":
                result = result.strip()
                break
            time.sleep(1)
        # print(f"必应翻译结果: {result}")
        self.result.emit("bing", result)
        # 清空输入框
        tab.ele('#tta_input_ta').input("", clear=True)
        return result

    def translate_360(self, tab):
        self.result.emit("360", "正在翻译,请稍后...")
        tab.ele('xpath://textarea[@placeholder="输入文字"]').input(self.text, clear=True)
        # 点击翻译
        tab.ele(".translate").click()
        # 获取结果
        result = ""
        for i in range(10):
            result = tab.ele('xpath://div[@class="result"]/p[@class="content"]', timeout=10).text
            if result and result != "翻译中...":
                result = result.strip()
                break
            time.sleep(1)
        # print(f"360翻译结果: {result}")
        self.result.emit("360", result)
        # 清空输入框
        tab.ele('xpath://textarea[@placeholder="输入文字"]').input("", clear=True)
        return result

translate_youdao()函数,实现有道翻译的逻辑。

translate_qq()函数,实现了腾讯翻译的逻辑。

translate_baidu()函数,实现百度翻译的逻辑。

translate_360()函数,实现360翻译的逻辑。

因为以上几个网站,因为是基于浏览器去操作的,网站本身就支持自动识别语言的,所以能很方便的实现:中译英、英译中的双向翻译哦!

四、为什么不需要驱动就能控制浏览器?

因为现在基于Chromium的浏览器都支持CDP协议来自动化控制浏览器,以前的自动化都是基于浏览器驱动的方式,很容易被一些手段检测到。但是现在浏览器本身就开放了这个CDP协议,所以我们只需要按照协议的约定去操作即可实现自动化。

最关键的是:因为他是用的真实的浏览器操作,所以很难被检测到!

五、获取源码及软件

我是王哪跑,持续更新python实操案例,不断提供有价值的技术和软件!

附完整版源码和软件:(完整版源码下载

获取后,有任何代码问题,均负责讲解答疑,保证正常运行!