import logging
import requests
import re
import ddddocr
import os
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s %(filename)s %(funcName)s:line %(lineno)d %(levelname)s %(message)s"
)
class GamemaleNoCookie:
def __init__(self, username, password, questionid='0', answer=None):
self.ocr = ddddocr.DdddOcr(show_ad=False)
self.post_formhash = None
self.sign_result = None
self.username = str(username)
self.password = str(password)
self.questionid = questionid
self.answer = answer
self.hostname = "www.gamemale.com"
self.session = requests.session()
self.session.headers.update({
'User-Agent': (
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) '
'AppleWebKit/537.36 (KHTML, like Gecko) '
'Chrome/91.0.4472.124 Safari/537.36'
)
})
# 访问登录页并解析出 loginhash 和 formhash
def get_login_formhash(self):
url = f"https://{self.hostname}/member.php?mod=logging&action=login"
text = self.session.get(url).text
loginhash_match = re.search(r'
', text)
formhash_match = re.search(
r'',
text
)
if not loginhash_match or not formhash_match:
logging.debug(f"无法获取 loginhash 或 formhash 时的页面内容:\n{text}")
raise ValueError("无法获取 loginhash 或 formhash")
loginhash = loginhash_match.group(1)
formhash = formhash_match.group(1)
logging.info(f"已成功获取 loginhash 与 formhash")
return loginhash, formhash
# 获取并识别验证码
def verify_code_once(self) -> str:
update_url = (
f"https://{self.hostname}/misc.php?mod=seccode&action=update"
f"&idhash=cSA&0.1234567&modid=member::logging"
)
update_text = self.session.get(update_url).text
update_match = re.search(r"update=(.+?)&idhash=", update_text)
if not update_match:
raise ValueError("无法匹配到验证码 update 参数")
update_val = update_match.group(1)
# 获取验证码图片
code_url = (
f"https://{self.hostname}/misc.php?mod=seccode&update="
f"{update_val}&idhash=cSA"
)
headers = {
'Accept': 'image/webp,image/apng,image/*,*/*;q=0.8',
'Referer': f"https://{self.hostname}/member.php?mod=logging&action=login",
}
code_resp = self.session.get(code_url, headers=headers)
# OCR 识别
return self.ocr.classification(code_resp.content)
# 多次尝试识别、提高成功率
def verify_code(self, max_retries=10) -> str:
for attempt in range(1, max_retries + 1):
code = self.verify_code_once()
verify_url = (
f"https://{self.hostname}/misc.php?mod=seccode&action=check&inajax=1&"
f"modid=member::logging&idhash=cSA&secverify={code}"
)
res = self.session.get(verify_url).text
if "succeed" in res:
logging.info(f"验证码识别成功 (第{attempt}次): {code}")
return code
else:
logging.info(f"验证码识别失败 (第{attempt}次), 继续重试... code={code}")
logging.error("验证码多次识别均失败")
return ""
# 登录
def login_with_verify_code(self) -> bool:
code = self.verify_code()
if not code:
logging.error("未能成功识别验证码,无法继续登录")
return False
loginhash, formhash = self.get_login_formhash()
login_url = (
f"https://{self.hostname}/member.php?mod=logging&action=login"
f"&loginsubmit=yes&loginhash={loginhash}&inajax=1"
)
form_data = {
'formhash': formhash,
'referer': f"https://{self.hostname}/",
'loginfield': self.username,
'username': self.username,
'password': self.password,
'questionid': self.questionid,
'answer': self.answer,
'cookietime': 2592000,
'seccodehash': 'cSA',
'seccodemodid': 'member::logging',
'seccodeverify': code,
}
resp_text = self.session.post(login_url, data=form_data).text
if "succeed" in resp_text:
logging.info("带验证码登录成功")
return True
logging.info("带验证码登录失败,请检查账号或密码是否正确")
return False
# 获取登录状态
def login(self) -> bool:
return self.login_with_verify_code()
# 登录成功后,获取论坛主页的 formhash 用于签到
def after_login_init(self):
forum_url = f"https://{self.hostname}/forum.php"
try:
text = self.session.get(forum_url).text
formhash_match = re.search(
r'',
text
)
if formhash_match:
self.post_formhash = formhash_match.group(1)
logging.info(f"已成功获取论坛主页 formhash")
else:
logging.warning("未能在论坛主页获取到 formhash")
except Exception as e:
logging.error(f"访问论坛主页出错: {e}")
def get_sign_hashcode(self) -> str:
return self.post_formhash or ""
# 签到
def sign_gamemale(self):
hashcode = self.get_sign_hashcode()
if not hashcode:
logging.warning("无法获取签到需要的 hashcode,跳过签到")
return
sign_url = (
f"https://{self.hostname}/k_misign-sign.html?"
f"operation=qiandao&format=button&formhash={hashcode}"
)
try:
resp = self.session.get(sign_url)
response_text = resp.text
if response_text.startswith("")
if cdata_start > 8 and cdata_end > cdata_start:
message = response_text[cdata_start:cdata_end]
else:
message = response_text
else:
message = response_text
if "签到成功" in message:
sign_status = "签到成功"
elif "已签" in message:
sign_status = "今日已签到"
else:
sign_status = "未知状态"
print(f"=== 本次签到结果 ===\n{sign_status}")
self.sign_result = {
"site": "GameMale",
"status": sign_status,
# "message": message
}
logging.info(f"GameMale 签到结果: {sign_status}")
except Exception as e:
logging.error(f"GameMale 签到失败: {e}")
self.sign_result = {
"site": "GameMale",
"status": "签到请求失败",
# "message": str(e)
}
def run(self):
if not self.login():
logging.error("登录失败,流程终止")
return
self.after_login_init()
self.sign_gamemale()
# logging.info(f"GameMale 签到结果: {sign_status} | {message}")
logging.info(f"签到最终结果: {self.sign_result}")
if __name__ == "__main__":
username = os.getenv("USERNAME")
password = os.getenv("PASSWORD")
# questionid = os.getenv("QID")
# answer = os.getenv("ANSWER")
if not username or not password:
print("❌ 错误:未提供用户名或密码")
exit(1)
gm = GamemaleNoCookie(username, password)
gm.run()