概要
应同学邀请,演示如何应用 PyQt5 内嵌浏览器浏览网页,并注入 Javascript 脚本实现自动化操作。

sg 原贴地址: 如何在Python利用runJavaScript模仿鼠标挪动页面的某个元素
https://segmentfault.com/q/10...

上面测试的是一个便宜机票预订网站(http://www.flyscoot.com/),关键点如下

1.应用 QWebEngineView 加载网页,并显示进度。
2.在默认配置(QWebEngineProfile)中植入 Javascript 内容,这样脚本会在所有关上的网页中执行,不管跳转到哪个网址。
3.Javascript 脚本应用网址中的路径名,判断以后网页地位,从而决定执行哪种操作。

python 代码示例

#!/usr/bin/env python3# -*- coding: utf-8 -*-'''应用 PyQt5 内嵌浏览器浏览网页,并注入 Javascript 脚本实现自动化操作。'''import osimport sysfrom datetime import datetimefrom PyQt5.QtWidgets import (    QWidget, QApplication, QVBoxLayout, QHBoxLayout,    QDesktopWidget, QTextEdit, QLabel, QLineEdit, QPushButton,    QFileDialog, QProgressBar,)from PyQt5.QtCore import QUrl, pyqtSlotfrom PyQt5.QtWebEngineWidgets import QWebEngineView, QWebEngineProfile, QWebEngineScript, QWebEnginePageclass Browser(QWidget):    def __init__(self):        super().__init__()        self.init_ui()        # 脚本        self.profile = QWebEngineProfile.defaultProfile()        self.script = QWebEngineScript()        self.prepare_script()    def init_ui(self):        self.webView = QWebEngineView()        self.logEdit = QTextEdit()        self.logEdit.setFixedHeight(100)        self.addrEdit = QLineEdit()        self.addrEdit.returnPressed.connect(self.load_url)        self.webView.urlChanged.connect(            lambda i: self.addrEdit.setText(i.toDisplayString()))        self.jsEdit = QLineEdit()        self.jsEdit.setText('inject.js')        loadUrlBtn = QPushButton('加载')        loadUrlBtn.clicked.connect(self.load_url)        chooseJsBtn = QPushButton('抉择脚本文件')        chooseJsBtn.clicked.connect(self.choose_js_file)        # 导航/工具        top = QWidget()        top.setFixedHeight(80)        topBox = QVBoxLayout(top)        topBox.setSpacing(0)        topBox.setContentsMargins(5, 0, 0, 5)        progBar = QProgressBar()        progBox = QHBoxLayout()        progBox.addWidget(progBar)        topBox.addLayout(progBox)        naviBox = QHBoxLayout()        naviBox.addWidget(QLabel('网址'))        naviBox.addWidget(self.addrEdit)        naviBox.addWidget(loadUrlBtn)        topBox.addLayout(naviBox)        naviBox = QHBoxLayout()        naviBox.addWidget(QLabel('注入脚本文件'))        naviBox.addWidget(self.jsEdit)        naviBox.addWidget(chooseJsBtn)        topBox.addLayout(naviBox)        self.webView.loadProgress.connect(progBar.setValue)        # 主界面        layout = QVBoxLayout(self)        layout.addWidget(self.webView)        layout.addWidget(top)        layout.addWidget(self.logEdit)        self.show()        self.resize(1024, 900)        self.center()    def center(self):        qr = self.frameGeometry()        cp = QDesktopWidget().availableGeometry().center()        qr.moveCenter(cp)        self.move(qr.topLeft())    @pyqtSlot()    def load_url(self):        url = self.addrEdit.text().strip()        if not url.lower().startswith('http://') \                and not url.lower().startswith('https://'):            url = 'http://{}'.format(url)        self.load(url)    @pyqtSlot()    def choose_js_file(self):        f, _ = QFileDialog.getOpenFileName(filter="Javascript files(*.js)")        if os.path.isfile(f):            self.jsEdit.setText(f)            self.prepare_script()    def prepare_script(self):        path = self.jsEdit.text().strip()        if not os.path.isfile(path):            self.log('invalid js path')            return        self.profile.scripts().remove(self.script)        with open(path, 'r') as f:            self.script.setSourceCode(f.read())        self.profile.scripts().insert(self.script)        self.log('injected js ready')    def log(self, msg, *args, **kwargs):        m = msg.format(*args, **kwargs)        self.logEdit.append('{} {}'.format(            datetime.now().strftime('%H:%M:%S'), m))    def load(self, url):        self.log(f'loading {url}')        self.addrEdit.setText(url)        self.webView.load(QUrl(url))if __name__ == '__main__':    app = QApplication(sys.argv)    b = Browser()    b.load('http://www.flyscoot.com/')    sys.exit(app.exec_())

Javascript 脚本示例

// 简略起见,这里只演示局部页面,脚本内容摘自 Heng丶原贴文。function handle(path) {    // 首页    if (path == '/zh') {        document.getElementsByClassName('radio-inline')[1].click();        document.getElementById('oneway_from').value='广州 (CAN)';        document.getElementById('oneway_to').value='新加坡 (SIN)';        document.getElementById('oneway_departuredate').value='2018年9月10日';        document.getElementsByClassName('btn--booking')[1].click();        return;    }    // 抉择航班    if (path == '/Book/Flight') {        document.getElementsByClassName('price--sale')[0].click();        document.getElementsByClassName('heading-4')[0].click();        document.getElementsByClassName('btn-submit')[0].click();        return;    }    // 乘客信息    if (path == '/BookFlight/Passengers') {        document.getElementsByClassName('fname1')[0].value = "匿名";    }}let host = document.location.hostname;if (host.endsWith('.flyscoot.com')) {    handle(document.location.pathname);}