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, Optional, Tuplefrom playwright.sync_api import sync_playwright, Page, BrowserContextBASE_URL = "https://www.19lou.com/"OUT_DIR = Path("./19lou_artifacts")OUT_DIR.mkdir(parents=True, exist_ok=True)HEADLESS = os.getenv("LOU19_HEADLESS", "true").strip().lower() not in ("0", "false", "off", "no")COOKIE_RAW = os.getenv("LOU19_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):# 19lou 主域名cookies = parse_cookie_header(cookie_header, domain=".19lou.com")if not cookies:raise ValueError("Cookie 解析失败:LOU19_COOKIE 内容不符合 'name=value; name2=value2' 格式")context.add_cookies(cookies)# -------------------------# 签到入口探测# -------------------------def candidate_urls() -> List[str]:"""常见论坛/社区/任务中心路径(尽量覆盖)真实网站可能变更:脚本会逐一访问并尝试识别"签到/打卡/领取"按钮。"""return [BASE_URL,BASE_URL.rstrip("/") + "/task",BASE_URL.rstrip("/") + "/tasks",BASE_URL.rstrip("/") + "/mission",BASE_URL.rstrip("/") + "/missions",BASE_URL.rstrip("/") + "/user",BASE_URL.rstrip("/") + "/my",BASE_URL.rstrip("/") + "/me",BASE_URL.rstrip("/") + "/signin",BASE_URL.rstrip("/") + "/sign",BASE_URL.rstrip("/") + "/checkin",BASE_URL.rstrip("/") + "/qiandao",BASE_URL.rstrip("/") + "/daily",BASE_URL.rstrip("/") + "/integral",# 兼容部分论坛程序(discuz 类)BASE_URL.rstrip("/") + "/plugin.php?id=dsu_paulsign:sign",BASE_URL.rstrip("/") + "/plugin.php?id=dsu_paulsign:sign&operation=qiandao",BASE_URL.rstrip("/") + "/home.php?mod=spacecp&ac=credit&op=base",]def looks_logged_in(html_text: str) -> bool:"""粗略判断是否登录成功(不同站点文案不同)"""patterns = [r"退出|登出|注销",r"消息|通知|我的|个人中心|头像",r"积分|金币|经验",]score = 0for p in patterns:if re.search(p, html_text):score += 1return score >= 2def find_and_click_sign_buttons(page: Page) -> Tuple[bool, str]:"""在页面中查找"签到/打卡/领取/去签到/一键签到"等按钮并点击返回:(是否点击到有效按钮, 描述)"""# 常见关键词keywords = ["签到", "打卡", "领取", "去签到", "一键签到", "立即签到","每日签到", "已签到", "补签", "领积分", "领取积分", "领红包"]# 先用文本定位(button/a/div/span 都试)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']","button:has-text('签到')","a: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"验证码登录"]for p in need_login_patterns:if re.search(p, html_text):return False, f"疑似未登录:{p}"return False, "未识别到明确的成功/已签文案(可能页面异步、或入口不对)"# -------------------------# 主流程# -------------------------def run():if not COOKIE_RAW:print("❌ 未设置环境变量 LOU19_COOKIE,无法免登录执行。")print("✅ 解决办法:登录 19lou 后复制 Cookie,设置:")print(" Windows(PowerShell): $env:LOU19_COOKIE='name=value; name2=value2'")print(" Linux/macOS: export LOU19_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)# 保存首页快照page.screenshot(path=str(OUT_DIR / f"home_{now_str()}.png"), full_page=True)save_text(f"home_{now_str()}.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)attempt_item = {"url": url,"clicked": clicked,"click_msg": click_msg,"success": success,"result_msg": result_msg,"screenshot": f"{snap_name}.png","html": f"{snap_name}.html","page_title": page.title(),}report["attempts"].append(attempt_item)print_kv("本次尝试结果", attempt_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("建议你打开 artifacts 目录:")print("1) 看最后一次 try_*.png 截图,确认真实入口/按钮文案")print("2) 把真实签到页 URL 或按钮的 CSS/XPath 发我,我可以帮你把脚本改成"100%命中"的版本。")sys.exit(2)if __name__ == "__main__":run()
该脚本为自动签到19楼脚本,主要作用包括:
Cookie 免登录自动化:从环境变量读取
LOU19_COOKIE,注入到浏览器上下文,直接以登录态访问 19楼。多入口探测:自动尝试一组常见的"签到/任务中心/积分"等入口 URL,避免你手动抓真正路径。
自动点击签到按钮:在页面内扫描"签到/打卡/领取/一键签到"等关键词按钮并点击。
结果识别与留痕:判断是否出现"已签到/签到成功/领取成功"等文案;无论成功失败都会保存截图+HTML+JSON报告,便于快速定位真实入口与选择器。
主要方法
parse_cookie_header(cookie_header, domain)作用:把浏览器里复制的 Cookie 字符串
a=1; b=2解析成 Playwright 需要的 cookie 对象列表。意义:实现"免输入账号密码"的自动化登录态。
inject_cookies(context, cookie_header)作用:把解析后的 cookies 注入浏览器上下文(
context.add_cookies)。意义:后续访问 19楼的任何页面,都自动带上登录态。
candidate_urls()作用:提供一组"可能的签到/任务/积分入口 URL"候选清单。
意义:不依赖你提前知道真实签到链接,脚本自动碰撞探测。
looks_logged_in(html_text)作用:根据页面关键词(如"退出/个人中心/积分"等)粗略判断是否处于登录状态。
意义:帮助你快速判断 Cookie 是否有效。
find_and_click_sign_buttons(page)作用:在当前页面用两层策略找按钮并点击:
意义:尽量通吃不同页面结构,降低你手动改脚本成本。
文本策略:用"签到/打卡/领取"等关键词在按钮/链接/文本中匹配;
CSS 猜测:匹配一些常见的
sign/checkin/qiandao类名或 id。detect_sign_result(html_text)作用:识别是否出现"签到成功/已签到/获得积分"等关键词,判断本次尝试是否成功。
意义:自动判断结果,不靠你肉眼看。
run()作用:主流程编排:注入 Cookie → 打开首页 → 逐入口探测 → 点击按钮 → 判断结果 → 落盘证据与 JSON 报告 → 给出最终退出码。
意义:工程化可排障、可复用、可持续维护。
注意:
本文部分变量已做脱敏处理,仅用于测试和学习研究,禁止用于商业用途,不能保证其合法性,准确性,完整性和有效性,请根据情况自行判断。技术层面需要提供帮助,可以通过打赏的方式进行探讨。
没有评论:
发表评论