This script automates daily check-ins on yizlife.com. It logs in via UI, finds and clicks check-in buttons, and logs results. Requires server setup (Alibaba/Tencent/Huawei Cloud) and QingLong panel deployment for scheduling. Includes retry logic and file logging.
阿里云:
服务器购买地址
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 reimport timeimport loggingfrom typing import Optional, List, Tuplefrom playwright.sync_api import sync_playwright, Page, TimeoutError as PWTimeoutError# =========================# 配置区# =========================BASE_URL = os.getenv("YIZLIFE_BASE_URL", "http://www.yizlife.com/")USERNAME = os.getenv("YIZLIFE_USERNAME", "")PASSWORD = os.getenv("YIZLIFE_PASSWORD", "")HEADLESS = os.getenv("YIZLIFE_HEADLESS", "true").strip().lower() not in ("0", "false", "off")TIMEOUT_MS = int(os.getenv("YIZLIFE_TIMEOUT_MS", "20000"))RETRY_TIMES = int(os.getenv("YIZLIFE_RETRY_TIMES", "2"))LOG_FILE = os.getenv("YIZLIFE_LOG_FILE", "yizlife_checkin.log")# 常见论坛(Discuz 等)签到插件入口CANDIDATE_SIGNIN_PATHS = ["plugin.php?id=dsu_paulsign:sign","plugin.php?id=k_misign:sign","plugin.php?id=dc_signin:signin","plugin.php?id=signin:signin","plugin.php?id=checkin:checkin","home.php?mod=spacecp&ac=credit","home.php?mod=spacecp&ac=credit&op=log",]# 在页面上识别SIGNIN_BUTTON_KEYWORDS = ["签到", "打卡", "签 到", "立即签到", "每日签到", "领取", "领取奖励", "签到领奖", "一键签到"]# 判断成功/已签的关键字SUCCESS_KEYWORDS = ["签到成功", "打卡成功", "已签到", "今日已签到", "已经签到", "领取成功", "获得", "奖励已发放"]# =========================# 日志# =========================def setup_logger() -> logging.Logger:logger = logging.getLogger("yizlife_checkin")logger.setLevel(logging.INFO)logger.handlers.clear()fmt = logging.Formatter("%(asctime)s | %(levelname)s | %(message)s")sh = logging.StreamHandler()sh.setFormatter(fmt)logger.addHandler(sh)fh = logging.FileHandler(LOG_FILE, encoding="utf-8")fh.setFormatter(fmt)logger.addHandler(fh)return loggerlogger = setup_logger()# =========================# 工具方法# =========================def normalize_url(base: str, path: str) -> str:if base.endswith("/") and path.startswith("/"):return base[:-1] + pathif (not base.endswith("/")) and (not path.startswith("/")):return base + "/" + pathreturn base + pathdef page_has_keywords(page: Page, keywords: List[str]) -> bool:content = page.content()return any(k in content for k in keywords)def safe_click(page: Page, selector: str, timeout_ms: int = 5000) -> bool:try:page.locator(selector).first.wait_for(state="visible", timeout=timeout_ms)page.locator(selector).first.click(timeout=timeout_ms)return Trueexcept Exception:return Falsedef find_and_click_by_text(page: Page, keywords: List[str]) -> Optional[str]:"""尝试在页面中查找包含关键词的按钮/链接并点击。返回点击到的关键词(用于日志),找不到返回 None。"""# 先查 button / a / div[role=button] 这类常见交互元素candidates = ["button","a","[role=button]","input[type=button]","input[type=submit]",]for kw in keywords:for css in candidates:loc = page.locator(css, has_text=kw)try:if loc.count() > 0:loc.first.click(timeout=3000)return kwexcept Exception:continuereturn None# =========================# 核心流程方法# =========================def open_home(page: Page) -> None:logger.info(f"打开站点首页: {BASE_URL}")page.goto(BASE_URL, wait_until="domcontentloaded", timeout=TIMEOUT_MS)page.wait_for_timeout(1500)def try_login(page: Page) -> bool:"""尝试用 UI 方式登录:点击"登录/会员登录"等入口,填表单提交。"""if not USERNAME or not PASSWORD:logger.error("未配置账号密码:请设置环境变量 YIZLIFE_USERNAME / YIZLIFE_PASSWORD")return False# 若页面已有"退出/注销/我的"等字样,可能已登录if page_has_keywords(page, ["退出", "注销", "欢迎您", "消息", "个人中心", "我的"]):logger.info("检测到疑似已登录状态,跳过登录步骤。")return True# 1) 尝试点击"登录"clicked = find_and_click_by_text(page, ["登录", "立即登录", "会员登录", "账号登录"])if clicked:logger.info(f"已点击登录入口关键词: {clicked}")page.wait_for_timeout(1200)# 2) 若是 Discuz 常见登录页,也可能是 member.php?mod=logging&action=login# 这里不强依赖 URL,只要能找到账号密码输入框即可。username_selectors = ["input[name='username']","input[name='user']","input[name='email']","input[placeholder*='用户名']","input[placeholder*='账号']","input[type='text']",]password_selectors = ["input[name='password']","input[type='password']","input[placeholder*='密码']",]submit_selectors = ["button[type='submit']","input[type='submit']","button:has-text('登录')","input[value*='登录']","a:has-text('登录')",]# 3) 填账号filled_user = Falsefor sel in username_selectors:loc = page.locator(sel)try:if loc.count() > 0:loc.first.fill(USERNAME, timeout=3000)filled_user = Truelogger.info(f"已填写账号输入框: {sel}")breakexcept Exception:continue# 4) 填密码filled_pwd = Falsefor sel in password_selectors:loc = page.locator(sel)try:if loc.count() > 0:loc.first.fill(PASSWORD, timeout=3000)filled_pwd = Truelogger.info(f"已填写密码输入框: {sel}")breakexcept Exception:continueif not (filled_user and filled_pwd):logger.error("未找到可用的账号/密码输入框(站点结构可能特殊)。")return False# 5) 提交submitted = Falsefor sel in submit_selectors:if safe_click(page, sel, timeout_ms=4000):logger.info(f"已点击登录提交控件: {sel}")submitted = Truebreakif not submitted:# 兜底:回车提交try:page.keyboard.press("Enter")logger.info("未找到提交按钮,已尝试按 Enter 提交登录。")submitted = Trueexcept Exception:passpage.wait_for_timeout(2500)# 6) 判断是否登录成功(弱判断)if page_has_keywords(page, ["退出", "注销", "欢迎您", "消息", "个人中心", "我的"]):logger.info("登录成功(根据页面关键字判断)。")return True# 有些论坛登录后会跳转首页,尝试回首页再判断一次try:page.goto(BASE_URL, wait_until="domcontentloaded", timeout=TIMEOUT_MS)page.wait_for_timeout(1500)except Exception:passif page_has_keywords(page, ["退出", "注销", "欢迎您", "消息", "个人中心", "我的"]):logger.info("登录成功(跳转后判断)。")return Truelogger.warning("登录结果不确定:未匹配到明显的已登录标识。")return True # 允许继续走签到逻辑(有些站点登录标识不明显)def try_signin_by_candidate_paths(page: Page) -> Tuple[bool, str]:"""逐个访问候选签到入口路径,尝试在页面中点击"签到/打卡/领取"。"""for p in CANDIDATE_SIGNIN_PATHS:url = normalize_url(BASE_URL, p)logger.info(f"尝试签到入口: {url}")try:page.goto(url, wait_until="domcontentloaded", timeout=TIMEOUT_MS)page.wait_for_timeout(1200)clicked_kw = find_and_click_by_text(page, SIGNIN_BUTTON_KEYWORDS)if clicked_kw:logger.info(f"在候选入口页点击到按钮关键词: {clicked_kw}")page.wait_for_timeout(2000)if page_has_keywords(page, SUCCESS_KEYWORDS):return True, f"候选入口 {p} 签到成功/已签(命中关键字)"# 有些站点签到成功是弹窗或局部刷新,这里做宽松返回return True, f"候选入口 {p} 已执行点击({clicked_kw}),请以站点结果为准"# 若页面本身就提示已签到if page_has_keywords(page, SUCCESS_KEYWORDS):return True, f"候选入口 {p} 显示已签到/成功"except PWTimeoutError:logger.warning(f"访问候选入口超时: {url}")except Exception as e:logger.warning(f"访问候选入口异常: {url} | {e}")return False, "未在候选入口中找到可执行的签到按钮/成功标识"def try_signin_from_home(page: Page) -> Tuple[bool, str]:"""在首页/常见导航中直接查找"签到/打卡"入口并点击。"""logger.info("回到首页尝试直接查找签到入口…")page.goto(BASE_URL, wait_until="domcontentloaded", timeout=TIMEOUT_MS)page.wait_for_timeout(1500)clicked_kw = find_and_click_by_text(page, SIGNIN_BUTTON_KEYWORDS)if clicked_kw:logger.info(f"首页点击到关键词: {clicked_kw}")page.wait_for_timeout(2000)if page_has_keywords(page, SUCCESS_KEYWORDS):return True, "首页签到成功/已签(命中关键字)"return True, f"首页已点击({clicked_kw}),请以站点结果为准"return False, "首页未找到明显签到/打卡入口"def run_checkin_once(page: Page) -> Tuple[bool, str]:open_home(page)if not try_login(page):return False, "登录失败(未找到输入框或账号密码未配置)"# 先从首页找入口,再走候选路径兜底ok, msg = try_signin_from_home(page)if ok:return True, msgok, msg = try_signin_by_candidate_paths(page)return ok, msgdef main() -> None:logger.info("======== 亦庄生活 自动签到脚本启动 ========")logger.info(f"BASE_URL={BASE_URL} | headless={HEADLESS} | retries={RETRY_TIMES}")with sync_playwright() as p:browser = p.chromium.launch(headless=HEADLESS)context = browser.new_context()page = context.new_page()page.set_default_timeout(TIMEOUT_MS)last_err = ""for attempt in range(RETRY_TIMES + 1):try:logger.info(f"--- 第 {attempt + 1}/{RETRY_TIMES + 1} 次尝试 ---")ok, msg = run_checkin_once(page)logger.info(f"结果: {ok} | {msg}")if ok:logger.info("======== 执行完成(成功/已执行) ========")breaklast_err = msgexcept Exception as e:last_err = str(e)logger.error(f"执行异常: {e}")finally:if attempt < RETRY_TIMES:time.sleep(2)if last_err and attempt >= RETRY_TIMES:logger.error(f"最终失败: {last_err}")context.close()browser.close()if __name__ == "__main__":main()
自动打开亦庄生活(yizlife.com)网页
使用账号密码完成登录(UI方式)
自动寻找并执行"签到/打卡/领取奖励"等动作
记录日志到
yizlife_checkin.log,并带重试机制
主要方法
setup_logger:初始化控制台 + 文件日志输出,便于排查登录/签到失败原因。
normalize_url:把基础域名与相对路径拼成可访问的完整URL。
page_has_keywords:通过页面HTML内容判断是否出现"已签到/签到成功"等关键字。
safe_click:安全点击指定选择器,失败不抛异常(用于兜底)。
find_and_click_by_text:在页面里扫描按钮/链接等元素,按关键词(签到/打卡/领取)查找并点击。
open_home:打开站点首页并等待页面加载。
try_login:通过UI方式点击"登录"入口、填写账号密码并提交,完成登录(弱判断已登录状态)。
try_signin_from_home:在首页直接查找"签到/打卡"入口并点击,优先走最自然路径。
try_signin_by_candidate_paths:若首页找不到签到入口,则遍历常见论坛"签到插件路径",进入后再自动点击签到按钮。
run_checkin_once:一次完整流程:打开首页 → 登录 → 首页签到 → 候选入口兜底签到。
main:脚本入口:启动浏览器、执行流程、失败自动重试并输出最终结果。
注意:
本文部分变量已做脱敏处理,仅用于测试和学习研究,禁止用于商业用途,不能保证其合法性,准确性,完整性和有效性,请根据情况自行判断。技术层面需要提供帮助,可以通过打赏的方式进行探讨。
没有评论:
发表评论