PyQt5内嵌浏览器注入JavaScript脚本实现自动化操作的代码实例


Posted in Python onFebruary 13, 2019

概要

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

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

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

python 代码示例

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
'''使用 PyQt5 内嵌浏览器浏览网页,并注入 Javascript 脚本实现自动化操作。'''
import os
import sys
from datetime import datetime
from PyQt5.QtWidgets import (
  QWidget, QApplication, QVBoxLayout, QHBoxLayout,
  QDesktopWidget, QTextEdit, QLabel, QLineEdit, QPushButton,
  QFileDialog, QProgressBar,
)
from PyQt5.QtCore import QUrl, pyqtSlot
from PyQt5.QtWebEngineWidgets import QWebEngineView, QWebEngineProfile, QWebEngineScript, QWebEnginePage
class 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);
}

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,谢谢大家对三水点靠木的支持。如果你想了解更多相关内容请查看下面相关链接

Python 相关文章推荐
Python和php通信乱码问题解决方法
Apr 15 Python
Python开发常用的一些开源Package分享
Feb 14 Python
python根据给定文件返回文件名和扩展名的方法
Mar 27 Python
Python找出list中最常出现元素的方法
Jun 14 Python
基于python(urlparse)模板的使用方法总结
Oct 13 Python
基于python中staticmethod和classmethod的区别(详解)
Oct 24 Python
Python实现读取字符串按列分配后按行输出示例
Apr 17 Python
python中for用来遍历range函数的方法
Jun 08 Python
DataFrame.to_excel多次写入不同Sheet的实例
Dec 02 Python
python实现npy格式文件转换为txt文件操作
Jul 01 Python
Python爬虫分析微博热搜关键词的实现代码
Feb 22 Python
Python面向对象之成员相关知识总结
Jun 24 Python
Python实现Event回调机制的方法
Feb 13 #Python
Python socket实现多对多全双工通信的方法
Feb 13 #Python
对python文件读写的缓冲行为详解
Feb 13 #Python
python单线程文件传输的实例(C/S)
Feb 13 #Python
Python 实现文件打包、上传与校验的方法
Feb 13 #Python
使用python3构建文件传输的方法
Feb 13 #Python
对python 自定义协议的方法详解
Feb 13 #Python
You might like
php文本转图片自动换行的方法
2013/03/13 PHP
PHP getName()函数讲解
2019/02/03 PHP
Yii2框架控制器、路由、Url生成操作示例
2019/05/27 PHP
php面向对象程序设计入门教程
2019/06/22 PHP
jQuery:节点(插入,复制,替换,删除)操作
2013/03/04 Javascript
将nodejs打包工具整合到鼠标右键的方法
2013/05/11 NodeJs
JS匀速运动演示示例代码
2013/11/26 Javascript
jquery常用特效方法使用示例
2014/04/25 Javascript
Node.js开发之访问Redis数据库教程
2015/01/14 Javascript
详解JavaScript对W3C DOM模版的支持情况
2015/06/16 Javascript
详解webpack3如何正确引用并使用jQuery库
2017/08/26 jQuery
nodejs读取图片返回给浏览器显示
2019/07/25 NodeJs
处理JavaScript值为undefined的7个小技巧
2020/07/28 Javascript
Vue实现一种简单的无限循环滚动动画的示例
2021/01/10 Vue.js
在Python的列表中利用remove()方法删除元素的教程
2015/05/21 Python
Python 网页解析HTMLParse的实例详解
2017/08/10 Python
分享一下Python数据分析常用的8款工具
2018/04/29 Python
解决Pycharm出现的部分快捷键无效问题
2018/10/22 Python
python批量赋值操作实例
2018/10/22 Python
使用Python在Windows下获取USB PID&VID的方法
2019/07/02 Python
python如何停止递归
2020/09/09 Python
基于python模拟TCP3次握手连接及发送数据
2020/11/06 Python
Python实现树莓派摄像头持续录像并传送到主机的步骤
2020/11/30 Python
html5指南-1.html5全局属性(html5 global attributes)深入理解
2013/01/07 HTML / CSS
HTML5中使用json对象的实例代码
2018/09/10 HTML / CSS
智能旅行箱:Horizn Studios
2018/04/30 全球购物
委托与事件是什么关系?为什么要使用委托
2014/04/18 面试题
大学本科毕业生求职简历的自我评价
2013/10/09 职场文书
大学生物业管理求职信
2013/10/24 职场文书
园艺师求职信
2014/03/10 职场文书
工程建设实施方案
2014/03/14 职场文书
中央空调节能方案
2014/06/15 职场文书
2015年学校教务处工作总结
2015/05/11 职场文书
回复函格式及范文
2015/07/14 职场文书
同学会演讲稿
2019/04/02 职场文书
SQLServer2019 数据库环境搭建与使用的实现
2021/04/08 SQL Server