0%

PyQt5-触手TV定时挂房

先放个成品图
在这里插入图片描述

在这里插入图片描述

  • 使用pyqt5的好处当然就是可以用designer工具进行拖拽操,界面定制化成都非常高,需要用到的关键组件主要是webengine 引擎采用的是chromium,这中间过程坑不少… 主要就是此引擎的web元素与chrome的Flash内元素竟然不同! 用selenium可以完全操作chrome所有的元素,哦对了!selenium也可以自动开启Flash限制,要修改谷歌在本地的注册表而后强制开启,pyqt5的web引擎就简单了 一行代码就可以搞定。
  • 其中pyqt5的打包是真的恶心至极,搞了5个小时左右才成功打包完成,打包后的内容也极其庞大 差不多100M,在一般个人电脑上运行是没问题的,在win系列的服务器上就各种报错,目前也没有解决。
  • 最新版本的pyqt5不包括WebEngine,需要单独下载,我个人建议是别使用pyside2来写qt 主要是因为执行js没有回调,这原因也不知道为什么,但是在pyqt5上就没问题,而且在我本机上使用pyside2的tools工具汉化是有问题的….. 下面是按照pyqt5,tools会下载设计者工具(拖拽组件实时查看)
1
2
3
pip install PyQtWebEngine -i https://pypi.douban.com/simple
pip install PyQt5==5.13.1 -i https://pypi.douban.com/simple
pip install pyqt5-tools -i https://pypi.douban.com/simple
  • 坑嘛 入了不少,GUI的多线程任务定时等等也是费了一天功夫才搞明白,抽奖操作直接发js,送礼物直接请求……. 下面是程序代码, 文章末尾有成品链接。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
"""
Created on 2019年12月27日
@author: 包子
@site: http://www.wxiptest.com
@email: 2775617031@qq.com
"""

from PyQt5.QtWidgets import QMainWindow, QApplication
from PyQt5.QtCore import QThread,pyqtSignal,QTimer,QUrl
from PyQt5.uic import loadUi
from PyQt5.QtWebEngineWidgets import QWebEngineProfile, QWebEngineSettings, QWebEngineView
from apscheduler.schedulers.blocking import BlockingScheduler
from apscheduler.triggers.cron import CronTrigger
import sys, requests, html, re, json, time
from datetime import datetime



# 挂机线程
class WorkThread(QThread):
trigger = pyqtSignal(str,str)

# 初始化调度方法 如果不添加这个就报错
def __init__(self, parent=None):
super().__init__(parent=None)
self.scheduler = BlockingScheduler()
#初始化线程
def __int__(self):
super(WorkThread, self).__init__()

def run(self):
# 执行线程时候调用下面的方法
self.web_run()
# 关闭驱动进程
def exit_r(self):
self.scheduler.remove_job('exit') # 移除web_exit调度任务
# 挂机结束时间 发出信号 跳转到指定页面
self.trigger.emit('https://chushou.tv/livezone.htm')
# 执行任务
def cookie_run(self, room,tmpH,tmpM):
trigger = CronTrigger(hour=int(tmpH), minute=int(tmpM))
self.scheduler.add_job(func=self.exit_r, trigger=trigger, id='exit')
# 向主线程发送信号 发出的是需要执行的房间 跳转到下面的room房间
url = 'https://chushou.tv/room/{}.htm'.format(room)
self.trigger.emit(url,str(room))

def web_run(self):
tim_text = self.read_tmp_text()
for i in tim_text:
startTim = f'{i[0]}:{i[1]}'
endTim = f'{i[3]}:{i[4]}'
# 范围时间
d_time = datetime.strptime(str(datetime.now().date()) + startTim, '%Y-%m-%d%H:%M') # 开始时间
d_time1 = datetime.strptime(str(datetime.now().date()) + endTim, '%Y-%m-%d%H:%M') # 结束时间
n_time = datetime.now() # 当前时间

# 判断当前时间是否在范围时间内 如果不在就正常等待 在的话就直接执行
if n_time > d_time and n_time < d_time1:
self.cookie_run(i[2], i[3], i[4])

# 运行 打包的时候需要用CronTrigger对象
trigger = CronTrigger(hour=int(i[0]), minute=int(i[1]))
self.scheduler.add_job(func=self.cookie_run, args=(i[2], i[3], i[4]), trigger=trigger)


# 开始调度程序
try:
self.scheduler.start()
except (KeyboardInterrupt, SystemExit):
pass
def read_tmp_text(self):
with open('时间.txt', 'r') as f:
file = f.readlines()
li = []
for i in file:
a = i.strip()
li.append(a.split('-'))
la = []
for i1 in li:
o = []
for i2 in i1:
a = i2.split(':')
for i3 in a:
o.append(i3)
la.append(o)
return la

# 移除所有调度任务
def remove_jobs(self):
self.scheduler.remove_all_jobs()



# 左键单击后打开的新窗口 M = WebEngineView
class M(QWebEngineView):
windowList = []

# 重写createwindow()
def createWindow(self, QWebEnginePage_WebWindowType):
new_webview = M() # 创建web视图对象
new_window = MainWindow() # 创建主界面的对象
new_window.zy.setCentralWidget(new_webview) # 调用主界面的方法
new_window.zy.show()
self.windowList.append(new_window) # 注:没有这句会崩溃!!!
return new_webview


# 主界面
class MainWindow(QMainWindow):
# 在主线程中设置自定义的信号 这里主要是给子线程发送信号
signal_m = pyqtSignal()
def __init__(self,*args, **kwargs):
super(MainWindow, self).__init__(*args, **kwargs)

# # 图片加载
# QWebEngineSettings.globalSettings().setAttribute(QWebEngineSettings.AutoLoadImages, False)

# 执行onCookieAdd
QWebEngineProfile.defaultProfile().cookieStore().cookieAdded.connect(self.onCookieAdd)
self.cookies = {} # 存放cookie字典

self.ui = loadUi('chushou.ui') # 之后控件都在ui下使用
# 设置窗口标题为启动时间
self.ui.setWindowTitle('程序启动时间:'+str(time.strftime('%Y-%m-%d %H:%M:%S',time.localtime()))+' by:包子')
# 设置透明度 # 0到1,1表示不透明,0表示完全透明
self.ui.setWindowOpacity(1)
# # 设置窗口背景色
# self.ui.setStyleSheet("background-color: white")
with open('时间.txt','r',encoding='utf-8') as f:
self.ui.textEdit_2.setText(f.read())
tmp = str(time.strftime('%Y-%m-%d %H:%M:%S', time.localtime())) + ' [欢迎使用自动挂机程序 任务开始前请登录触手TV]'
self.ui.listWidget.addItem(tmp)

# 另外的小窗口 用于登录用的 或打开其他链接
self.zy = loadUi('zy.ui')

# 设置窗体禁止拖拽 保持初始的固定大小
self.ui.setFixedSize(self.ui.width(), self.ui.height())

# 开启Flash权限
settings = QWebEngineSettings.globalSettings()
settings.setAttribute(QWebEngineSettings.PluginsEnabled, True) # 开启插件 支持flash
settings.setAttribute(QWebEngineSettings.WebGLEnabled, True) # 开启h5

# 调用重写的WebEngineView类 这样点击页面链接时候会弹出来小窗口
self.browser = M()
self.browser.load(QUrl('https://chushou.tv/livezone.htm'))
self.ui.out1.addWidget(self.browser) # 主窗口布局中显示web

self.timer = QTimer() # 检查房间任务
self.timer2 = QTimer() # 检测抽奖
self.js_web = QTimer() # 替换页面元素定时器
self.video = QTimer() # 切换标清

self.web_refresh = QTimer() # 定时刷新房间url
self.ui.pushButton_4.clicked.connect(self.room_id_reload) # 进入房间
self.timer.timeout.connect(self.get_room) # 检查房间任务
self.timer2.timeout.connect(self.lottery) # 检测抽奖
self.web_refresh.timeout.connect(self.refresh_web) # 发送到槽refresh函数


# 定时替换页面顶部元素
self.js_web.timeout.connect(self.js_web_re)

# 保存挂机文本
self.ui.saveB.clicked.connect(self.save_text)


self.ui.pushButton.clicked.connect(self.thread_work) # 点击开始挂机按钮启动thread_work函数 调用多线程
self.ui.pushButton_2.clicked.connect(self.t_stop) # 停止所有任务
# 保存挂机文本内容
def save_text(self):
gj_f = self.ui.textEdit_2.toPlainText()
with open('时间.txt','w',encoding='utf-8') as f:
f.write(gj_f)
js = '''
function a(){
return alert("已保存挂机时间!");
}a();
'''
self.browser.page().runJavaScript(js)
tmp = str(time.strftime('%Y-%m-%d %H:%M:%S', time.localtime())) + ' [修改并保存挂机设置]'

self.ui.listWidget.addItem(tmp)
# 处理cookie添加的事件
def onCookieAdd(self, cookie):
name = cookie.name().data().decode('utf-8')
value = cookie.value().data().decode('utf-8') # 先获取cookie值,再把编码处理一下
self.cookies[name] = value

# 获取cookie
def get_cookie(self):
cookie_str = ''
for key, value in self.cookies.items(): # 遍历字典
cookie_str += (key + '=' + value + ';') # 将键值对拿出来拼接一下
return cookie_str


# 多线程启用的函数
def thread_work(self):
tmp = str(time.strftime('%Y-%m-%d %H:%M:%S', time.localtime())) + ' [任务开始]'

self.ui.listWidget.addItem(tmp)
self.ui.saveB.setEnabled(False) # 保存挂机文本的按钮
self.ui.lineEdit.setEnabled(False)# 页面设定文本框
self.ui.lineEdit_2.setEnabled(False) # 房间检测文本框
self.ui.lineEdit_3.setEnabled(False) # 抽奖按钮
self.ui.textEdit_2.setEnabled(False) # 设置定时挂机文本框
self.ui.pushButton_4.setEnabled(False) # 房间进入按钮
self.ui.lineEdit_4.setEnabled(False) # 房间进入文本框
self.ui.checkBox.setEnabled(False) # 抽奖是否开启选择框
self.ui.checkBox_2.setEnabled(False) # 小心心选择框
# self.ui.checkBox_3.setEnabled(False) # 欢迎入场选择框
self.ui.pushButton.setEnabled(False) # 开始任务按钮
self.ui.pushButton_2.setEnabled(True) # 结束任务按钮

self.workThread = WorkThread()
self.workThread.start()
self.workThread.trigger.connect(self.thread_dy) # 用子线程的trigger属性触发thread_dy方法


# 停止多线程
def t_stop(self):
tmp = str(time.strftime('%Y-%m-%d %H:%M:%S', time.localtime())) + ' [已停止任务]'

self.ui.listWidget.addItem(tmp)
self.workThread.remove_jobs() # 删除子线程的所有调度任务
self.ui.saveB.setEnabled(True) # 保存挂机文本的按钮
self.ui.lineEdit.setEnabled(True) # 页面设定文本框
self.ui.lineEdit_2.setEnabled(True) # 房间检测文本框
self.ui.lineEdit_3.setEnabled(True) # 抽奖按钮
self.ui.textEdit_2.setEnabled(True) # 设置定时挂机文本框
self.ui.pushButton_4.setEnabled(True) # 房间进入按钮
self.ui.lineEdit_4.setEnabled(True) # 房间进入文本框
self.ui.checkBox.setEnabled(True) # 抽奖是否开启选择框
self.ui.checkBox_2.setEnabled(True) # 小心心选择框
# self.ui.checkBox_3.setEnabled(False) # 欢迎入场选择框
self.ui.pushButton.setEnabled(True) # 开始任务按钮
self.ui.pushButton_2.setEnabled(False) # 结束任务按钮
self.timer.stop() # 房间检测停止
self.web_refresh.stop() # 刷新停止
# 查看抽奖复选框是否被选中 为真就执行
fbox = self.ui.checkBox.isChecked()
if fbox:
self.timer2.stop() # 抽奖定时停止


# 子线程返回的内容输出在这里
def thread_dy(self,url,room):

if url == 'https://chushou.tv/livezone.htm': # 任务闲置时候将停止抽奖任务或者房间检测
self.browser.page().load(QUrl(url))
self.ui.out1.addWidget(self.browser)
# self.js_web.start(2000) # 开启页面元素替换定制
self.timer2.stop() # 抽奖定时停止
self.timer.stop() # 房间检测停止
else:
self.browser.page().load(QUrl(url))
self.ui.out1.addWidget(self.browser)
self.js_web.start(2000) # 开启页面元素替换定制

# 房间检测定时 从文本中获取数值
time_t1 = self.ui.lineEdit_2.text()
rt1 = int(time_t1) * 1000
self.timer.start(rt1)

# 查看抽奖复选框是否被选中 为真就执行
fbox = self.ui.checkBox.isChecked()
if fbox:
# 启用定时任务lottery
time_t2 = self.ui.lineEdit_3.text()
rt2 = int(time_t2) * 1000
self.timer2.start(rt2)

# 检查送礼物框是否被选中 小心心
xx_box = self.ui.checkBox_2.isChecked()
if xx_box:
cookie = self.get_cookie()
ua = 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.97 Safari/537.36'
headers = {
'user-agent': ua,
'cookie': cookie
}
device = 'WHJMrwNw1k/FLSoPdPLfXCIE0PMONrML7Zj2HUH+IjxjWEZFacpAP0XMELsE451oYb8onkxXb6Iembn5aAt1u8d3FkK6Tw31zot7UPqD/THUlU6kzcB21yQ/GllaqVP3BvWdC0QQex8gFHjDDDjKLreYPhhoSQZbvzZgTvbtY80EkHzaXlGXLfABZekwPW2Lt8Op5NbVgDOU2bCBM9cOgFUhS9qgw1ZilVavvSgv28gyn83AWxXi6J3QltbZXcg85LL4jR37UvaKQVtjWZGHAUk0MwWQLnbQc1487582755342'

xxx = requests.get('https://chushou.tv/', headers=headers)
try:
dataName = re.findall('data-nicename="(.*?)"', xxx.text)
dataToken = re.findall('data-token="(.*?)"', xxx.text)
user = html.unescape(dataName[0]) # 用户名
except IndexError:
info = str(time.strftime('%Y-%M-%M %H:%M:%S',time.localtime()))+' [请先登录账号在开始运行]'
self.ui.listWidget.addItem(info)
self.js_web.stop()
self.t_stop()
self.browser.page().load(QUrl('https://chushou.tv/livezone.htm'))
self.ui.out1.addWidget(self.browser)

else:

# 发送小礼物
xx_data = {
'count': '1',
'primaryKey': '120',
'targetKey': room,
'_listKey': 'null',
'type': '1',
'_sas': '1000',
'token': dataToken,
'device_stoken': device
}
xxx = requests.post('https://chushou.tv/room-pocket/consume.htm', data=xx_data,headers=headers).json()
if xxx['code'] == 404:
info = str(time.strftime('%Y-%M-%M %H:%M:%S', time.localtime())) + ' 房间:['+room+'][没有小心心可发送]'
self.ui.listWidget.addItem(info)
elif xxx['code'] == 401:
print(xxx,'未登录')
else:
# 发送小心心
info = str(time.strftime('%Y-%M-%M %H:%M:%S', time.localtime())) + ' 房间:['+room+'][已发送小心心]'
self.ui.listWidget.addItem(info)



# 启用URL定时刷新
time_t3 = self.ui.lineEdit.text()
rt3 = int(time_t3) * 1000
self.web_refresh.start(rt3)



# 刷新房间
def refresh_web(self):
self.browser.reload()
self.js_web.start(2000) # 开启页面元素替换定制
# 替换页面元素
def js_web_re(self):
js_hide = '''
function a(){
var htm = '<center><h2>测试版触手挂机程序 有问题也不改 再见</h2></center>';
var s = document.querySelector("body > div.cs_header > div.cs_left").innerText;

if(s == '测试版触手挂机程序 有问题也不改 再见'){
return 'y';
}else{
document.querySelector("#room_chat_ul > li.firstRemend > span.zb_userwords.noticeWords").innerText='使用本程序出现未知BUG,请点击蓝色按钮扫码添加好友'
document.querySelector("body > div.cs_header > div.cs_left").innerHTML=htm;
document.querySelector("#theaterModeContainer > div.live-room-giftbar > div.room-giftbar-info > div.giftbar-info-category").hidden=true;
document.querySelector("#rightBlock > div.roomOnline.roomOnlineStyle0 > div.room-activity > div").hidden=true;
document.querySelector("#liveBlockContainer > div.live-room-main > div.live-room-left > div.live-room-nav > div > div.nav-baseinfo-column > div").hidden=true;
document.querySelector("#roomLeftNav").hidden=true;
document.querySelector("#theaterModeContainer > div.live-room-giftbar > div.room-giftbar-info > div.giftbar-info-list").hidden=true;
//document.querySelector("#anchorvideo").hidden=true;
document.querySelector("#otheranchor").hidden=true;
document.querySelector("#liveBlockContainer > div.live-room-main > div.live-room-left > div.live-room-nav > div > div.nav-title-column.nav-title-column-anchor > div.nav-title-operate > a").innerText='By:包子';
document.querySelector("#liveBlockContainer > div.live-room-main > div.live-room-left > div.live-room-nav > div > div.nav-title-column.nav-title-column-anchor > div.nav-title-operate > a").href='http://i1.fuimg.com/706520/0d92d24fd9fa187d.jpg';
document.querySelector("#anchorvideo").hidden=true;
return 'n';
}
}
a();
'''
self.browser.page().runJavaScript(js_hide, self.js_web_call)

# 判断元素替换 已经替换就停止替换元素的定时任务
def js_web_call(self, a):
if str(a) == 'y':
self.js_web.stop()

# 进入房间
def room_id_reload(self):
roomID = self.ui.lineEdit_4.text()
url = 'https://chushou.tv/room/{}.htm'.format(roomID)
self.browser.page().load(QUrl(url))
self.ui.out1.addWidget(self.browser)
self.js_web.start(2000)

def get_room(self):
js_str = '''
function a(){
return document.querySelector("#liveBlockContainer > div.live-room-main.min-room-width > div.live-room-left > div.live-room-nav > div > div.nav-baseinfo-column > p.room-anchor-roomid").textContent;
}a();'''
self.browser.page().runJavaScript(js_str, self.js_callback_room)

# 接收js返回值 房间
def js_callback_room(self, a):
if a:
tmp = str(time.strftime('%Y-%m-%d %H:%M:%S', time.localtime())) + ' [当前挂机房间:' + a+']'
self.ui.listWidget.addItem(tmp)
else:
tmp = str(time.strftime('%Y-%m-%d %H:%M:%S', time.localtime())) + ' [主播已下线 未直播]'
self.ui.listWidget.addItem(tmp)

# 抽奖
def lottery(self):
js = '''
function a(){
// 直接在主页面执行js 操作iframe 点击抽奖按钮
document.getElementById('lotteryFrame').contentWindow.document.getElementById('lottery_btn').click();
}a();
'''
self.browser.page().runJavaScript(js)


if __name__ == '__main__':
app = QApplication(sys.argv)
stats = MainWindow() # 调用show方法 打开窗口
stats.ui.show()
sys.exit(app.exec_()) # 调用sys库的exit退出方法,条件是app.exec_()也就是整个窗口关闭
  • 下载地址 https://share.weiyun.com/5KTYAAf