某编程学习论坛python脚本实现自动登陆,签到领金币
本帖最后由 二十瞬 于 2023-3-15 22:31 编辑这个是某编程学习网站的登陆页面.验证码不复杂.签到可以得金币,金币可以兑换课程.手动领取太麻烦了.
网站地址 aHR0cHM6Ly93d3cuamF2YXh4ei5jb20=
首先进入登陆页面
用Charles抓包分析获取二维码图片的链接实际上测试这些都是固定值. 那个小数应该是为了避免浏览器缓存.
每次刷新页面都不一样.我们可以在python直接发请求.
也可以直接从登陆页面直接拿链接请求
然后问题就是识别二维码. 这个问题先放一放.看下登陆请求.
其中formhash参数不知道哪里来的在Charles搜一下,或者控制台搜索,发现是登陆页面隐藏字段.
拿过来用就是了.
同样的path也可以直接从form的action拿.其余的账号密码填上就是了其中
seccodeverifycr27这个是验证码.
好了整个流程还是很简单.没有加密没有混淆.好多参数直接在页面就可以拿到.
接下来说说怎么识别验证码.使用的 ddddocr
ddddocr是由sml2h3开发的专为验证码厂商进行对自家新版本验证码难易强度进行验证的一个python库,
其由作者与kerlomz共同合作完成,通过大批量生成随机数据后进行深度网络训练,本身并非针对任何一家验证码厂商而制作,
本库使用效果完全靠玄学,可能可以识别,可能不能识别。
ddddocr奉行着开箱即用、最简依赖的理念,尽量减少用户的配置和使用成本,希望给每一位测试者带来舒适的体验
项目地址: https://github.com/sml2h3/ddddocr.
我下面的代码是采用的部署成服务方式调用来处理的.我签到任务是在青龙面板docker中执行的.在青龙面板中安装ddddocr依赖失败了.
就调用部署在宿主机的接口来进行识别.
可以参考python | 傻瓜式一键搭建图片验证码识别接口https://blog.csdn.net/qq_41437542/article/details/129193844部署api.
如果你在环境安装ddddocr依赖没问题就直接把decodeYzm改成直接用ddddocr方式去处理.就不需要http方式去处理.
base64_data = base64.b64decode(ImageBase64)
ocr = ddddocr.DdddOcr(show_ad=False)
res = ocr.classification(base64_data)
return {"result": res}
下面代码支持多账号.
需要修改的有几个地方.
domain 改为正确的域名,
push_plus_token 的值.
账号密码.
验证码识别接口地址.或者直接修改成ddddocr直接识别.
最后效果
代码有详细注释可以参考
import base64
import sys
import requests
import os
from bs4 import BeautifulSoup
import random
# 很重要用session方式就不用手动处理cookie.注意换账号登陆时候记得清除cookie
session = requests.Session()
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36 Edg/110.0.1587.69"
}
# 域名请自行解码
domain = "aHR0cHM6Ly93d3cuamF2YXh4ei5jb20="
# 这样开着代{过}{滤}理的时候不会报连接错误方便测试
os.environ['NO_PROXY'] = domain
#在 https://www.pushplus.plus 免费获取
push_plus_token = "你的token"ocrapi = "http://ip:port"accounts = [{"username": "账号1", "password": "密码1"},
{"username": "账号2", "password": "密码2"},
{"username": "账号3", "password": "密码3"},
{"username": "账号4", "password": "密码4"}
]
#模拟随机生产五位数字
def getNums(length):
the_str = ""
for i in range(length):
ch = chr(random.randrange(ord('0'), ord('9') + 1))
the_str += ch
return the_str
# 获取验证码
def getYzmImg():
rdnum = getNums(5)
url = f"{domain}/misc.php?mod=seccode&update={rdnum}&idhash=cS"
getHeader = {"referer": f"{domain}/member.php?mod=logging&action=login&phonelogin=no",
"user-agent": headers['User-Agent']}
get = session.get(url, headers=getHeader)
# print(get.content)
# print(f"getYzmImg cookie:{session.cookies}")
return get.content
# 调用搭建的验证码识别api服务.采用这种方式是因为在青龙面板中安装相关依赖失败了.
def decodeYzm(img_types):
img_base64 = base64.b64encode(img_types).decode()
# print(img_base64)
post_headers = {"content-type": "application/json"}
res = session.post(url=f"{ocrapi}/code/", headers=post_headers, json={'ImageBase64': img_base64})
# print(res.text)
return eval(res.text)['result']
# 获取登陆需要的formhash参数action时间上好像是定值,不过在form里面就顺便拿过来了
def getLoginParam():
url = f"{domain}/member.php?mod=logging&action=login&phonelogin=no"
response = session.get(url, headers=headers)
response.encoding = 'utf-8'
soup = BeautifulSoup(response.text, "html.parser")
formhash = soup.find('input', attrs={'name': 'formhash'})["value"]
action = soup.find('form', attrs={'name': 'login'})["action"]
# print(f"formhash:{formhash}")
# print(f"action:{action}")
param = {"formhash": formhash, "action": action}
return param
# 执行登陆逻辑
def doLoginOnce(param, user, yzm):
login_dict = {
"formhash": param['formhash'],
"referer": f"{domain}/./",
"loginfield": "username",
"username": user["username"],
"password": user["password"],
"questionid": "0",
"answer": "",
"seccodemodid": "member::logging",
"seccodeverify": yzm
}
res = session.post(url=f"{domain}/{param['action']}", headers=headers, data=login_dict)
# 登陆成功后返回的页面内容会包含用户名
if user["username"] in res.text:
# print(f"登陆结果:{res.text}")
return True
return False
# 登陆并签到
def doLoginAndSign(user):
i = 1
login_param = getLoginParam()
login_suc = doLoginOnce(login_param, user, decodeYzm(getYzmImg()))
# 验证码识别错误,登陆失败的话 重试
while i < 7 and not login_suc:
print(f"{user['username']}登陆失败,第{i}次重新尝试")
login_suc = doLoginOnce(login_param, user, decodeYzm(getYzmImg()))
i += 1
if login_suc:
doSign(user)
# 执行签到操作
def doSign(user):
index = session.get(f"{domain}/index.php", headers=headers)
soup = BeautifulSoup(index.text, "html.parser")
sign_form_hash = soup.find('input', attrs={'name': 'formhash'})["value"]
sign_before_action = f"plugin.php?id=dsu_paulsign:sign&{sign_form_hash}&infloat=yes&handlekey=dsu_paulsign&inajax=1&ajaxtarget=fwin_content_dsu_paulsign"
sign_before_res = session.get(f"{domain}/{sign_before_action}", headers=headers)
# print(f"签到弹框:{sign_before_res.text}")
acton = "plugin.php?id=dsu_paulsign%3Asign&operation=qiandao&infloat=1&sign_as=1&inajax=1"
qd_param = {"formhash": {sign_form_hash}, "qdxq": "kx"}
res = session.post(url=f"{domain}/{acton}", headers=headers, data=qd_param)
print(f"{user['username']}签到结果:{res.text}")
sendNotify(f"java学习{user['username']}签到成功", res.text)
# 发送微信push_plus消息
def sendNotify(title, content):
param = {"title": title,
"content": content,
"template": "html",
"token": f"{push_plus_token}",
}
requests.post(url="https://www.pushplus.plus/send", headers=headers, data=param)
if __name__ == '__main__':
for account in accounts:
doLoginAndSign(account)
session.cookies.clear()
sys.exit(0)
dft2010 发表于 2023-3-16 00:34
学习一下,为什么最近网址都这样加密了
稍微遮掩一下. 稍微知道的一看就知道怎么还原.毕竟拿来就用 .怕对别人网站产生不好的影响 "username": user["username"],
TypeError: list indices must be integers or slices, not str
查了一下,说是list的索引必须是整数或者片,而不是str 学习一下,为什么最近网址都这样加密了 先保存源代码,慢慢研究一下 {:1_911:}gx感谢分享 插个眼,明天有空看看 先插眼,改天研究一下 学习一下,感谢分享 谢谢 学习 学习 可以dock运行吗 插个眼明天看 不错的教程。