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进阶教程之文本文件的读取和写入
Aug 29 Python
Python2.x版本中基本的中文编码问题解决
Oct 12 Python
Python的Flask框架中配置多个子域名的方法讲解
Jun 07 Python
Flask框架Flask-Principal基本用法实例分析
Jul 23 Python
Python中dict和set的用法讲解
Mar 28 Python
搭建python django虚拟环境完整步骤详解
Jul 08 Python
正则给header的冒号两边参数添加单引号(Python请求用)
Aug 09 Python
Python如何实现动态数组
Nov 02 Python
Tensorflow 使用pb文件保存(恢复)模型计算图和参数实例详解
Feb 11 Python
python下载卫星云图合成gif的方法示例
Feb 18 Python
Python GUI编程学习笔记之tkinter中messagebox、filedialog控件用法详解
Mar 30 Python
全网最全python库selenium自动化使用详细教程
Jan 12 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
海贼王动画变成“真人”后,凯多神还原,雷利太帅了!
2020/04/09 日漫
php获取远程文件的内容和大小
2015/11/03 PHP
php实现的AES加密类定义与用法示例
2018/01/29 PHP
基于laravel where的高级使用方法
2019/10/10 PHP
Laravel框架自定义分页样式操作示例
2020/01/26 PHP
JSChart轻量级图形报表工具(内置函数中文参考)
2010/10/11 Javascript
用于节点操作的API,颠覆原生操作HTML DOM节点的API
2010/12/11 Javascript
jQuery 滑动方法slideDown向下滑动元素
2014/01/16 Javascript
node.js中的fs.realpath方法使用说明
2014/12/16 Javascript
简介JavaScript中fixed()方法的使用
2015/06/08 Javascript
javascript电商网站抢购倒计时效果实现
2015/11/19 Javascript
easyui combotree加载静态数据问题(选不上)解决方法
2016/12/26 Javascript
jQuery实现鼠标悬停3d菜单展开动画效果
2017/01/19 Javascript
使用nodejs分离html文件里的js和css详解
2019/04/12 NodeJs
layui自定义验证,用ajax查询后台是否有重复数据,form.verify的例子
2019/09/06 Javascript
基于js实现抽红包并分配代码实例
2019/09/19 Javascript
layui加载数据显示loading加载完成loading消失的实例代码
2019/09/23 Javascript
Vue通过WebSocket建立长连接的实现代码
2019/11/05 Javascript
vue tab切换,解决echartst图表宽度只有100px的问题
2020/07/19 Javascript
Vue实现小购物车功能
2020/12/21 Vue.js
django实现同一个ip十分钟内只能注册一次的实例
2017/11/03 Python
一文带你了解Python中的字符串是什么
2018/11/20 Python
python面向对象法实现图书管理系统
2019/04/19 Python
python使用requests.session模拟登录
2019/08/09 Python
Python散点图与折线图绘制过程解析
2019/11/30 Python
pytorch 准备、训练和测试自己的图片数据的方法
2020/01/10 Python
详解Python 最短匹配模式
2020/07/29 Python
HTML5是否真的可以取代Flash
2010/02/10 HTML / CSS
中国最大的潮流商品购物网站:YOHO!BUY有货
2017/01/07 全球购物
澳大利亚当地最大的时装生产商:Cue
2018/08/06 全球购物
英国网上超市:Ocado
2020/03/05 全球购物
库房保管员岗位职责
2014/04/07 职场文书
房产转让协议书
2014/04/11 职场文书
消防志愿者活动方案
2014/08/23 职场文书
MySQL Router实现MySQL的读写分离的方法
2021/05/27 MySQL
nginx代理实现静态资源访问的示例代码
2022/07/07 Servers