Summary: This script automates daily check-in on Fang.com forums. It loads cookies or login states, clicks sign-in/check-in buttons, detects captchas/risks, and saves login status for reuse. Requires server setup and environment configuration.
阿里云:
服务器购买地址
https://t.aliyun.com/U/siC86h若失效,可用地址
https://www.aliyun.com/benefit?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.代码如下
import osimport sysimport jsonimport timefrom pathlib import Pathfrom typing import List, Dict, Optional, Tuplefrom playwright.sync_api import sync_playwright, TimeoutError as PlaywrightTimeoutError# -----------------------------# 基础配置# -----------------------------BASE_URL = "https://bbs.fang.com/"STATE_PATH = Path("fang_storage_state.json")# 建议用 Cookie 登录(更稳定)# 环境变量:FANG_COOKIES_JSON# 例子(Chrome DevTools -> Application -> Cookies 把需要的 cookie 拷出来):# export FANG_COOKIES_JSON='[{"name":"xx","value":"yy","domain":".fang.com","path":"/"}]'FANG_COOKIES_JSON = os.getenv("FANG_COOKIES_JSON", "").strip()# 可选:账号密码(不推荐,容易触发验证码/风控)FANG_USERNAME = os.getenv("FANG_USERNAME", "").strip()FANG_PASSWORD = os.getenv("FANG_PASSWORD", "").strip()# -----------------------------# 工具方法# -----------------------------def log(msg: str):now = time.strftime("%Y-%m-%d %H:%M:%S")print(f"[{now}] {msg}")def load_cookies_from_env() -> List[Dict]:"""从环境变量读取 Cookie JSON"""if not FANG_COOKIES_JSON:return []try:cookies = json.loads(FANG_COOKIES_JSON)if not isinstance(cookies, list):raise ValueError("Cookie JSON 必须是 list")# 简单校验字段for c in cookies:if "name" not in c or "value" not in c:raise ValueError("每条 cookie 至少要有 name/value")c.setdefault("path", "/")return cookiesexcept Exception as e:raise RuntimeError(f"解析 FANG_COOKIES_JSON 失败:{e}")def save_storage_state(context):"""保存登录态(下次可免 Cookie)"""context.storage_state(path=str(STATE_PATH))log(f"已保存登录态到:{STATE_PATH.resolve()}")def is_login_page(html: str) -> bool:"""简单判断是否处于未登录状态"""keywords = ["登录", "注册", "立即登录", "账号登录"]return any(k in html for k in keywords)def detect_risk_or_captcha(page) -> bool:"""检测常见验证码/风控提示(出现则返回 True)"""html = page.content()risk_keywords = ["验证码", "滑块", "请完成验证", "安全验证", "异常访问", "访问过于频繁","robot", "captcha", "verify"]return any(k.lower() in html.lower() for k in risk_keywords)def try_click_by_text(page, texts: List[str], timeout_ms: int = 3000) -> Tuple[bool, str]:"""尝试根据按钮文案点击返回:(是否成功, 命中的文本)"""for t in texts:try:# text selector:尽量点击可见元素loc = page.locator(f"text={t}").firstloc.wait_for(state="visible", timeout=timeout_ms)loc.click(timeout=timeout_ms)return True, texcept Exception:continuereturn False, ""def try_click_by_css_candidates(page, selectors: List[str], timeout_ms: int = 3000) -> bool:"""尝试点击一组 CSS 候选选择器"""for sel in selectors:try:loc = page.locator(sel).firstloc.wait_for(state="visible", timeout=timeout_ms)loc.click(timeout=timeout_ms)return Trueexcept Exception:continuereturn False# -----------------------------# 核心流程# -----------------------------def init_context_with_cookie(browser):"""创建浏览器上下文:优先加载 storage_state,其次注入 cookies"""if STATE_PATH.exists():log("检测到本地登录态文件,优先使用 storage_state")return browser.new_context(storage_state=str(STATE_PATH))ctx = browser.new_context()cookies = load_cookies_from_env()if cookies:log(f"从环境变量注入 Cookie:{len(cookies)} 条")ctx.add_cookies(cookies)else:log("未提供 Cookie,将尝试账号密码登录(可能触发验证码)")return ctxdef login_if_needed(page):"""如果未登录:尝试账号密码登录(可选)注意:房天下可能出现验证码,脚本不做绕过"""if not (FANG_USERNAME and FANG_PASSWORD):returnlog("尝试账号密码登录(若出现验证码需手动处理)")# 打开首页,点击登录入口(文案/入口可能变化)page.goto(BASE_URL, wait_until="domcontentloaded", timeout=30000)time.sleep(2)# 尝试点击"登录"ok, hit = try_click_by_text(page, ["登录", "立即登录", "账号登录"], timeout_ms=5000)if ok:log(f"已点击登录入口:{hit}")time.sleep(2)# 尝试填写账号密码(选择器可能因版本变化而不同,这里做多候选)user_selectors = ['input[type="text"]','input[name="username"]','input[name="mobile"]','input[placeholder*="手机号"]','input[placeholder*="账号"]',]pass_selectors = ['input[type="password"]','input[name="password"]','input[placeholder*="密码"]',]btn_selectors = ['button:has-text("登录")','input[type="submit"]','button[type="submit"]',]# 填账号filled_user = Falsefor sel in user_selectors:try:loc = page.locator(sel).firstloc.wait_for(state="visible", timeout=5000)loc.fill(FANG_USERNAME)filled_user = Truebreakexcept Exception:continue# 填密码filled_pass = Falsefor sel in pass_selectors:try:loc = page.locator(sel).firstloc.wait_for(state="visible", timeout=5000)loc.fill(FANG_PASSWORD)filled_pass = Truebreakexcept Exception:continueif not (filled_user and filled_pass):log("未能自动定位到账号/密码输入框,建议改用 Cookie 登录")return# 点击登录按钮clicked = Falsefor sel in btn_selectors:try:loc = page.locator(sel).firstloc.wait_for(state="visible", timeout=5000)loc.click()clicked = Truebreakexcept Exception:continueif not clicked:log("未能自动定位登录按钮,建议改用 Cookie 登录")returntime.sleep(3)if detect_risk_or_captcha(page):log("检测到验证码/风控页面:请手动完成验证后再重跑(脚本不绕过验证码)")returnlog("账号密码登录流程已执行(成功与否以页面实际为准)")def perform_daily_checkin(page) -> bool:"""执行签到:多策略查找"签到/打卡/领取"等入口并点击"""page.goto(BASE_URL, wait_until="domcontentloaded", timeout=30000)time.sleep(2)if detect_risk_or_captcha(page):log("进入首页即出现验证码/风控,停止执行")return False# 1) 先用常见文本匹配(不同站点可能写"签到/打卡/每日签到/领取/做任务")text_candidates = ["签到", "每日签到", "打卡", "签到领奖", "签到领", "领取","领积分", "任务", "做任务", "福利", "签到有礼",]ok, hit = try_click_by_text(page, text_candidates, timeout_ms=2500)if ok:log(f"命中文案入口:{hit},已点击")time.sleep(3)# 2) 再尝试一些可能的按钮/链接结构(兜底 CSS)css_candidates = ['a[href*="sign"]','a[href*="qiandao"]','a[href*="checkin"]','a:has-text("签到")','button:has-text("签到")',]if try_click_by_css_candidates(page, css_candidates, timeout_ms=2500):log("命中 CSS 入口并点击")time.sleep(3)if detect_risk_or_captcha(page):log("点击入口后出现验证码/风控,停止执行")return False# 3) 在签到页里继续找"签到/立即签到/领取"等确认按钮confirm_texts = ["签到", "立即签到", "确认签到", "领取", "立即领取", "一键领取", "去签到"]ok2, hit2 = try_click_by_text(page, confirm_texts, timeout_ms=3000)if ok2:log(f"已点击确认按钮:{hit2}")time.sleep(2)if detect_risk_or_captcha(page):log("确认签到后出现验证码/风控,停止执行")return False# 4) 简单判断结果:页面包含"已签到/签到成功"等html = page.content()success_keywords = ["已签到", "签到成功", "打卡成功", "领取成功", "今天已签到"]if any(k in html for k in success_keywords):log("检测到成功关键词:疑似签到成功/已签到")return True# 如果没有关键词,也不一定失败(有些页面异步弹 toast)log("未检测到明确成功关键词:可能已签到,也可能入口不同(建议手动确认一次)")return Truedef main():"""主入口:- 初始化上下文(storage_state / cookies)- 访问首页,必要时尝试账号密码登录- 执行签到- 保存登录态"""with sync_playwright() as p:browser = p.chromium.launch(headless=True)context = init_context_with_cookie(browser)page = context.new_page()# 先访问一次首页,判断登录态page.goto(BASE_URL, wait_until="domcontentloaded", timeout=30000)time.sleep(2)# 如未登录且提供了账号密码,则尝试登录if is_login_page(page.content()) and (FANG_USERNAME and FANG_PASSWORD):login_if_needed(page)# 执行签到ok = perform_daily_checkin(page)# 保存登录态(如果成功登录过/或 cookie 生效)try:save_storage_state(context)except Exception as e:log(f"保存登录态失败(可忽略):{e}")context.close()browser.close()if not ok:sys.exit(1)if __name__ == "__main__":main()
自动加载 Cookie / 登录态,尽量免登录进入房天下论坛
自动寻找"签到/打卡/领取/任务"等入口并点击,完成每日签到动作
检测验证码/风控页面,一旦出现立即停止并提示人工处理
保存 storage_state,下次执行更稳定(无需反复注入 Cookie)
主要方法
load_cookies_from_env→ 从环境变量读取并解析 Cookie 列表(JSON 格式),用于免登录。save_storage_state→ 把当前登录态保存为fang_storage_state.json,下次直接复用。is_login_page→ 通过页面关键词判断是否处于未登录状态。detect_risk_or_captcha→ 检测页面是否出现验证码/风控提示,避免脚本继续误操作。try_click_by_text→ 按候选文案(如"签到/打卡/领取")寻找可见元素并点击。try_click_by_css_candidates→ 按候选 CSS 选择器兜底点击(如a[href*="qiandao"])。init_context_with_cookie→ 创建浏览器上下文:优先加载storage_state,否则注入 Cookie。login_if_needed→ 若未登录且你提供了账号密码,则尝试走一次登录流程(不处理验证码)。perform_daily_checkin→ 执行签到主流程:进首页 → 点入口 → 点确认 → 判断"已签到/成功"。main→ 脚本入口:初始化 → 判断登录 → 签到 → 保存登录态 → 退出。
注意:
本文部分变量已做脱敏处理,仅用于测试和学习研究,禁止用于商业用途,不能保证其合法性,准确性,完整性和有效性,请根据情况自行判断。技术层面需要提供帮助,可以通过打赏的方式进行探讨。
没有评论:
发表评论