1.购买服务器阿里云:服务器购买地址https://t.aliyun.com/U/55RK8C若失效,可用地址
阿里云:
服务器购买地址
https://t.aliyun.com/U/55RK8C若失效,可用地址
https://www.aliyun.com/daily-act/ecs/activity_selection?source=5176.29345612&userCode=49hts92d腾讯云:
https://curl.qcloud.com/wJpWmSfU若失效,可用地址
https://cloud.tencent.com/act/cps/redirect?redirect=2446&cps_key=ad201ee2ef3b771157f72ee5464b1fea&from=console华为云
https://activity.huaweicloud.com/cps.html?fromacct=64b5cf7cc11b4840bb4ed2ea0b2f4468&utm_source=V1g3MDY4NTY=&utm_medium=cps&utm_campaign=2019052.部署教程
3.代码如下
# -*- coding: utf-8 -*-import osimport reimport sysimport timeimport jsonimport tracebackfrom urllib.parse import urljoin, unquote, urlparseimport requestsfrom bs4 import BeautifulSoupBASE_URL = "https://www.0573ren.com/"UA = ("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 ""(KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36")TIMEOUT = 20class SignError(Exception):passclass Discuz0573RenSigner:def __init__(self, base_url: str, username: str, password: str):self.base_url = base_url.rstrip("/") + "/"self.username = usernameself.password = passwordself.sess = requests.Session()self.sess.headers.update({"User-Agent": UA,"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8","Accept-Language": "zh-CN,zh;q=0.9","Connection": "keep-alive",})# ---------------------- 基础工具方法 ----------------------def _get(self, url: str, **kwargs) -> requests.Response:return self.sess.get(url, timeout=TIMEOUT, allow_redirects=True, **kwargs)def _post(self, url: str, data=None, **kwargs) -> requests.Response:return self.sess.post(url, data=data, timeout=TIMEOUT, allow_redirects=True, **kwargs)def _abs(self, path_or_url: str) -> str:return urljoin(self.base_url, path_or_url)def _soup(self, html: str) -> BeautifulSoup:return BeautifulSoup(html, "lxml")def _extract_formhash(self, html: str) -> str | None:"""Discuz 常见 formhash 出现形式:- <input type="hidden" name="formhash" value="xxxx">- formhash=xxxx- 'formhash' : 'xxxx'"""m = re.search(r'name=["\']formhash["\']\s+value=["\']([a-zA-Z0-9]+)["\']', html)if m:return m.group(1)m = re.search(r"formhash=([a-zA-Z0-9]+)", html)if m:return m.group(1)m = re.search(r"['\"]formhash['\"]\s*[:=]\s*['\"]([a-zA-Z0-9]+)['\"]", html)if m:return m.group(1)return Nonedef _looks_like_logged_in(self, html: str) -> bool:# Discuz 登录后通常有 "退出" "设置" "消息" "我的" 等元素keywords = ["退出", "设置", "消息", "提醒", "我的", "space-uid", "home.php?mod=space"]return any(k in html for k in keywords)# ---------------------- 核心流程方法 ----------------------def fetch_login_params(self) -> dict:"""抓取登录页,提取 loginhash / formhash / referer 等必要参数"""login_page = self._abs("member.php?mod=logging&action=login")r = self._get(login_page)if r.status_code != 200:raise SignError(f"打开登录页失败: HTTP {r.status_code}")html = r.text# loginhash 形如:loginhash=xxxxxxm = re.search(r"loginhash=([a-zA-Z0-9]+)", html)loginhash = m.group(1) if m else Noneformhash = self._extract_formhash(html)# referer 一般可用首页referer = self.base_urlif not formhash:# 有些站点登录页不带 formhash,需要再访问一次首页或其它页面补home = self._get(self.base_url)formhash = self._extract_formhash(home.text)if not formhash:raise SignError("未能提取 formhash(站点结构可能有变或被反爬拦截)")return {"loginhash": loginhash, "formhash": formhash, "referer": referer}def login(self) -> None:"""执行 Discuz 登录"""params = self.fetch_login_params()loginhash = params["loginhash"]formhash = params["formhash"]referer = params["referer"]# 登录提交地址(Discuz 常规)# member.php?mod=logging&action=login&loginsubmit=yes&inajax=1submit_url = "member.php?mod=logging&action=login&loginsubmit=yes"if loginhash:submit_url += f"&loginhash={loginhash}"submit_url += "&inajax=1"url = self._abs(submit_url)payload = {"formhash": formhash,"referer": referer,"loginfield": "username","username": self.username,"password": self.password,"questionid": "0","answer": "",}headers = {"Referer": self._abs("member.php?mod=logging&action=login"),"X-Requested-With": "XMLHttpRequest",}r = self._post(url, data=payload, headers=headers)text = r.text or ""# 登录成功的判断方式:再拉一次首页看是否出现"退出/space-uid"home = self._get(self.base_url)if not self._looks_like_logged_in(home.text):# 给出更多诊断信息brief = text[:300].replace("\n", " ")raise SignError(f"登录可能失败(未检测到登录态)。登录响应片段: {brief}")def discover_signin_entry(self) -> dict:"""登录后从页面中探测"签到入口链接"返回:{"type": "...", "url": "..."} 或抛错"""# 有的站点签到入口在首页;有的在"插件"页;这里多试几个入口candidate_pages = [self.base_url,self._abs("plugin.php"),self._abs("home.php?mod=spacecp"),]patterns = [# PaulSign:最常见("dsu_paulsign", re.compile(r"(plugin\.php\?id=dsu_paulsign:sign[^\"'\s]*)", re.I)),# misign:另一常见签到插件("k_misign", re.compile(r"(plugin\.php\?id=k_misign:sign[^\"'\s]*)", re.I)),# 其它可能存在的签到插件关键字(兜底)("signin_generic", re.compile(r"(plugin\.php\?id=[^\"'\s]*(sign|signin|qiandao)[^\"'\s]*)", re.I)),]for page_url in candidate_pages:r = self._get(page_url)html = r.text or ""for typ, pat in patterns:m = pat.search(html)if m:found = unquote(m.group(1))return {"type": typ, "url": self._abs(found)}# 如果页面没直接露出链接,再尝试"关键词检索"#(例如菜单里写"签到/打卡/每日签到"等)home = self._get(self.base_url).textif any(k in home for k in ["签到", "打卡", "每日签到"]):# 但没抓到链接,提示用户手动抓一次签到页URLraise SignError("页面出现"签到/打卡"字样但未解析到入口URL,可能链接由JS生成或被混淆。")raise SignError("未探测到签到入口(可能没有开放签到插件,或需要在个人中心/任务中心进入)")def do_signin_paulsign(self, entry_url: str) -> str:"""dsu_paulsign:sign 常见签到流程:1) GET entry_url 拿 formhash2) POST plugin.php?id=dsu_paulsign:sign&operation=qiandao&inajax=1"""r = self._get(entry_url)html = r.text or ""formhash = self._extract_formhash(html)if not formhash:raise SignError("Paulsign 签到页未提取到 formhash")post_url = self._abs("plugin.php?id=dsu_paulsign:sign&operation=qiandao&inajax=1")# 这些字段在不同站点可能略有差异,但大多数兼容:payload = {"formhash": formhash,"qdxq": "kx", # 心情:kx/ym/wl/nu/ch/yl...(站点自定义)"qdmode": "1", # 签到模式"todaysay": "", # 签到内容"fastreply": "0",}headers = {"Referer": entry_url,"X-Requested-With": "XMLHttpRequest",}resp = self._post(post_url, data=payload, headers=headers)text = resp.text or ""# Discuz inajax 返回可能是 XML/HTML/JSON 片段,这里做关键词判定if any(k in text for k in ["已经签到", "今日已签到", "您今日已经签到", "已签", "success"]):return "今日已签到"if any(k in text for k in ["签到成功", "恭喜", "获得", "success"]):return "签到成功"if "formhash" in text and "invalid" in text.lower():return "签到失败:formhash无效(可能需要重新登录或入口不是此插件)"# 输出部分响应帮助排查return f"签到结果未知(响应片段):{text[:200].replace(chr(10), ' ')}"def do_signin_misign(self, entry_url: str) -> str:"""k_misign:sign 常见签到流程(站点差异更大):1) GET entry_url 拿 formhash2) POST plugin.php?id=k_misign:sign&operation=qiandao&inajax=1"""r = self._get(entry_url)html = r.text or ""formhash = self._extract_formhash(html)if not formhash:raise SignError("MiSign 签到页未提取到 formhash")post_url = self._abs("plugin.php?id=k_misign:sign&operation=qiandao&inajax=1")payload = {"formhash": formhash,"qdmode": "1","todaysay": "",}headers = {"Referer": entry_url,"X-Requested-With": "XMLHttpRequest",}resp = self._post(post_url, data=payload, headers=headers)text = resp.text or ""if any(k in text for k in ["已经签到", "今日已签到", "您今日已经签到", "已签", "success"]):return "今日已签到"if any(k in text for k in ["签到成功", "恭喜", "获得", "success"]):return "签到成功"return f"签到结果未知(响应片段):{text[:200].replace(chr(10), ' ')}"def sign(self) -> str:"""总控:探测插件 -> 执行签到"""entry = self.discover_signin_entry()typ = entry["type"]url = entry["url"]if typ == "dsu_paulsign":return self.do_signin_paulsign(url)if typ == "k_misign":return self.do_signin_misign(url)# 兜底:探测到疑似签到插件,但不在内置列表return f"已发现疑似签到入口但未内置适配:{url}(type={typ})。你把该页源码/入口URL发我,我可补齐提交参数。"def run(self) -> str:"""主执行入口:登录 -> 签到"""self.login()return self.sign()def main():username = os.getenv("R0573REN_USERNAME", "").strip()password = os.getenv("R0573REN_PASSWORD", "").strip()if not username or not password:print("❌ 缺少环境变量:R0573REN_USERNAME / R0573REN_PASSWORD")sys.exit(1)signer = Discuz0573RenSigner(BASE_URL, username, password)try:print(f"🌐 目标站点:{BASE_URL}")print("🔐 开始登录...")result = signer.run()print(f"✅ 执行完成:{result}")sys.exit(0)except Exception as e:print(f"❌ 执行失败:{e}")print(traceback.format_exc())sys.exit(2)if __name__ == "__main__":main()
解析
该脚本为嘉兴人论坛自动签到脚本,主要作用包括
自动登录
https://www.0573ren.com/(常见登录流程:抓loginhash/formhash→ POST 登录)登录后自动探测论坛使用的是哪种"签到插件入口"(例如
dsu_paulsign/k_misign)根据插件类型,自动提交每日签到请求
输出签到结果:
签到成功 / 今日已签到 / 响应片段用于排查
主要方法
fetch_login_params:打开登录页并提取登录所需参数(如loginhash、formhash、referer)。login:执行登录提交并校验登录态(通过首页是否出现"退出/space-uid"等特征判断)。discover_signin_entry:登录后从首页/插件页/个人中心页探测签到入口 URL,并判断插件类型。do_signin_paulsign:针对dsu_paulsign插件的签到实现(GET 拿 formhash → POSToperation=qiandao)。do_signin_misign:针对k_misign插件的签到实现(GET 拿 formhash → POSToperation=qiandao)。sign:签到总控方法:根据探测到的插件类型选择对应签到实现,并做兜底提示。run:执行主流程:登录 → 签到 → 返回结果。main:脚本入口:读取环境变量、初始化对象、捕获异常并设置退出码。
注意:
本文部分变量已做脱敏处理,仅用于测试和学习研究,禁止用于商业用途,不能保证其合法性,准确性,完整性和有效性,请根据情况自行判断。技术层面需要提供帮助,可以通过打赏的方式进行探讨。
没有评论:
发表评论