**摘要**:提供服务器购买、青龙面板部署及自动签到脚本。脚本通过Playwright登录并同步Cookie,支持Discuz常见签到入口,输出结构化结果。需配置环境变量运行。
阿里云:
服务器购买地址
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, urlparseimport requestsfrom bs4 import BeautifulSoupfrom playwright.sync_api import sync_playwrightBASE_URL = "https://www.mala.cn/"UA = ("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 ""(KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36")class MalaSignBot:def __init__(self, username: str, password: str, headless: bool = True):self.username = usernameself.password = passwordself.headless = headlessself.session = requests.Session()self.session.headers.update({"User-Agent": UA})# =========================# 1) Playwright 登录拿 cookie# =========================def login_and_export_cookies(self) -> None:"""用浏览器登录后,把 cookie 同步到 requests.Session"""with sync_playwright() as p:browser = p.chromium.launch(headless=self.headless)context = browser.new_context(user_agent=UA)page = context.new_page()page.goto(BASE_URL, wait_until="domcontentloaded", timeout=60_000)# 尝试找 Discuz 登录入口(常见:member.php?mod=logging...)login_url = self._discover_login_url(page.url, page.content())if not login_url:# 兜底:直接拼常见登录入口login_url = urljoin(BASE_URL, "member.php?mod=logging&action=login")page.goto(login_url, wait_until="domcontentloaded", timeout=60_000)# 常见 Discuz 登录表单字段# username: name="username" 或 "loginfield" + "username"# password: name="password"# 如果站点改了字段名,你就需要在这里按页面结构改一下 selectortry:# 有些站点输入框可能不是标准 name,这里做一组兜底user_sel_candidates = ['input[name="username"]','input[id*="username"]','input[placeholder*="用户名"]','input[placeholder*="账号"]','input[type="text"]',]pass_sel_candidates = ['input[name="password"]','input[id*="password"]','input[placeholder*="密码"]','input[type="password"]',]user_box = Nonefor sel in user_sel_candidates:loc = page.locator(sel)if loc.count() > 0:user_box = loc.firstbreakif not user_box:raise RuntimeError("未找到用户名输入框(请打开非 headless 模式观察页面后调整 selector)")pass_box = Nonefor sel in pass_sel_candidates:loc = page.locator(sel)if loc.count() > 0:pass_box = loc.firstbreakif not pass_box:raise RuntimeError("未找到密码输入框(请打开非 headless 模式观察页面后调整 selector)")user_box.fill(self.username)pass_box.fill(self.password)# 提交按钮:常见 class/id/name 关键字 submit/loginsubmit_candidates = ['button[type="submit"]','input[type="submit"]','button:has-text("登录")','input[value*="登录"]',]clicked = Falsefor sel in submit_candidates:loc = page.locator(sel)if loc.count() > 0:loc.first.click()clicked = Truebreakif not clicked:# 兜底:按回车提交pass_box.press("Enter")# 等待跳转/登录态出现(比如"退出/设置/消息/个人中心"等)page.wait_for_timeout(3000)except Exception as e:browser.close()raise RuntimeError(f"登录步骤失败:{e}")# 取 cookie 同步到 requestscookies = context.cookies()browser.close()# 把 playwright cookies 写入 requests sessionfor ck in cookies:domain = ck.get("domain", "")name = ck.get("name")value = ck.get("value")if not name:continue# requests 的 cookie domain 写法略不同,这里尽量兼容self.session.cookies.set(name, value, domain=domain.lstrip("."), path=ck.get("path", "/"))def _discover_login_url(self, current_url: str, html_text: str) -> str | None:"""从首页/当前页面源码里,尝试找登录入口链接"""soup = BeautifulSoup(html_text, "lxml")for a in soup.select("a[href]"):href = a.get("href", "")if "member.php" in href and "mod=logging" in href and "action=login" in href:return urljoin(current_url, href)return None# =========================# 2) 探测 formhash# =========================def get_formhash(self) -> str | None:"""Discuz 常用的 CSRF 字段:formhash"""resp = self.session.get(BASE_URL, timeout=30)resp.raise_for_status()m = re.search(r'name="formhash"\s+value="([^"]+)"', resp.text)if m:return m.group(1)# 兜底:从 JS 变量里找m2 = re.search(r'formhash\s*=\s*"([^"]+)"', resp.text)if m2:return m2.group(1)return None# =========================# 3) 执行签到(多路径候选 + 自动提交)# =========================def try_signin(self) -> dict:"""尝试多种 Discuz/插件签到入口:- plugin.php?id=dsu_paulsign:sign- plugin.php?id=dsu_paulsign- 其他常见签到插件入口(需要你根据实际页面补充)"""formhash = self.get_formhash()# 候选签到入口(你可以把日志里探测到的真实入口补进来)candidates = [# 最常见:dsu_paulsign"plugin.php?id=dsu_paulsign:sign","plugin.php?id=dsu_paulsign",# 其他可能的签到入口(不同站点会有差异)"plugin.php?id=dc_signin","plugin.php?id=dd_sign","k_misign-sign.html","home.php?mod=spacecp&ac=credit&op=base",]# 1) 先 GET 入口页,看看是否存在签到表单(formhash/按钮/提示)for path in candidates:url = urljoin(BASE_URL, path)try:r = self.session.get(url, timeout=30)if r.status_code >= 400:continue# 如果页面里出现"请先登录",说明登录没成功if "登录" in r.text and ("请先登录" in r.text or "您需要先登录" in r.text):return {"ok": False, "stage": "signin", "message": "登录态无效:访问签到页提示未登录", "url": url}# 如果是 dsu_paulsign,通常有 formhash 和提交 actionif "dsu_paulsign" in r.text or "paulsign" in r.text or "签到" in r.text:# 2) 尝试从签到页抽 form action / formhashaction_url = self._extract_form_action(url, r.text) or urlfh = self._extract_formhash_from_html(r.text) or formhash# 如果仍拿不到 formhash,基本无法提交if not fh:continue# 3) 组装常见签到 payload(dsu_paulsign 常用字段)payload = {"formhash": fh,"qdxq": "kx", # 心情:kx/ym/wl/ng/fd(不同站点可不同,但一般不严格校验)"qdmode": "1", # 签到模式"todaysay": "每日签到", # 签到说说"fastreply": "0","submit": "true",}# 4) POST 提交pr = self.session.post(action_url, data=payload, timeout=30)# 5) 解析结果:常见返回关键字text = pr.textif any(k in text for k in ["签到成功", "恭喜", "已签到", "已经签到", "success"]):return {"ok": True, "stage": "signin", "message": "签到成功/已签到", "url": action_url}# 有些站点会跳转到提示页:再看 Locationif pr.status_code in (301, 302, 303, 307, 308):return {"ok": True, "stage": "signin", "message": "签到请求已提交(发生跳转)", "url": action_url}# 如果失败,把部分信息带回(方便你定位实际字段/入口)return {"ok": False,"stage": "signin","message": "签到提交可能失败(未命中成功关键字),建议开启 headless=false 观察实际签到入口与字段","url": action_url,"debug_snippet": text[:500],}except Exception:continuereturn {"ok": False,"stage": "discover","message": "未在候选路径中找到可用签到入口。建议:先手动登录并签到一次,然后把签到页面 URL 发我,我帮你把入口/字段定死。",}def _extract_form_action(self, page_url: str, html_text: str) -> str | None:"""从 HTML 表单中提取 action(用于 POST)"""soup = BeautifulSoup(html_text, "lxml")form = soup.select_one("form[action]")if not form:return Noneaction = form.get("action", "").strip()if not action:return Nonereturn urljoin(page_url, action)def _extract_formhash_from_html(self, html_text: str) -> str | None:m = re.search(r'name="formhash"\s+value="([^"]+)"', html_text)return m.group(1) if m else Nonedef main():username = os.getenv("MALA_USERNAME", "").strip()password = os.getenv("MALA_PASSWORD", "").strip()headless_env = os.getenv("MALA_HEADLESS", "true").strip().lower()headless = headless_env not in ("false", "0", "off", "no")if not username or not password:print("❌ 缺少账号密码环境变量:MALA_USERNAME / MALA_PASSWORD")sys.exit(1)bot = MalaSignBot(username=username, password=password, headless=headless)try:print("1) 开始登录并同步 Cookie ...")bot.login_and_export_cookies()print("✅ 登录成功,Cookie 已同步")print("2) 开始尝试签到 ...")result = bot.try_signin()print("=== 签到结果 ===")print(json.dumps(result, ensure_ascii=False, indent=2))sys.exit(0 if result.get("ok") else 2)except Exception as e:print("❌ 脚本执行异常:", str(e))print(traceback.format_exc())sys.exit(3)if __name__ == "__main__":main()
该脚本为麻辣社区自动签到脚本,主要功能包括:
自动打开
mala.cn,完成账号登录登录成功后把浏览器 Cookie 同步到
requests会话自动探测 Discuz 常见
formhash(CSRF token)依次尝试多个常见签到入口(如
dsu_paulsign插件入口),并自动提交签到表单输出结构化结果(成功/失败原因、使用的 URL、失败时截断的响应片段)
主要方法
login_and_export_cookies→ 使用 Playwright 完成网页登录,并将登录后的 Cookie 同步到requests.Session,为后续 HTTP 方式签到做准备。_discover_login_url→ 从首页 HTML 中尝试自动发现登录入口(常见member.php?mod=logging&action=login)。get_formhash→ 获取 Discuz 常见 CSRF 参数formhash(从首页或脚本变量中提取)。try_signin→ 核心签到流程:遍历候选签到入口,GET 判断是否为签到页,抽取表单 action/formhash,POST 提交签到并判定结果。_extract_form_action→ 从签到页 HTML 中提取<form action="...">作为 POST 提交地址。_extract_formhash_from_html→ 从签到页 HTML 中提取隐藏字段formhash。main→ 读取环境变量、初始化机器人、执行登录与签到、输出结果并设置退出码。
注意:
本文部分变量已做脱敏处理,仅用于测试和学习研究,禁止用于商业用途,不能保证其合法性,准确性,完整性和有效性,请根据情况自行判断。技术层面需要提供帮助,可以通过打赏的方式进行探讨。
没有评论:
发表评论