目录
- 🍳前言
- 🍳实验介绍
- 🍳产品介绍
- 🍳抓包分析
- 😃登录分析
- 😃😃第一步,获取验证码
- 😃😃第二步,保存验证码
- 😃😃第三步,识别验证码
- 😃😃第四步,实现登录
- 😃兑换操作分析
- 🍳实际操作
- ⭐注册Cloud Studio
- ⭐新建python项目(不选用模板)
- ⭐⭐创建项目
- ⭐⭐填写模板信息
- ⭐⭐选择环境镜像
- ⭐代码操作
- ⭐⭐获取验证码
- ⭐⭐保存验证码
- ⭐⭐识别验证码
- ⭐⭐⭐ddddocr修复bug
- ⭐⭐接口鉴权
- ⭐⭐福利码兑换
- ⭐构建GUI视图
- ⭐⭐绘制可视化界面
- ⭐⭐获得视图层代码
- ⭐⭐代码事件封装
- ⭐⭐软件打包
- ⭐总结
🍳前言
Cloud Studio 是基于浏览器的集成式开发环境(IDE),为开发者提供了一个永不间断的云端工作站。用户在使用 Cloud Studio 时无需安装,随时随地打开浏览器就能在线编程。 Cloud Studio 作为在线 IDE,包含代码高亮、自动补全、Git 集成、终端等 IDE 的基础功能,同时支持实时调试、插件扩展等,可以帮助开发者快速完成各种应用的开发、编译与部署工作。 所以,本次开发蛋仔派对兑换码工具优先采用在线IDE-Cloud Studio,让我们随着本项目对Cloud Studio进行探索吧~
案例环境:
- python
模块:
- datetime
- json
- re
- tkinter
🍳实验介绍
通过本次项目实战,实现一个可视化的小工具,可批量操作兑换码
🍳产品介绍
Cloud Studio 是基于浏览器的集成式开发环境(IDE),为开发者提供了一个永不间断的云端工作站。用户在使用 Cloud Studio 时无需安装,随时随地打开浏览器就能在线编程。 Cloud Studio 作为在线 IDE,包含代码高亮、自动补全、Git 集成、终端等 IDE 的基础功能,同时支持实时调试、插件扩展等,可以帮助开发者快速完成各种应用的开发、编译与部署工作。
🍳抓包分析
在进行写代码前,我们进行流程分析,否则无从下手,这是蛋仔派对的兑换人口
😃登录分析
暂且就叫登录吧,因为它的第一步是输入游戏ID进行查询,才有后面的兑换操作
😃😃第一步,获取验证码
登录需要验证码,通过Stream,我们进行正常的兑换测试【图1-1】,发现在首页打开时会触发获取验证码的接口【图1-2】,这是抓包响应结构:
{"status": true, "base64": "/9j*****Q==", "ctoken_id": "e8814dbbec8348ea8b0b7adb0ec83458"}
图1-1
图1-2
猜测base64的内容就是验证码,我们通过在线网站还原base64图片看一下,
通过转换工具是否能得到验证码图片【图1-3】
图1-3
那也就是说,验证码在每次刷新网页就会请求到,通过base64加载图片,验证图片后,与之携带的ctoken_id就生效,就能得到token,有了token后面的,兑换操作就能继续往下
😃😃第二步,保存验证码
通过python函数将base64转换为本地图片,该功能代码如下:
base64转换图片-base64_data = base64.b64decode(base64_)将base64数据保存为图片with open('captcha.png', 'wb') as f:f.write(base64_data)
😃😃第三步,识别验证码
免费的验证码识别工具,我推荐DDDDocr,识别完成后通过验证即可
识别代码:
ocr = ddddocr.DdddOcr()
with open('captcha.png', 'rb') as f:img_bytes = f.read()
res = ocr.classification(img_bytes)
print('识别出的验证码为:' + res)
😃😃第四步,实现登录
通过抓包,我发现获取游戏昵称,也就是登录、鉴权的请求接口如此简单:
请求接口
http://com-sev.webapp.163.com/u5cdkey_query_uidinfo/api?img_auth=H8i&uid=123456&ctoken_id=58c24d6b9a1f43b3974306ae0b1e55c0&host_test=0&_=1689952803842&callback=jsonp
完整的请求响应结合刚刚分析的,再来总结下
请求参数 | 说明 |
---|---|
img_auth | 验证码内容 |
ctoken_id | 下发验证码的token_id |
uid | 用户游戏id |
响应参数 | 说明 |
---|---|
status | 状态 |
nick | 游戏昵称 |
那只要将上面的验证码识别出来配合uid就可以直接登录,并且获取到用户昵称以及认证过的token
😃兑换操作分析
在进行写代码前,我们进行流程分析,否则无从下手,这是蛋仔派对的兑换人口
请求URL | 请求方式 |
---|---|
http://com-sev.webapp.163.com/u5cdkey_redeem/api | GET |
将token,code,以及uid一起提交即可完成最后的操作,通过抓包我们将接口请求的思路,流程都梳理了一遍,现在就是动手的时候了,接下来我们通过cloud stdio在线IDE进行项目的开发
🍳实际操作
⭐注册Cloud Studio
注册Cloud Studio,这里注册和登录 Cloud Studio 非常方便,提供了下面三种注册方式:
- 使用 CODING 账号授权注册/登录
- 使用微信授权注册/登录 (本文使用方式)
- 使用 GitHub 授权注册/登录
coding可对代码进行托管,微信授权注册后可以在这里绑定
⭐新建python项目(不选用模板)
⭐⭐创建项目
点击【新建模板】,再次点击【手动新建】
⭐⭐填写模板信息
⭐⭐选择环境镜像
选择python3.9,其他默认
⭐代码操作
⭐⭐获取验证码
在上面创建的空项目中,我们新建一个文件名叫:get_captcha.py
输入代码
import requests
import datetime
import time
headers = {'Host': 'com-sev.webapp.163.com','Accept': '*/*','Connection': 'keep-alive','User-Agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 16_5_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.5.1 Mobile/15E148 Safari/604.1','Accept-Language': 'zh-CN,zh-Hans;q=0.9','Referer': 'http://party.163.com/dh/m/',}
now = datetime.datetime.now()# 将当前时间转换为时间戳(秒级)
timestamp = time.mktime(now.timetuple())# 将时间戳转换为毫秒级时间戳
millisecond_timestamp = int(timestamp * 1000)
params = {'return_type': 'base64','_': millisecond_timestamp,# 'callback': 'jsonp',}response = requests.get('http://com-sev.webapp.163.com/get_apnt_auth_img', params=params,headers=headers)
print(response.text)
result = response.json()
点击右上角运行后,显示base64图片代码和token_id,注意python中代码的格式对齐很严格
⭐⭐保存验证码
图片数据有了,应为要通过ocr识别,所以要将base64转换为图片保存在本地
'''
前面代码省略
'''
result = response.json()
if result['status']:base64_ = result['base64']ctoken_id = result['ctoken_id']print(base64_)print(ctoken_id)# base64转换图片-由于接口更换不需要base64_data = base64.b64decode(base64_)# 将base64数据保存为图片with open('captcha.png', 'wb') as f:f.write(base64_data)
else:print("验证码获取失败")
运行后本地产生一张图片
⭐⭐识别验证码
刚刚分析时,ddddocr,已经大概介绍过了,ddddocr是一个开源项目,托管在GitHub上。这使开发者可以自由地访问、使用和修改ddddocr的源代码,根据自己的需求进行定制和拓展。
ddddocr的主要功能和特点使其成为一个强大的双重数字识别工具,适用于各种需要识别双重数字的场景,例如票据识别、验证码识别、电子表格处理等。它的高准确性和灵活易用性使得开发者能够快速、准确地实现双重数字识别的需求,虽然免费版有很多缺点,但是识别今天的验证码够用了
'''
省略上面代码
注意,补上import ddddocr
'''
#5行即可识别
ocr = ddddocr.DdddOcr()
with open('captcha.png', 'rb') as f:img_bytes = f.read()
res = ocr.classification(img_bytes)
print('识别出的验证码为:' + res)
⭐⭐⭐ddddocr修复bug
图片数据有了,应为要通过ocr识别,所以要将base64转换为图片保存在本地
运行下看下识别效果,发现报错了
原来是刚刚的Ddddocr模块没装上,之前在顶部写入
import xxx
就会自动安装,但是现在不行,我们尝试手动安装吧
终端输入:
pip install ddddocr
等待安装完成即可
运行后报错:
➜ /workspace /root/.pyenv/versions/3.9.6/bin/python /workspace/index.py
Traceback (most recent call last):
File “/workspace/index.py”, line 6, in
res = ocr.classification(img_bytes)
File “/root/.pyenv/versions/3.9.6/lib/python3.9/site-packages/ddddocr/init.py”, line 1614, in classification
image = image.resize((int(image.size[0] * (64 / image.size[1])), 64), Image.ANTIALIAS).convert(‘L’)
AttributeError: module ‘PIL.Image’ has no attribute ‘ANTIALIAS’
问题出现在 Image.ANTIALIAS 属性上,这个属性在PIL库中已经不存在了。该错误提示与我之前提供的回答一致。从PIL 5.1.0版本开始,Image.ANTIALIAS 已经被替换为 Image.LANCZOS,因此导致你的代码在新版本的PIL库中出现错误。
要解决这个问题,你可以将 Image.ANTIALIAS 替换为 Image.LANCZOS,于是点击
跳转到__init__.py进行修改,不得不说cloud stdio 编辑器和真机环境没啥区别,自动化能力很强
修复后重新运行下,提取出的验证码与目标一致
⭐⭐接口鉴权
接下来我们编写一个接口请求用来通过验证,下方的代码运行后报错少了res,因为res是之前的验证码,接下来我们通过拼接使代码完整
#新建login.py
import requests
import datetime
import time
# 登录操作
headers = {
'Host': 'com-sev.webapp.163.com',
'Accept': '*/*',
'Connection': 'keep-alive',
'User-Agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 16_5_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.5.1 Mobile/15E148 Safari/604.1',
'Accept-Language': 'zh-CN,zh-Hans;q=0.9',
'Referer': 'http://party.163.com/dh/m/',
}
now = datetime.datetime.now()# 将当前时间转换为时间戳(秒级)
timestamp = time.mktime(now.timetuple())# 将时间戳转换为毫秒级时间戳
millisecond_timestamp = int(timestamp * 1000)
params = {
'img_auth': res,
'uid': '123456',
'ctoken_id': ctoken_id,
'host_test': '0',
'_': millisecond_timestamp,
# 'callback': 'jsonp3',
}response = requests.get(
'http://com-sev.webapp.163.com/u5cdkey_query_uidinfo/api',
params=params,
headers=headers,
)
print(response.text)
resul = response.json()
重新拼接刚刚获取到的res,所以完整代码为:
#get_captcha.py
import requests
import datetime
import time
import base64
import ddddocr
headers = {'Host': 'com-sev.webapp.163.com','Accept': '*/*','Connection': 'keep-alive','User-Agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 16_5_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.5.1 Mobile/15E148 Safari/604.1','Accept-Language': 'zh-CN,zh-Hans;q=0.9','Referer': 'http://party.163.com/dh/m/',}
now = datetime.datetime.now()# 将当前时间转换为时间戳(秒级)
timestamp = time.mktime(now.timetuple())# 将时间戳转换为毫秒级时间戳
millisecond_timestamp = int(timestamp * 1000)
params = {'return_type': 'base64','_': millisecond_timestamp,# 'callback': 'jsonp',}response = requests.get('http://com-sev.webapp.163.com/get_apnt_auth_img', params=params,headers=headers)
print(response.text)
result = response.json()
if result['status']:base64_ = result['base64']ctoken_id = result['ctoken_id']print(base64_)print(ctoken_id)# base64转换图片-由于接口更换不需要base64_data = base64.b64decode(base64_)# 将base64数据保存为图片with open('captcha.png', 'wb') as f:f.write(base64_data)ocr = ddddocr.DdddOcr()with open('captcha.png', 'rb') as f:img_bytes = f.read()res = ocr.classification(img_bytes)print('识别出的验证码为:' + res)
else:res=''print("验证码获取失败")# 登录操作
headers = {
'Host': 'com-sev.webapp.163.com',
'Accept': '*/*',
'Connection': 'keep-alive',
'User-Agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 16_5_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.5.1 Mobile/15E148 Safari/604.1',
'Accept-Language': 'zh-CN,zh-Hans;q=0.9',
'Referer': 'http://party.163.com/dh/m/',
}
now = datetime.datetime.now()# 将当前时间转换为时间戳(秒级)
timestamp = time.mktime(now.timetuple())# 将时间戳转换为毫秒级时间戳
millisecond_timestamp = int(timestamp * 1000)
params = {
'img_auth': res,
'uid': '123456',
'ctoken_id': ctoken_id,
'host_test': '0',
'_': millisecond_timestamp,
# 'callback': 'jsonp3',
}response = requests.get(
'http://com-sev.webapp.163.com/u5cdkey_query_uidinfo/api',
params=params,
headers=headers,
)
print(response.text)
resul = response.json()
测试效果,成功登录,由于验证码识别不是100%,所以你可能需要多试几次,直到登陆成功,可以进行下一个操作了,兑换操作!
⭐⭐福利码兑换
前面的各种参数我们已经拿到了,现在直接拿来用就OK,通过前面的抓包分析,我们发现,提交的参数就这三个
请求参数 | 说明 |
---|---|
code | 兑换码 |
uid | 个人游戏id |
请求头 | 说明 |
---|---|
token | token_id |
代码构造
# 兑换
import datetime
import timeimport requestsheaders = {'Host': 'com-sev.webapp.163.com','Accept': '*/*','ctoken_id': 'sdsdsd','Connection': 'keep-alive','User-Agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 16_5_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.5.1 Mobile/15E148 Safari/604.1','Accept-Language': 'zh-CN,zh-Hans;q=0.9','Referer': 'http://party.163.com/dh/m/',}
now = datetime.datetime.now()# 将当前时间转换为时间戳(秒级)
timestamp = time.mktime(now.timetuple())# 将时间戳转换为毫秒级时间戳
millisecond_timestamp = int(timestamp * 1000)params = {'code':'123','uid': '123456','host_test': '0','_': millisecond_timestamp,# 'callback': 'jsonp7',}response = requests.get('http://com-sev.webapp.163.com/u5cdkey_redeem/api', params=params,headers=headers)
print(response.json())
拼接后完整代码:
import requests
import datetime
import time
import base64
import ddddocr
headers = {'Host': 'com-sev.webapp.163.com','Accept': '*/*','Connection': 'keep-alive','User-Agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 16_5_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.5.1 Mobile/15E148 Safari/604.1','Accept-Language': 'zh-CN,zh-Hans;q=0.9','Referer': 'http://party.163.com/dh/m/',}
now = datetime.datetime.now()# 将当前时间转换为时间戳(秒级)
timestamp = time.mktime(now.timetuple())# 将时间戳转换为毫秒级时间戳
millisecond_timestamp = int(timestamp * 1000)
params = {'return_type': 'base64','_': millisecond_timestamp,# 'callback': 'jsonp',}response = requests.get('http://com-sev.webapp.163.com/get_apnt_auth_img', params=params,headers=headers)
print(response.text)
result = response.json()
if result['status']:base64_ = result['base64']ctoken_id = result['ctoken_id']print(base64_)print(ctoken_id)# base64转换图片-由于接口更换不需要base64_data = base64.b64decode(base64_)# 将base64数据保存为图片with open('captcha.png', 'wb') as f:f.write(base64_data)ocr = ddddocr.DdddOcr()with open('captcha.png', 'rb') as f:img_bytes = f.read()res = ocr.classification(img_bytes)print('识别出的验证码为:' + res)
else:res=''print("验证码获取失败")# 登录操作
headers = {
'Host': 'com-sev.webapp.163.com',
'Accept': '*/*',
'Connection': 'keep-alive',
'User-Agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 16_5_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.5.1 Mobile/15E148 Safari/604.1',
'Accept-Language': 'zh-CN,zh-Hans;q=0.9',
'Referer': 'http://party.163.com/dh/m/',
}
now = datetime.datetime.now()# 将当前时间转换为时间戳(秒级)
timestamp = time.mktime(now.timetuple())# 将时间戳转换为毫秒级时间戳
millisecond_timestamp = int(timestamp * 1000)
params = {
'img_auth': res,
'uid': '123456',
'ctoken_id': ctoken_id,
'host_test': '0',
'_': millisecond_timestamp,
# 'callback': 'jsonp3',
}response = requests.get(
'http://com-sev.webapp.163.com/u5cdkey_query_uidinfo/api',
params=params,
headers=headers,
)
print(response.text)
resul = response.json()# 兑换headers = {'Host': 'com-sev.webapp.163.com','Accept': '*/*','ctoken_id': 'sdsdsd','Connection': 'keep-alive','User-Agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 16_5_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.5.1 Mobile/15E148 Safari/604.1','Accept-Language': 'zh-CN,zh-Hans;q=0.9','Referer': 'http://party.163.com/dh/m/',}
now = datetime.datetime.now()# 将当前时间转换为时间戳(秒级)
timestamp = time.mktime(now.timetuple())# 将时间戳转换为毫秒级时间戳
millisecond_timestamp = int(timestamp * 1000)params = {'code':'123','uid': '123456','host_test': '0','_': millisecond_timestamp,# 'callback': 'jsonp7',}response = requests.get('http://com-sev.webapp.163.com/u5cdkey_redeem/api', params=params,headers=headers)
print(response.json())
运行后结果
状态码 | 说明 |
---|---|
400 | 兑换码不存在 |
未知 | 兑换码已被使用 |
200 | 兑换成功 |
⭐构建GUI视图
要想实现上面的效果,光有代码可是不够,于是我们需要通过可视化工具帮我们“画”一个界面
⭐⭐绘制可视化界面
接下来通过VB6画一个可视化界面
⭐⭐获得视图层代码
刷新窗体获得代码
这部分不懂得同学,可以看这个《Python tkinter快速可视化开发GUI界面指南:详细教程(附带工具)》
⭐⭐代码事件封装
在cloud stdio新建index.py,将下面我封装好的代码粘贴上去即可,由于ddddocr存在打包问题我这里有两个版本
#版本1-在线验证码识别
#!/usr/bin/env python
#-*- coding:utf-8 -*-
import datetime
import json
import re
import time
from tkinter import messageboximport requeststry:from tkinter import *
except ImportError: #Python 2.xPythonVersion = 2from Tkinter import *from tkFont import Fontfrom ttk import *#Usage:showinfo/warning/error,askquestion/okcancel/yesno/retrycancelfrom tkMessageBox import *#Usage:f=tkFileDialog.askopenfilename(initialdir='E:/Python')#import tkFileDialog#import tkSimpleDialog
else: #Python 3.xPythonVersion = 3from tkinter.font import Fontfrom tkinter.ttk import *from tkinter.messagebox import *#import tkinter.filedialog as tkFileDialog#import tkinter.simpledialog as tkSimpleDialog #askstring()def get_captcha():global ctoken_idglobal base_64_headers = {'Host': 'com-sev.webapp.163.com','Accept': '*/*','Connection': 'keep-alive','User-Agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 16_5_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.5.1 Mobile/15E148 Safari/604.1','Accept-Language': 'zh-CN,zh-Hans;q=0.9','Referer': 'http://party.163.com/dh/m/',}now = datetime.datetime.now()# 将当前时间转换为时间戳(秒级)timestamp = time.mktime(now.timetuple())# 将时间戳转换为毫秒级时间戳millisecond_timestamp = int(timestamp * 1000)params = {'return_type': 'base64','_': millisecond_timestamp,# 'callback': 'jsonp',}response = requests.get('http://com-sev.webapp.163.com/get_apnt_auth_img', params=params,headers=headers)# print(response.text)result = response.json()if result['status']:base64_ = result['base64']ctoken_id = result['ctoken_id']print(base64_)print(ctoken_id)ctoken_id=ctoken_idbase_64_=base64_# base64转换图片-由于接口更换不需要# base64_data = base64.b64decode(base64_)## # 将base64数据保存为图片# with open('captcha.png', 'wb') as f:# f.write(base64_data)else:print("验证码获取失败")messagebox.showinfo(title='温馨提示', message='验证码获取失败')
class Application_ui(Frame):#这个类仅实现界面生成功能,具体事件处理代码在子类Application中。def __init__(self, master=None):Frame.__init__(self, master)self.master.title('网易-蛋仔派对礼包码兑换工具-Q390983964')self.master.geometry('453x363')self.createWidgets()# self.Label6.config(text='0')# self.Label7.config(text='0')def createWidgets(self):self.top = self.winfo_toplevel()self.style = Style()self.style.configure('TCommand2.TButton', font=('宋体', 9))self.Command2 = Button(self.top, text='一键兑换', command=self.Command2_Cmd, style='TCommand2.TButton')self.Command2.place(relx=0.706, rely=0.793, relwidth=0.214, relheight=0.135)self.Text2Var = StringVar(value='')self.Text2 = Entry(self.top, textvariable=self.Text2Var, font=('宋体',9))self.Text2.place(relx=0.018, rely=0.419, relwidth=0.673, relheight=0.554)self.style.configure('TFrame1.TLabelframe', font=('宋体',9))self.style.configure('TFrame1.TLabelframe.Label', font=('宋体',9))self.Frame1 = LabelFrame(self.top, text='数据面板', style='TFrame1.TLabelframe')self.Frame1.place(relx=0.018, rely=0.132, relwidth=0.673, relheight=0.245)self.style.configure('TCommand1.TButton', font=('宋体',9))self.Command1 = Button(self.top, text='登录', command=self.Command1_Cmd, style='TCommand1.TButton')self.Command1.place(relx=0.477, rely=0.044, relwidth=0.091, relheight=0.069)self.Text1Var = StringVar(value='')self.Text1 = Entry(self.top, textvariable=self.Text1Var, font=('宋体',9))self.Text1.place(relx=0.141, rely=0.044, relwidth=0.232, relheight=0.051)self.style.configure('TLabel8.TLabel', anchor='w', font=('宋体', 9))self.Label8 = Label(self.top, text='日志区域', style='TLabel8.TLabel')self.Label8.place(relx=0.6, rely=0.066, relwidth=0.373, relheight=0.047)self.style.configure('TLabel1.TLabel', anchor='w', font=('宋体',9))self.Label1 = Label(self.top, text='游戏ID:', style='TLabel1.TLabel')self.Label1.place(relx=0.018, rely=0.044, relwidth=0.108, relheight=0.047)self.style.configure('TLabel7.TLabel', anchor='w', font=('宋体',9))self.Label7 = Label(self.Frame1, text='0', style='TLabel7.TLabel')self.Label7.place(relx=0.734, rely=0.629, relwidth=0.134, relheight=0.191)self.style.configure('TLabel6.TLabel', anchor='w', font=('宋体',9))self.Label6 = Label(self.Frame1, text='0', style='TLabel6.TLabel')self.Label6.place(relx=0.236, rely=0.629, relwidth=0.134, relheight=0.191)self.style.configure('TLabel5.TLabel', anchor='w', font=('宋体',9))self.Label5 = Label(self.Frame1, text='兑换失败:', style='TLabel5.TLabel')self.Label5.place(relx=0.498, rely=0.629, relwidth=0.213, relheight=0.191)self.style.configure('TLabel4.TLabel', anchor='w', font=('宋体',9))self.Label4 = Label(self.Frame1, text='兑换成功:', style='TLabel4.TLabel')self.Label4.place(relx=0.026, rely=0.629, relwidth=0.213, relheight=0.191)self.style.configure('TLabel3.TLabel', anchor='w', font=('宋体',9))self.Label3 = Label(self.Frame1, text='null', style='TLabel3.TLabel')self.Label3.place(relx=0.262, rely=0.27, relwidth=0.37, relheight=0.191)self.style.configure('TLabel2.TLabel', anchor='w', font=('宋体',9))self.Label2 = Label(self.Frame1, text='游戏昵称:', style='TLabel2.TLabel')self.Label2.place(relx=0.026, rely=0.27, relwidth=0.213, relheight=0.191)self.style.configure('TLabel9.TLabel', anchor='w', font=('宋体', 9))self.Label9 = Label(self.top, text='总计:', style='TLabel9.TLabel')self.Label9.place(relx=0.724, rely=0.419, relwidth=0.091, relheight=0.047)self.style.configure('TLabel10.TLabel', anchor='w', font=('宋体', 9))self.Label10 = Label(self.top, text='null', style='TLabel10.TLabel')self.Label10.place(relx=0.83, rely=0.419, relwidth=0.108, relheight=0.047)self.style.configure('TCommand3.TButton', font=('宋体', 9))self.Command3 = Button(self.top, text='兑换码统计', command=self.Command3_Cmd, style='TCommand3.TButton')self.Command3.place(relx=0.706, rely=0.551, relwidth=0.214, relheight=0.113)class Application(Application_ui):#这个类实现具体的事件处理回调函数。界面生成代码在Application_ui中。def __init__(self, master=None):Application_ui.__init__(self, master)def Command2_Cmd(self, event=None):convert_fail=0convert_success=0# 一键兑换if login_state == 0:messagebox.showinfo(title='温馨提示', message='请先登录')else:if self.Text2Var.get() == '':self.Label8.config(text='兑换码不能为空...')messagebox.showinfo(title='温馨提示', message='兑换码不能为空')else:self.Label8.config(text='校验中...')text = self.Text2Var.get()pattern = r"【(卡\d+|通用)】(.*?)(?=【|$)"result = re.findall(pattern, text)# 总计self.Label10.config(text=len(result))for item in result:time.sleep(1)# item[1]s=item[1]ket_word = s.strip()# 兑换headers = {'Host': 'com-sev.webapp.163.com','Accept': '*/*','Connection': 'keep-alive','User-Agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 16_5_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.5.1 Mobile/15E148 Safari/604.1','Accept-Language': 'zh-CN,zh-Hans;q=0.9','Referer': 'http://party.163.com/dh/m/',}now = datetime.datetime.now()# 将当前时间转换为时间戳(秒级)timestamp = time.mktime(now.timetuple())# 将时间戳转换为毫秒级时间戳millisecond_timestamp = int(timestamp * 1000)params = {'code': ket_word,'uid': username,'host_test': '0','_': millisecond_timestamp,# 'callback': 'jsonp7',}response = requests.get('http://com-sev.webapp.163.com/u5cdkey_redeem/api', params=params,headers=headers)print(response.json())result = response.json()if result['status']:self.Label8.config(text=result['msg'])convert_success+=1print(convert_success)# self.Label6.config(text=convert_success)else:print(ket_word)self.Label8.config(text=result['msg'])convert_fail += 1print(convert_fail)# self.Label7.config(text=convert_fail)self.Label6.config(text=convert_success)self.Label7.config(text=convert_fail)def Command3_Cmd(self, event=None):self.Label8.config(text='正在统计兑换码!')print(f'输入框:{self.Text2Var.get()}')if self.Text2Var.get() == '':self.Label8.config(text='兑换码不能为空...')messagebox.showinfo(title='温馨提示', message='兑换码不能为空')else:self.Label8.config(text='校验中...')text =self.Text2Var.get()pattern = r"【(卡\d+|通用)】(.*?)(?=【|$)"result = re.findall(pattern, text)# 总计self.Label10.config(text=len(result))def Command1_Cmd(self, event=None):global usernameglobal login_stateself.Label8.config(text='正在登陆...')url = 'http://web.taila.club/'response = requests.get(url)# print(response.json()) # 打印响应内容result = response.json()if result['code'] == 100:print(f'输入框:{self.Text1Var.get()}')if self.Text1Var.get() == '':self.Label8.config(text='用户id不能为空...')messagebox.showinfo(title='温馨提示', message='用户id不能为空')else:self.Label8.config(text='获取验证码中...')get_captcha()time.sleep(2)self.Label8.config(text='获取成功...')time.sleep(1)self.Label8.config(text='正在计算验证码...')#headers = {'Host': 'com-sev.webapp.163.com','Accept': '*/*','Content-Type': 'application/json','Connection': 'keep-alive','User-Agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 16_5_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.5.1 Mobile/15E148 Safari/604.1','Accept-Language': 'zh-CN,zh-Hans;q=0.9','Referer': 'http://party.163.com/dh/m/',}params = {'image': base_64_,'token': 'Ysmft8hCyULaAHbeIFuLFNeKy-hsZVjaw9dT6yKBM-Q','type': '10110',}response = requests.post('http://api.jfbym.com/api/YmServer/customApi', data=json.dumps(params),headers=headers)result = response.json()if result['code'] == 10000:print("请求成功")print(result['data']['data'])res = result['data']['data']else:print("验证码获取失败")res = 'iii'## 更换接口为在线的 ddddocr打包失败# ocr = ddddocr.DdddOcr()# with open('captcha.png', 'rb') as f:# img_bytes = f.read()# res = ocr.classification(img_bytes)# print('识别出的验证码为:' + res)yzm_msg = f'识别出的验证码为:{res}'self.Label8.config(text=yzm_msg)# 登录操作headers = {'Host': 'com-sev.webapp.163.com','Accept': '*/*','Connection': 'keep-alive','User-Agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 16_5_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.5.1 Mobile/15E148 Safari/604.1','Accept-Language': 'zh-CN,zh-Hans;q=0.9','Referer': 'http://party.163.com/dh/m/',}now = datetime.datetime.now()# 将当前时间转换为时间戳(秒级)timestamp = time.mktime(now.timetuple())# 将时间戳转换为毫秒级时间戳millisecond_timestamp = int(timestamp * 1000)params = {'img_auth': res,'uid': self.Text1Var.get(),'ctoken_id': ctoken_id,'host_test': '0','_': millisecond_timestamp,# 'callback': 'jsonp3',}response = requests.get('http://com-sev.webapp.163.com/u5cdkey_query_uidinfo/api',params=params,headers=headers,)print(response.text)resul = response.json()if resul['status']:# 208146020self.Label8.config(text='登陆成功...')self.Label3.config(text=resul['nick'])username = self.Text1Var.get()login_state = 1else:print("UID_ERROR")self.Label8.config(text='登陆失败,请尝试')self.Label3.config(text=resul['msg'])self.Label8.config(text='请重新尝试登录')# 501验证码过期else:self.Label8.config(text='授权已被收回.')if __name__ == "__main__":global login_state # 0未登录 1登录global ctoken_id #tokenglobal base_64_global username # 账号login_state=0top = Tk()Application(top).mainloop()
#版本2-本地验证码ddddocr识别
#!/usr/bin/env python
#-*- coding:utf-8 -*-
import datetime
import json
import re
import time
import base64
import ddddocr
from tkinter import messageboximport requeststry:from tkinter import *
except ImportError: #Python 2.xPythonVersion = 2from Tkinter import *from tkFont import Fontfrom ttk import *#Usage:showinfo/warning/error,askquestion/okcancel/yesno/retrycancelfrom tkMessageBox import *#Usage:f=tkFileDialog.askopenfilename(initialdir='E:/Python')#import tkFileDialog#import tkSimpleDialog
else: #Python 3.xPythonVersion = 3from tkinter.font import Fontfrom tkinter.ttk import *from tkinter.messagebox import *#import tkinter.filedialog as tkFileDialog#import tkinter.simpledialog as tkSimpleDialog #askstring()def get_captcha():global ctoken_idglobal base_64_headers = {'Host': 'com-sev.webapp.163.com','Accept': '*/*','Connection': 'keep-alive','User-Agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 16_5_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.5.1 Mobile/15E148 Safari/604.1','Accept-Language': 'zh-CN,zh-Hans;q=0.9','Referer': 'http://party.163.com/dh/m/',}now = datetime.datetime.now()# 将当前时间转换为时间戳(秒级)timestamp = time.mktime(now.timetuple())# 将时间戳转换为毫秒级时间戳millisecond_timestamp = int(timestamp * 1000)params = {'return_type': 'base64','_': millisecond_timestamp,# 'callback': 'jsonp',}response = requests.get('http://com-sev.webapp.163.com/get_apnt_auth_img', params=params,headers=headers)# print(response.text)result = response.json()if result['status']:base64_ = result['base64']ctoken_id = result['ctoken_id']print(base64_)print(ctoken_id)ctoken_id=ctoken_idbase_64_=base64_# base64转换图片-由于接口更换不需要base64_data = base64.b64decode(base64_)# 将base64数据保存为图片with open('captcha.png', 'wb') as f:f.write(base64_data)else:print("验证码获取失败")messagebox.showinfo(title='温馨提示', message='验证码获取失败')
class Application_ui(Frame):#这个类仅实现界面生成功能,具体事件处理代码在子类Application中。def __init__(self, master=None):Frame.__init__(self, master)self.master.title('网易-蛋仔派对礼包码兑换工具-Q390983964')self.master.geometry('453x363')self.createWidgets()# self.Label6.config(text='0')# self.Label7.config(text='0')def createWidgets(self):self.top = self.winfo_toplevel()self.style = Style()self.style.configure('TCommand2.TButton', font=('宋体', 9))self.Command2 = Button(self.top, text='一键兑换', command=self.Command2_Cmd, style='TCommand2.TButton')self.Command2.place(relx=0.706, rely=0.793, relwidth=0.214, relheight=0.135)self.Text2Var = StringVar(value='')self.Text2 = Entry(self.top, textvariable=self.Text2Var, font=('宋体',9))self.Text2.place(relx=0.018, rely=0.419, relwidth=0.673, relheight=0.554)self.style.configure('TFrame1.TLabelframe', font=('宋体',9))self.style.configure('TFrame1.TLabelframe.Label', font=('宋体',9))self.Frame1 = LabelFrame(self.top, text='数据面板', style='TFrame1.TLabelframe')self.Frame1.place(relx=0.018, rely=0.132, relwidth=0.673, relheight=0.245)self.style.configure('TCommand1.TButton', font=('宋体',9))self.Command1 = Button(self.top, text='登录', command=self.Command1_Cmd, style='TCommand1.TButton')self.Command1.place(relx=0.477, rely=0.044, relwidth=0.091, relheight=0.069)self.Text1Var = StringVar(value='')self.Text1 = Entry(self.top, textvariable=self.Text1Var, font=('宋体',9))self.Text1.place(relx=0.141, rely=0.044, relwidth=0.232, relheight=0.051)self.style.configure('TLabel8.TLabel', anchor='w', font=('宋体', 9))self.Label8 = Label(self.top, text='日志区域', style='TLabel8.TLabel')self.Label8.place(relx=0.6, rely=0.066, relwidth=0.373, relheight=0.047)self.style.configure('TLabel1.TLabel', anchor='w', font=('宋体',9))self.Label1 = Label(self.top, text='游戏ID:', style='TLabel1.TLabel')self.Label1.place(relx=0.018, rely=0.044, relwidth=0.108, relheight=0.047)self.style.configure('TLabel7.TLabel', anchor='w', font=('宋体',9))self.Label7 = Label(self.Frame1, text='0', style='TLabel7.TLabel')self.Label7.place(relx=0.734, rely=0.629, relwidth=0.134, relheight=0.191)self.style.configure('TLabel6.TLabel', anchor='w', font=('宋体',9))self.Label6 = Label(self.Frame1, text='0', style='TLabel6.TLabel')self.Label6.place(relx=0.236, rely=0.629, relwidth=0.134, relheight=0.191)self.style.configure('TLabel5.TLabel', anchor='w', font=('宋体',9))self.Label5 = Label(self.Frame1, text='兑换失败:', style='TLabel5.TLabel')self.Label5.place(relx=0.498, rely=0.629, relwidth=0.213, relheight=0.191)self.style.configure('TLabel4.TLabel', anchor='w', font=('宋体',9))self.Label4 = Label(self.Frame1, text='兑换成功:', style='TLabel4.TLabel')self.Label4.place(relx=0.026, rely=0.629, relwidth=0.213, relheight=0.191)self.style.configure('TLabel3.TLabel', anchor='w', font=('宋体',9))self.Label3 = Label(self.Frame1, text='null', style='TLabel3.TLabel')self.Label3.place(relx=0.262, rely=0.27, relwidth=0.37, relheight=0.191)self.style.configure('TLabel2.TLabel', anchor='w', font=('宋体',9))self.Label2 = Label(self.Frame1, text='游戏昵称:', style='TLabel2.TLabel')self.Label2.place(relx=0.026, rely=0.27, relwidth=0.213, relheight=0.191)self.style.configure('TLabel9.TLabel', anchor='w', font=('宋体', 9))self.Label9 = Label(self.top, text='总计:', style='TLabel9.TLabel')self.Label9.place(relx=0.724, rely=0.419, relwidth=0.091, relheight=0.047)self.style.configure('TLabel10.TLabel', anchor='w', font=('宋体', 9))self.Label10 = Label(self.top, text='null', style='TLabel10.TLabel')self.Label10.place(relx=0.83, rely=0.419, relwidth=0.108, relheight=0.047)self.style.configure('TCommand3.TButton', font=('宋体', 9))self.Command3 = Button(self.top, text='兑换码统计', command=self.Command3_Cmd, style='TCommand3.TButton')self.Command3.place(relx=0.706, rely=0.551, relwidth=0.214, relheight=0.113)class Application(Application_ui):#这个类实现具体的事件处理回调函数。界面生成代码在Application_ui中。def __init__(self, master=None):Application_ui.__init__(self, master)def Command2_Cmd(self, event=None):convert_fail=0convert_success=0# 一键兑换if login_state == 0:messagebox.showinfo(title='温馨提示', message='请先登录')else:if self.Text2Var.get() == '':self.Label8.config(text='兑换码不能为空...')messagebox.showinfo(title='温馨提示', message='兑换码不能为空')else:self.Label8.config(text='校验中...')text = self.Text2Var.get()pattern = r"【(卡\d+|通用)】(.*?)(?=【|$)"result = re.findall(pattern, text)# 总计self.Label10.config(text=len(result))for item in result:time.sleep(1)# item[1]s=item[1]ket_word = s.strip()# 兑换headers = {'Host': 'com-sev.webapp.163.com','Accept': '*/*','Connection': 'keep-alive','User-Agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 16_5_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.5.1 Mobile/15E148 Safari/604.1','Accept-Language': 'zh-CN,zh-Hans;q=0.9','Referer': 'http://party.163.com/dh/m/',}now = datetime.datetime.now()# 将当前时间转换为时间戳(秒级)timestamp = time.mktime(now.timetuple())# 将时间戳转换为毫秒级时间戳millisecond_timestamp = int(timestamp * 1000)params = {'code': ket_word,'uid': username,'host_test': '0','_': millisecond_timestamp,# 'callback': 'jsonp7',}response = requests.get('http://com-sev.webapp.163.com/u5cdkey_redeem/api', params=params,headers=headers)print(response.json())result = response.json()if result['status']:self.Label8.config(text=result['msg'])convert_success+=1print(convert_success)# self.Label6.config(text=convert_success)else:print(ket_word)self.Label8.config(text=result['msg'])convert_fail += 1print(convert_fail)# self.Label7.config(text=convert_fail)self.Label6.config(text=convert_success)self.Label7.config(text=convert_fail)def Command3_Cmd(self, event=None):self.Label8.config(text='正在统计兑换码!')print(f'输入框:{self.Text2Var.get()}')if self.Text2Var.get() == '':self.Label8.config(text='兑换码不能为空...')messagebox.showinfo(title='温馨提示', message='兑换码不能为空')else:self.Label8.config(text='校验中...')text =self.Text2Var.get()pattern = r"【(卡\d+|通用)】(.*?)(?=【|$)"result = re.findall(pattern, text)# 总计self.Label10.config(text=len(result))def Command1_Cmd(self, event=None):global usernameglobal login_stateself.Label8.config(text='正在登陆...')url = 'http://web.taila.club/'response = requests.get(url)# print(response.json()) # 打印响应内容result = response.json()if result['code'] == 100:print(f'输入框:{self.Text1Var.get()}')if self.Text1Var.get() == '':self.Label8.config(text='用户id不能为空...')messagebox.showinfo(title='温馨提示', message='用户id不能为空')else:self.Label8.config(text='获取验证码中...')get_captcha()time.sleep(2)self.Label8.config(text='获取成功...')time.sleep(1)self.Label8.config(text='正在计算验证码...')## headers = {# 'Host': 'com-sev.webapp.163.com',# 'Accept': '*/*',# 'Content-Type': 'application/json',# 'Connection': 'keep-alive',# 'User-Agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 16_5_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.5.1 Mobile/15E148 Safari/604.1',# 'Accept-Language': 'zh-CN,zh-Hans;q=0.9',# 'Referer': 'http://party.163.com/dh/m/',# }## params = {# 'image': base_64_,# 'token': 'Ysmft8hCyULaAHbeIFuLFNeKy-hsZVjaw9dT6yKBM-Q',# 'type': '10110',# }## response = requests.post('http://api.jfbym.com/api/YmServer/customApi', data=json.dumps(params),# headers=headers)# result = response.json()# if result['code'] == 10000:# print("请求成功")# print(result['data']['data'])# res = result['data']['data']# else:# print("验证码获取失败")# res = 'iii'## 更换接口为在线的 ddddocr打包失败ocr = ddddocr.DdddOcr()with open('captcha.png', 'rb') as f:img_bytes = f.read()res = ocr.classification(img_bytes)print('识别出的验证码为:' + res)yzm_msg = f'识别出的验证码为:{res}'self.Label8.config(text=yzm_msg)# 登录操作headers = {'Host': 'com-sev.webapp.163.com','Accept': '*/*','Connection': 'keep-alive','User-Agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 16_5_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.5.1 Mobile/15E148 Safari/604.1','Accept-Language': 'zh-CN,zh-Hans;q=0.9','Referer': 'http://party.163.com/dh/m/',}now = datetime.datetime.now()# 将当前时间转换为时间戳(秒级)timestamp = time.mktime(now.timetuple())# 将时间戳转换为毫秒级时间戳millisecond_timestamp = int(timestamp * 1000)params = {'img_auth': res,'uid': self.Text1Var.get(),'ctoken_id': ctoken_id,'host_test': '0','_': millisecond_timestamp,# 'callback': 'jsonp3',}response = requests.get('http://com-sev.webapp.163.com/u5cdkey_query_uidinfo/api',params=params,headers=headers,)print(response.text)resul = response.json()if resul['status']:# 208146020self.Label8.config(text='登陆成功...')self.Label3.config(text=resul['nick'])username = self.Text1Var.get()login_state = 1else:print("UID_ERROR")self.Label8.config(text='登陆失败,请尝试')self.Label3.config(text=resul['msg'])self.Label8.config(text='请重新尝试登录')# 501验证码过期else:self.Label8.config(text='授权已被收回.')if __name__ == "__main__":global login_state # 0未登录 1登录global ctoken_id #tokenglobal base_64_global username # 账号login_state=0top = Tk()Application(top).mainloop()
⭐⭐软件打包
相信能到这里的同学,应该都在cloud stdio中尝试运行过,可惜报错了
这个错误提示说明在你的代码中使用了Tkinter图形界面,但是当前环境下没有可用的图形显示。这通常是因为你正在使用一个没有图形界面的环境,比如SSH远程连接或者在不支持图形界面的服务器上运行脚本,所以在线编辑器目前运行不了,但是我们可以打包下载到本地环境运行
在cloud stdio终端输入
clear
进行日志清空
安装pyinstaller进行打包
pip install pyinstaller
模块安装完后,进行打包操作
pyinstaller -D -w index.py
打包完成后下载即可
本地运行
⭐总结
本次项目用到了在线IDE:Cloud stdio,不得不说真的很方便,我经过这次体验总结出几个优缺点
Cloud stdio | 优点 | 缺点 |
---|---|---|
Python环境-模块 | 自动安装模块 | 大的模块需要手动安装,例如:request自动安装但是Ddddocr需要手动命令安装 |
Python环境-共享库 | 资源齐全,下载速度快,版本不支持也会提醒升级pip | 打包文件时需要的依赖由于环境问题无法拉取打包,无法找到所需的共享库文件来支持打包,其中包括libpython3.9m.so等文件 |
Python环境-可视化 | 可视化界面运行不了,会提示_tkinter.TclError: couldn't connect to display ":0" | |
界面 | 整洁清爽,容易快速上手,可选择基础框架快速开发 | 新建工作空间不能使用本地代码进行上传只能从仓库拉取 |
费用 | 标准版包月300有点贵 |
建议:
优化虚拟模块的拉取方式,使得tk项目可以打包,对一些可视化的容错处理,报错信息再优化,其他做的真不错,可能后面可视化建议有点不切实际,如果价格能够下降一点会更受青睐,以上是我个人的建议。
🍋如果你喜欢爬虫相关,请看下面
《记一次云之家签到抓包》
《记一次视频抓包m3u8解密过程》
《抓包部分软件时无网络+过代理检测 解决办法 安卓黄鸟httpcanary+vmos》
《Python】记录抓包分析自动领取芝麻HTTP每日免费IP(成品+教程)》
《某课抓包视频 安卓手机:黄鸟+某课app+VirtualXposed虚拟框架》
推荐专栏:
《Python爬虫脚本项目实战》
该专栏往期文章:
《【Python爬虫项目实战一】获取Chatgpt3.5免费接口文末付代码(过Authorization认证)》
🥦如果感觉看完文章还不过瘾,欢迎查看我的其它专栏
🥦作者对python有很大的兴趣,完成过很多独立的项目:例如滇医通等等脚本,但是由于版权的原因下架了,爬虫这一类审核比较严谨,稍有不慎就侵权违规了,所以在保证质量的同时会对文章进行筛选
如果您对爬虫感兴趣请收藏或者订阅该专栏哦《Python爬虫脚本项目实战》,如果你有项目欢迎联系我,我会同步教程到本专栏!
🚀Python爬虫项目实战系列文章!!
⭐⭐欢迎订阅⭐⭐
【Python爬虫项目实战一】获取Chatgpt3.5免费接口文末付代码(过Authorization认证)
【Python爬虫项目实战二】Chatgpt还原验证算法-解密某宝伪知网数据接口
⭐⭐欢迎订阅⭐⭐
Python爬虫脚本项目实战
完整项目:https://e.coding.net/coding-damowang/danzaipaiduiduihuanmagongju/workspace.git