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除法运算小技巧
Apr 05 Python
在Django中创建URLconf相关的通用视图的方法
Jul 20 Python
VScode编写第一个Python程序HelloWorld步骤
Apr 06 Python
django启动uwsgi报错的解决方法
Apr 08 Python
PyQt5每天必学之拖放事件
Aug 27 Python
Python 查看list中是否含有某元素的方法
Jun 27 Python
对pandas中iloc,loc取数据差别及按条件取值的方法详解
Nov 06 Python
详解opencv Python特征检测及K-最近邻匹配
Jan 21 Python
python画蝴蝶曲线图的实例
Nov 21 Python
numpy ndarray 取出满足特定条件的某些行实例
Dec 05 Python
Django+boostrap 美化admin后台的操作
Mar 11 Python
Python字符串的15个基本操作(小结)
Feb 03 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 截取字符串并以零补齐str_pad() 函数
2011/05/07 PHP
ThinkPHP学习笔记(一)ThinkPHP部署
2014/06/22 PHP
getJSON跨域SyntaxError问题分析
2014/08/07 PHP
PHP中文乱码解决方案
2015/03/05 PHP
PHP实现基于文本的摩斯电码生成器
2016/01/11 PHP
JavaScript 模仿vbs中的 DateAdd() 函数的代码
2007/08/13 Javascript
JS 巧妙获取剪贴板数据 Excel数据的粘贴
2009/07/09 Javascript
对于this和$(this)的个人理解
2013/09/08 Javascript
关于js内存泄露的一个好例子
2013/12/09 Javascript
jquery移动点击的项目到列表最顶端的方法
2015/06/24 Javascript
AngularJS的一些基本样式初窥
2015/07/27 Javascript
jQuery实现购物车表单自动结算效果实例
2015/08/10 Javascript
JS动态日期时间的获取方法
2015/09/28 Javascript
基于MVC4+EasyUI的Web开发框架形成之旅之界面控件的使用
2015/12/16 Javascript
通过JS和PHP两种方法判断用户请求时使用的浏览器类型
2016/09/01 Javascript
COM组件中调用JavaScript函数详解及实例
2017/02/23 Javascript
EasyUI的DataGrid每行数据添加操作按钮的实现代码
2017/08/22 Javascript
JavaScript中set与get方法用法示例
2018/08/15 Javascript
小程序云开发初探(小结)
2018/10/24 Javascript
vue.js路由mode配置之去掉url上默认的#方法
2019/11/01 Javascript
[41:13]完美世界DOTA2联赛PWL S2 Forest vs Rebirth 第一场 11.20
2020/11/20 DOTA
python使用socket远程连接错误处理方法
2015/04/29 Python
Numpy之将矩阵拉成向量的实例
2019/11/30 Python
python3实现raspberry pi(树莓派)4驱小车控制程序
2020/02/12 Python
Python pysnmp使用方法及代码实例
2020/08/24 Python
捷克母婴用品购物网站:Feedo.cz
2020/12/28 全球购物
2014两会优秀的心得体会范文
2014/03/17 职场文书
专科生就业求职信
2014/06/22 职场文书
群众路线教育实践活动对照检查材料思想汇报(副处级领导)
2014/10/04 职场文书
辩论赛主持人开场白
2015/05/29 职场文书
有关西游记的读书笔记
2015/06/25 职场文书
投资入股协议书
2016/03/22 职场文书
创业计划书之电动车企业
2019/10/11 职场文书
使用react+redux实现计数器功能及遇到问题
2021/06/02 Javascript
Python 如何将integer转化为罗马数(3999以内)
2021/06/05 Python
浅谈怎么给Python添加类型标注
2021/06/08 Python