python 是门好语言,简单上手,轮子多。qml 是一个非常好的桌面工具,像html 一样的语法开发迅速,界面好看。selenium 是自动化测试与爬虫非常好的工具。
使用python,qml,selenium结合,可以开发出可视化的爬虫。
qml 做前端与python 通信,python再调用selenium 爬取数据,将过程,结果不断反馈给qml 进行显示。
第一步: 新建main.qml 文件做出主界面。
import QtQuick 2.15
import QtQuick.Window
import QtQuick.Controls
Window {
id: root
width: 1050
height: 600
visible: true
title: qsTr("python-qml")
}
第二步:使用python 运行qml,我这里使用的 pyside6 。
# coding=utf-8
from PySide6.QtWidgets import QApplication
from PySide6.QtGui import QIcon
from PySide6.QtQml import QQmlApplicationEngine
import sys
if __name__ == "__main__":
app = QApplication(sys.argv)
engine = QQmlApplicationEngine()
engine.load("main.qml")
if not engine.rootObjects():
sys.exit(-1)
sys.exit(app.exec())
这样第一个qml 应用就开始了。
接下来需要python与qml 可以相互通信才能互相传递数据。
qml 去调用python是需要桥的。
# coding=utf-8
from PySide6.QtCore import QObject, Slot
from PySide6.QtWidgets import QApplication
from PySide6.QtGui import QIcon
from PySide6.QtQml import QQmlApplicationEngine
from PySide6.QtQml import QmlElement
import sys
QML_IMPORT_NAME = "io.qt.textproperties"
QML_IMPORT_MAJOR_VERSION = 1
@QmlElement
class Bridge(QObject):
@Slot(int, result=int)
def getdata(self, num):
return num*num
if __name__ == "__main__":
app = QApplication(sys.argv)
app.setWindowIcon(QIcon('icon/box2-heart-fill.svg'))
engine = QQmlApplicationEngine()
bridge = Bridge()
engine.rootContext().setContextProperty('bridge', bridge)
engine.load("main.qml")
if not engine.rootObjects():
sys.exit(-1)
sys.exit(app.exec())
我们需要在python中定义这个一个继承QObect的类,并注册到qml当中。这样qml 就能调用到python 类中定义的方法。其中 @Slot 约定了双方发送参数的格式与接受参数的格式。
QML_IMPORT_NAME = "io.qt.textproperties" QML_IMPORT_MAJOR_VERSION = 1
这两句话不能少,具体作用可以查看Qt 的官方手册。
qml 是用 js 作为脚本语言,调用python 如下。
import QtQuick 2.15
import QtQuick.Window
import QtQuick.Controls
Window {
id: root
width: 1050
height: 600
visible: true
title: qsTr("python-qml")
Component.onCompleted: {
let b = bridge.getdata(10)
print(b)
}
}
我们可以在窗口看到qml 的输出
这样qml调用python 就完成了。
而python主动给qml 发消息需要qt 里面的信号 Signal。信号同样需要定义在桥里面。
from PySide6.QtCore import Signal #注意导入信号
@QmlElement
class Bridge(QObject):
sig = Signal(str, arguments=['res'])
@Slot(int, result=int)
def getdata(self, num):
return num*num
Signal 第一个参数代表参数的类型,第二个参数代表qml接受的参数名称。qml 要接收到此信号发的消息就要监听此信号。
import QtQuick 2.15
import QtQuick.Window
import QtQuick.Controls
Window {
id: root
width: 1050
height: 600
visible: true
title: qsTr("python-qml")
Component.onCompleted: {
let b = bridge.getdata(10)
print(b)
}
Connections{
target: bridge
function onSig(res){
print(res)
}
}
}
# coding=utf-8
import time
from PySide6.QtCore import QObject, Slot, Signal
from PySide6.QtWidgets import QApplication
from PySide6.QtGui import QIcon
from PySide6.QtQml import QQmlApplicationEngine
from PySide6.QtQml import QmlElement
import sys
import threading
QML_IMPORT_NAME = "io.qt.textproperties"
QML_IMPORT_MAJOR_VERSION = 1
@QmlElement
class Bridge(QObject):
sig = Signal(str, arguments=['res'])
@Slot(int, result=int)
def getdata(self, num):
return num*num
def timetoprint(bridge):
while 1:
bridge.sig.emit("hello!!!")
time.sleep(1)
if __name__ == "__main__":
app = QApplication(sys.argv)
app.setWindowIcon(QIcon('icon/box2-heart-fill.svg'))
engine = QQmlApplicationEngine()
bridge = Bridge()
engine.rootContext().setContextProperty('bridge', bridge)
engine.load("main.qml")
t = threading.Thread(target=timetoprint,args=(bridge,))
t.start()
if not engine.rootObjects():
sys.exit(-1)
sys.exit(app.exec())
qml 就能收到我们主动发的消息了。在有了前面的前置条件后,就能往下不断的开发功能,开发界面了。
接下来是selenium,不用过多介绍,网上有许多的资料,选用selenium 是因为现在的网站资源大多是动态加载的,无法通过静态网页来获取,requests有点力不从心。selenium 能够很好的模拟浏览器,达到欺骗网站的效果。这次的实践网站,就以全球拥有巨大访问量的P站做练习。P站的视频时使用m3u8做索引的流式视频,但主要的m3u8 链接是由js动态生成的,并且js代码还被混淆。
道高一尺,魔高一丈,我们使用selenium-wire 直接抓取网站发出m3u8 链接,抓到链接后再解析其中的ts 链接并下载合成。
主要思路为qml 输入链接-> python 收到链接 开多线程启动selenium 打开链接 -> selenium加载网页完毕->selenium-wire 获取请求链接-> 匹配其中的m3u8 链接 解析下载 -> ffmpeg 合成。
哈哈,终于可以在P站愉快的学习微积分而不用被妈妈发现了 >_< 。
对项目感兴趣的小伙伴可以去GitHub 上瞧瞧 github.com/alishanjack/h-box