discuz 论坛登录还是比较简单的,而且通用性比较强。python代码中主要使用了 requests 库,简单易用。
登录简要步骤:
- 获取
loginhash
、formhash
- 如果涉及验证码,需要通过获取
update
值来获取图片,并且识别验证码和校验 - 综合以上信息与账号验证信息,发送登录请求
import re
import requests
class DiscuzLogin:
proxies = {
'http': 'http://127.0.0.1:1080',
'https': 'https://127.0.0.1:1080'
}
def __init__(self, hostname, username, password, questionid='0', answer=None, proxies=None):
self.session = requests.session()
self.hostname = hostname
self.username = username
self.password = password
self.questionid = questionid
self.answer = answer
if proxies:
self.proxies = proxies
@classmethod
def user_login(cls, hostname, username, password, questionid='0', answer=None, proxies=None):
user = DiscuzLogin(hostname, username, password, questionid, answer, proxies)
user.login()
def form_hash(self):
rst = self.session.get(f'https://{self.hostname}/member.php?mod=logging&action=login').text
loginhash = re.search(r'<div id="main_messaqge_(.+?)">', rst).group(1)
formhash = re.search(r'<input type="hidden" name="formhash" value="(.+?)" />', rst).group(1)
return loginhash, formhash
def login(self):
loginhash, formhash = self.form_hash()
login_url = f'https://{self.hostname}/member.php?mod=logging&action=login&loginsubmit=yes&loginhash={loginhash}&inajax=1'
formData = {
'formhash': formhash,
'referer': f'https://{self.hostname}/',
'loginfield': self.username,
'username': self.username,
'password': self.password,
'questionid': self.questionid,
'answer': self.answer,
'cookietime': 2592000
}
login_rst = self.session.post(login_url, proxies=self.proxies, data=formData)
if self.session.cookies.get('xxzo_2132_auth'):
print(f'Welcome {self.username}!')
else:
raise ValueError('Verify Failed! Check your username and password!')
if __name__ == '__main__':
DiscuzLogin.user_login('hostname', 'username', 'password')
DiscuzLogin 对象
DiscuzLogin.form_hash(): 获取loginhash
、formhash
DiscuzLogin.login(): 发送登录请求
username
: 用户名
password
: 密码,根据 discuz 的设定,可能直接传递,也可能传递密码的 MD5 值
questionid
: 验证问题的id,默认值即没有设置
answer
: 验证问题的答案,默认为空
proxies
: 代理信息,默认使用了ss,可以直接删去,亦可自行传入
如果请求发送的是密码的 MD5 值:
import hashlib
def md5(password):
return hashlib.md5(password.encode(encoding='UTF-8')).hexdigest()
发送登录请求后,通过判断 cookies 中是否存在xxzo_2132_auth
来判断是否登录成功。
如果登录验证失败,也可考虑把错误信息展示出来。
验证码
如果 discuz 设置开启了验证码,需要通过update
的值来获取图片,获取、识别后可以通过验证接口来进行检查。这里略过图片中验证码的识别。
def update_value(self):
rst = self.session.get(f'https://{self.hostname}/misc.php?mod=seccode&action=update&idhash=cS&0.3701502461393815&modid=member::logging').text
update = re.search(r'update=(.+?)&idhash=', rst).group(1)
return update
def get_code_png(self):
code_headers = {
'Accept': 'image/webp,image/apng,image/*,*/*;q=0.8',
'Accept-Encoding': 'gzip, deflate, br',
'Accept-Language': 'zh-CN,zh;q=0.9',
'Connection': 'keep-alive',
'hostname': f'{self.hostname}',
'Referer': f'https://{self.hostname}/member.php?mod=logging&action=login',
'Sec-Fetch-Mode': 'no-cors',
'Sec-Fetch-Site': 'same-origin',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36'
}
rst = self.session.get(f'https://{self.hostname}/misc.php?mod=seccode&update={self.update_value()}&idhash=cS',
headers=code_headers)
with open('code.png', 'wb') as f:
f.write(rst.content)
def check_code(self, code):
check_rst = self.session.get(
f'https://{self.hostname}/misc.php?mod=seccode&action=check&inajax=1&modid=member::logging&idhash=cS&secverify={code}')
print(check_rst.text)
同时登录提交的表单中,要相应增加验证信息:
formData = {
'seccodehash': 'cS',
'seccodemodid': 'member::logging',
'seccodeverify': '', # verify code
}