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 jsonfrom datetime import datetimefrom pathlib import Pathfrom typing import List, Tuplefrom playwright.sync_api import sync_playwright, Page, BrowserContextBASE_URL = "https://www.fubbs.cn/"OUT_DIR = Path("./fubbs_artifacts")OUT_DIR.mkdir(parents=True, exist_ok=True)HEADLESS = os.getenv("FUBBS_HEADLESS", "true").strip().lower() not in ("0", "false", "off", "no")COOKIE_RAW = os.getenv("FUBBS_COOKIE", "").strip()# -------------------------# 工具:落盘/日志# -------------------------def now_str() -> str:return datetime.now().strftime("%Y%m%d_%H%M%S")def save_text(name: str, content: str) -> Path:p = OUT_DIR / namep.write_text(content, encoding="utf-8", errors="ignore")return pdef print_kv(title: str, kv: dict):print(f"\n=== {title} ===")for k, v in kv.items():print(f"- {k}: {v}")# -------------------------# Cookie 解析与注入# -------------------------def parse_cookie_header(cookie_header: str, domain: str) -> List[dict]:"""把 "a=1; b=2; ..." 解析成 Playwright 可用 cookies 列表"""cookies = []parts = [x.strip() for x in cookie_header.split(";") if x.strip()]for part in parts:if "=" not in part:continuename, value = part.split("=", 1)cookies.append({"name": name.strip(),"value": value.strip(),"domain": domain,"path": "/",})return cookiesdef inject_cookies(context: BrowserContext, cookie_header: str):# 主域名cookies = parse_cookie_header(cookie_header, domain=".fubbs.cn")if not cookies:raise ValueError("Cookie 解析失败:FUBBS_COOKIE 内容不符合 'name=value; name2=value2' 格式")context.add_cookies(cookies)# -------------------------# 入口探测(尽量覆盖常见论坛体系)# -------------------------def candidate_urls() -> List[str]:"""常见社区/论坛签到入口猜测(Discuz / 通用)- 如果网站结构不同,脚本会保存截图/HTML,方便你定位真实入口"""base = BASE_URL.rstrip("/")return [BASE_URL,f"{base}/home.php?mod=space&do=profile",f"{base}/home.php?mod=spacecp",f"{base}/home.php?mod=spacecp&ac=credit",f"{base}/home.php?mod=spacecp&ac=credit&op=base",# 常见签到插件(Discuz)f"{base}/plugin.php?id=dsu_paulsign:sign",f"{base}/plugin.php?id=dsu_paulsign:sign&operation=qiandao",f"{base}/plugin.php?id=k_misign:sign",f"{base}/plugin.php?id=fx_checkin:checkin",f"{base}/plugin.php?id=dc_signin:signin",# 通用路径f"{base}/signin",f"{base}/sign",f"{base}/checkin",f"{base}/qiandao",f"{base}/task",f"{base}/tasks",f"{base}/mission",f"{base}/missions",f"{base}/my",f"{base}/me",f"{base}/user",f"{base}/account",f"{base}/integral",f"{base}/points",]def looks_logged_in(html_text: str) -> bool:"""粗略判断是否登录成功:命中多个关键词就认为 Cookie 有效"""patterns = [r"退出|登出|注销",r"消息|提醒|通知",r"我的|个人中心|设置|头像",r"积分|金币|威望|经验|贡献",]hit = sum(1 for p in patterns if re.search(p, html_text))return hit >= 2# -------------------------# 页面动作:找"签到/领取"按钮并点击# -------------------------def find_and_click_sign_buttons(page: Page) -> Tuple[bool, str]:"""在页面中查找"签到/打卡/领取/一键签到"等按钮并点击返回:(是否点击到, 描述)"""keywords = ["签到", "打卡", "领取", "去签到", "一键签到", "立即签到","每日签到", "补签", "领取奖励", "领取积分", "领积分", "任务"]# 文本定位(按钮/链接/文本)for kw in keywords:locators = [page.get_by_role("button", name=re.compile(kw)),page.get_by_role("link", name=re.compile(kw)),page.locator(f"text={kw}"),]for loc in locators:try:if loc.count() > 0:el = loc.firstif el.is_visible():el.click(timeout=3000)return True, f"已点击文本命中的元素:{kw}"except Exception:pass# CSS 猜测(常见 class/id)css_candidates = ["[class*='sign']","[class*='checkin']","[class*='qiandao']","[id*='sign']","[id*='checkin']","[id*='qiandao']","a:has-text('签到')","button:has-text('签到')","a:has-text('打卡')","button:has-text('打卡')","a:has-text('领取')","button:has-text('领取')",]for sel in css_candidates:try:loc = page.locator(sel)if loc.count() > 0:el = loc.firstif el.is_visible():el.click(timeout=3000)return True, f"已点击CSS命中的元素:{sel}"except Exception:passreturn False, "未找到可点击的签到/领取按钮"def detect_sign_result(html_text: str) -> Tuple[bool, str]:"""通过关键字判断是否"已签到/签到成功""""ok_patterns = [r"签到成功", r"打卡成功", r"领取成功", r"已签到", r"今日已签到", r"连续签到",r"获得\s*\d+\s*(积分|金币|经验|威望|贡献|红包)",]for p in ok_patterns:if re.search(p, html_text):return True, f"命中成功/已签文案:{p}"need_login_patterns = [r"登录", r"请先登录", r"用户名", r"密码", r"验证码"]for p in need_login_patterns:if re.search(p, html_text):return False, f"疑似未登录或Cookie无效:{p}"return False, "未识别到明确的成功/已签文案(可能入口不对或异步加载)"# -------------------------# 主流程# -------------------------def run():if not COOKIE_RAW:print("❌ 未设置环境变量 FUBBS_COOKIE,无法免登录执行。")print("✅ 解决办法:登录 fubbs.cn 后复制 Cookie,设置:")print(" Windows(PowerShell): $env:FUBBS_COOKIE='name=value; name2=value2'")print(" Linux/macOS: export FUBBS_COOKIE='name=value; name2=value2'")sys.exit(1)with sync_playwright() as p:browser = p.chromium.launch(headless=HEADLESS)context = browser.new_context()page = context.new_page()# 注入 Cookieinject_cookies(context, COOKIE_RAW)# 打开首页验证登录态page.goto(BASE_URL, wait_until="domcontentloaded", timeout=30000)time.sleep(2)home_html = page.content()login_ok = looks_logged_in(home_html)# 保存首页快照stamp = now_str()page.screenshot(path=str(OUT_DIR / f"home_{stamp}.png"), full_page=True)save_text(f"home_{stamp}.html", home_html)print_kv("登录态检测", {"base_url": BASE_URL,"headless": str(HEADLESS),"looks_logged_in": str(login_ok),"artifacts_dir": str(OUT_DIR.resolve()),})report = {"base_url": BASE_URL,"looks_logged_in": login_ok,"attempts": [],"final": {"success": False, "message": ""},}for url in candidate_urls():try:print(f"\n➡️ 尝试入口: {url}")page.goto(url, wait_until="domcontentloaded", timeout=30000)time.sleep(2)clicked, click_msg = find_and_click_sign_buttons(page)time.sleep(2)html_text = page.content()success, result_msg = detect_sign_result(html_text)snap_name = f"try_{re.sub(r'[^a-zA-Z0-9]+', '_', url)[:60]}_{now_str()}"page.screenshot(path=str(OUT_DIR / f"{snap_name}.png"), full_page=True)save_text(f"{snap_name}.html", html_text)item = {"url": url,"clicked": clicked,"click_msg": click_msg,"success": success,"result_msg": result_msg,"page_title": page.title(),"screenshot": f"{snap_name}.png","html": f"{snap_name}.html",}report["attempts"].append(item)print_kv("本次尝试结果", item)if success:report["final"] = {"success": True,"message": f"签到/领取疑似成功({result_msg}),入口:{url}",}breakexcept Exception as e:err = str(e)print(f"⚠️ 入口异常: {err}")report["attempts"].append({"url": url, "error": err})report_path = OUT_DIR / f"report_{now_str()}.json"report_path.write_text(json.dumps(report, ensure_ascii=False, indent=2), encoding="utf-8")print(f"\n📄 运行报告已保存: {report_path.resolve()}")if report["final"]["success"]:print(f"✅ {report['final']['message']}")sys.exit(0)else:print("❌ 未能确认签到成功。")print("建议:打开 fubbs_artifacts 目录查看最后一张 try_*.png 截图,")sys.exit(2)if __name__ == "__main__":run()
该脚本为书香小说自动签到任务脚本,主要作用包括:
免登录自动化:从环境变量读取 Cookie 注入浏览器,实现"已登录态"访问 fubbs.cn。
自动探测签到入口:遍历一批常见签到/任务中心 URL,自动找到最可能的签到页。
自动点击签到/领取按钮:识别页面中的"签到/打卡/领取/一键签到"等按钮并点击。
可排障落盘:每一步都保存截图、HTML、JSON 报告,方便你快速定位"真实签到路径/按钮选择器"。
主要方法
parse_cookie_header(cookie_header, domain)
把name=value; name2=value2解析为 Playwright 可用的 cookie 列表。inject_cookies(context, cookie_header)
将 cookie 注入浏览器上下文,后续访问默认处于登录态。candidate_urls()
生成候选入口列表(含通用路径 + 常见 Discuz 插件路径),用于自动碰撞探测真实签到页。looks_logged_in(html_text)
通过关键词(退出/个人中心/积分等)判断 Cookie 是否大概率有效。find_and_click_sign_buttons(page)
两段策略找按钮并点击:
1)文本关键词(签到/领取等)
2)CSS 猜测(class/id 包含 sign/checkin/qiandao)detect_sign_result(html_text)
检测"签到成功/已签到/获得积分"等文案,判断是否成功。run()
主流程:注入 Cookie → 打开首页验证 → 遍历入口探测 → 点击签到 → 判断结果 → 保存报告/截图。
注意:
本文部分变量已做脱敏处理,仅用于测试和学习研究,禁止用于商业用途,不能保证其合法性,准确性,完整性和有效性,请根据情况自行判断。技术层面需要提供帮助,可以通过打赏的方式进行探讨。
没有评论:
发表评论