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复制文件操作实例详解
Nov 10 Python
python3 遍历删除特定后缀名文件的方法
Apr 23 Python
Empty test suite.(PyCharm程序运行错误的解决方法)
Nov 30 Python
python自定义时钟类、定时任务类
Feb 22 Python
手机使用python操作图片文件(pydroid3)过程详解
Sep 25 Python
python处理excel绘制雷达图
Oct 18 Python
python清空命令行方式
Jan 13 Python
使用python求斐波那契数列中第n个数的值示例代码
Jul 26 Python
Python常用类型转换实现代码实例
Jul 28 Python
简单了解Python字典copy与赋值的区别
Sep 16 Python
python对文档中元素删除,替换操作
Apr 02 Python
python中 Flask Web 表单的使用方法
May 20 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
2019十大人气国漫
2020/03/13 国漫
便携利器 — TECSUN PL-365简评
2021/03/02 无线电
php 按指定元素值去除数组元素的实现方法
2011/11/04 PHP
PHP中exec函数和shell_exec函数的区别
2014/08/20 PHP
php文件缓存类用法实例分析
2015/04/22 PHP
PHP使用mysql与mysqli连接Mysql数据库用法示例
2016/07/07 PHP
PHP使用Nginx实现反向代理
2017/09/20 PHP
thinkPHP框架动态配置用法实例分析
2018/06/14 PHP
apycom出品的jQuery精美菜单破解方法
2011/02/18 Javascript
关于js遍历表格的实例
2013/07/10 Javascript
nodejs分页类代码分享
2014/06/17 NodeJs
Nodejs极简入门教程(三):进程
2014/10/27 NodeJs
javascript实现点击提交按钮后显示loading的方法
2015/07/03 Javascript
非常漂亮的相册集 使用jquery制作相册集
2016/04/28 Javascript
使用jQuery制作遮罩层弹出效果的极简实例分享
2016/05/12 Javascript
Vue官方推荐AJAX组件axios.js使用方法详解与API
2018/10/09 Javascript
基于Vue el-autocomplete 实现类似百度搜索框功能
2019/10/25 Javascript
在vue中把含有html标签转为html渲染页面的实例
2019/10/28 Javascript
python基础教程之面向对象的一些概念
2014/08/29 Python
Python实现文件内容批量追加的方法示例
2017/08/29 Python
Python正则表达式知识汇总
2017/09/22 Python
Python+OpenCV图片局部区域像素值处理详解
2019/01/23 Python
OpenCV3.0+Python3.6实现特定颜色的物体追踪
2019/07/23 Python
如何使用Flask-Migrate拓展数据库表结构
2019/07/24 Python
Django命名URL和反向解析URL实现解析
2019/08/09 Python
阿巴庭院:Abba Patio
2019/06/18 全球购物
自我鉴定四大框架
2014/01/17 职场文书
医药销售自荐书
2014/05/29 职场文书
应用外语系自荐信
2014/06/26 职场文书
学校食堂标语
2014/10/06 职场文书
2014年实验室工作总结
2014/12/03 职场文书
红色经典电影观后感
2015/06/18 职场文书
大学生入党自我鉴定范文
2019/06/21 职场文书
redis内存空间效率问题的深入探究
2021/05/17 Redis
在 SQL 语句中处理 NULL 值的方法
2021/06/07 SQL Server
vscode远程免密登入Linux服务器的配置方法
2022/06/28 Servers