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通过定义一个类实例作为ftp回调方法
May 04 Python
Python的Django框架中TEMPLATES项的设置教程
May 29 Python
python爬取淘宝商品详情页数据
Feb 23 Python
把csv文件转化为数组及数组的切片方法
Jul 04 Python
彻彻底底地理解Python中的编码问题
Oct 15 Python
Python rstrip()方法实例详解
Nov 11 Python
python pygame实现方向键控制小球
May 17 Python
Python logging设置和logger解析
Aug 28 Python
python yield和Generator函数用法详解
Feb 10 Python
Python matplotlib画图时图例说明(legend)放到图像外侧详解
May 16 Python
Selenium 安装和简单使用的实现
Dec 04 Python
python批量更改目录名/文件名的方法
Apr 18 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
如何让搜索引擎抓取AJAX内容解决方案
2014/08/25 PHP
PHP简单选择排序算法实例
2015/01/26 PHP
微信公众平台之快递查询功能用法实例
2015/04/14 PHP
php商品对比功能代码分享
2015/09/24 PHP
php处理带有中文URL的方法
2016/07/11 PHP
PhpStorm2020.1 安装 debug - Postman 调用的详细教程
2020/08/17 PHP
javascript学习随笔(使用window和frame)的技巧
2007/03/08 Javascript
jQuery 加上最后自己的验证
2009/11/04 Javascript
Javascript的严格模式strict mode详细介绍
2014/06/06 Javascript
javascript属性访问表达式用法分析
2015/04/25 Javascript
avalon js实现仿微博拖动图片排序
2015/08/14 Javascript
javascript数据类型验证方法
2015/12/31 Javascript
node.js 和HTML5开发本地桌面应用程序
2016/12/13 Javascript
BootStrap 获得轮播中的索引和当前活动的焦点对象
2017/05/11 Javascript
JavaScript面向对象精要(下部)
2017/09/12 Javascript
解决npm管理员身份install时出现权限的问题
2018/03/16 Javascript
Angular 数据请求的实现方法
2018/05/07 Javascript
Nodejs实现的操作MongoDB数据库功能完整示例
2019/02/02 NodeJs
ES7之Async/await的使用详解
2019/03/28 Javascript
javascript实现简易聊天室
2019/07/12 Javascript
vue商城中商品“筛选器”功能的实现代码
2020/07/01 Javascript
[55:47]DOTA2上海特级锦标赛C组小组赛#2 LGD VS Newbee第三局
2016/02/27 DOTA
利用python获取Ping结果示例代码
2017/07/06 Python
Python实现读写sqlite3数据库并将统计数据写入Excel的方法示例
2017/08/07 Python
python连接数据库的方法
2017/10/19 Python
python如何为创建大量实例节省内存
2018/03/20 Python
python模拟表单提交登录图书馆
2018/04/27 Python
Python+PyQt5实现美剧爬虫可视工具的方法
2019/04/25 Python
关于numpy中eye和identity的区别详解
2019/11/29 Python
input file上传文件样式支持html5的浏览器解决方案
2012/11/14 HTML / CSS
共产党员公开承诺书范文
2014/03/28 职场文书
九一八事变演讲稿
2014/09/05 职场文书
实习班主任自我评价
2015/03/11 职场文书
开学随笔
2015/08/15 职场文书
2016年先进班集体事迹材料
2016/02/26 职场文书
关于Python中*args和**kwargs的深入理解
2021/08/07 Python