164 lines
4.8 KiB
Python
164 lines
4.8 KiB
Python
#!/usr/bin/env python3
|
||
# -*- coding: utf-8 -*-
|
||
"""
|
||
咪咕短剧超级会员 flows.cdyylkj.com/miguSM/home 自动化(DrissionPage + TgeBrowser):
|
||
1) 新建 TgeBrowser 浏览器
|
||
2) 打开页面,输入手机号,点击发送验证码 → 返回成功
|
||
3) 接收验证码并填写、提交
|
||
"""
|
||
from __future__ import annotations
|
||
|
||
import time
|
||
from typing import Any, Optional
|
||
|
||
from DrissionPage import ChromiumOptions, ChromiumPage
|
||
|
||
|
||
MIGU_HOME_URL = "https://flows.cdyylkj.com/miguSM/home"
|
||
|
||
|
||
def _find_first(page, selectors: list[str], timeout: float = 8):
|
||
"""尝试多个选择器,返回第一个找到的元素。"""
|
||
for sel in selectors:
|
||
try:
|
||
ele = page.ele(sel, timeout=timeout)
|
||
if ele:
|
||
return ele
|
||
except Exception:
|
||
continue
|
||
return None
|
||
|
||
|
||
def _click_safe(ele) -> None:
|
||
try:
|
||
ele.click()
|
||
except Exception:
|
||
try:
|
||
ele.click(by_js=True)
|
||
except Exception:
|
||
ele.run_js("this.click()")
|
||
|
||
|
||
def input_phone_and_send_code(
|
||
page: ChromiumPage,
|
||
phone: str,
|
||
url: str = MIGU_HOME_URL,
|
||
wait_after_open: float = 1.0,
|
||
) -> dict:
|
||
"""
|
||
步骤一:输入手机号、点击发送验证码。
|
||
返回 {"success": True, "message": "输入电话号码成功"} 或抛出异常。
|
||
"""
|
||
page.get(url)
|
||
time.sleep(wait_after_open)
|
||
|
||
# 手机号输入框(按常见 H5 表单结构)
|
||
phone_input = _find_first(page, [
|
||
'x://input[@placeholder*="手机"]',
|
||
'x://input[@placeholder*="电话"]',
|
||
'x://input[@placeholder*="号码"]',
|
||
'x://input[@type="tel"]',
|
||
'x://input[@type="number"]',
|
||
"css:input[type='tel']",
|
||
"css:input[placeholder*='手机']",
|
||
"css:input[placeholder*='电话']",
|
||
"css:input.inp-txt",
|
||
"css:.phone-input input",
|
||
"css:input.phone",
|
||
], timeout=10)
|
||
if not phone_input:
|
||
raise RuntimeError("未找到手机号输入框,请根据页面调整选择器")
|
||
|
||
phone_input.input(phone, clear=True)
|
||
time.sleep(0.3)
|
||
|
||
# 可选:勾选协议(若页面有)
|
||
agree = _find_first(page, [
|
||
"css:input[type='checkbox']",
|
||
"css:.agree-checkbox",
|
||
'x://input[@type="checkbox"]',
|
||
'x://i[contains(@class,"checkbox")]',
|
||
], timeout=2)
|
||
if agree:
|
||
try:
|
||
_click_safe(agree)
|
||
time.sleep(0.2)
|
||
except Exception:
|
||
pass
|
||
|
||
# 发送验证码按钮
|
||
send_btn = _find_first(page, [
|
||
'x://button[contains(text(),"获取验证码")]',
|
||
'x://span[contains(text(),"获取验证码")]',
|
||
'x://*[contains(text(),"获取验证码")]',
|
||
'x://button[contains(text(),"发送验证码")]',
|
||
'x://*[contains(text(),"发送验证码")]',
|
||
"css:button.btn-code",
|
||
"css:.send-code",
|
||
"css:.get-code",
|
||
"css:button[class*='code']",
|
||
"css:.verify-btn",
|
||
], timeout=8)
|
||
if not send_btn:
|
||
raise RuntimeError("未找到「获取验证码」按钮,请根据页面调整选择器")
|
||
|
||
_click_safe(send_btn)
|
||
time.sleep(0.5)
|
||
|
||
return {"success": True, "message": "输入电话号码成功"}
|
||
|
||
|
||
def input_code_and_submit(
|
||
page: ChromiumPage,
|
||
code: str,
|
||
) -> dict:
|
||
"""
|
||
步骤二:填写验证码并提交(若存在提交按钮)。
|
||
返回 {"success": True, "message": "验证码已填写"}。
|
||
"""
|
||
code_input = _find_first(page, [
|
||
'x://input[@placeholder*="验证码"]',
|
||
'x://input[@placeholder*="短信"]',
|
||
'x://input[@placeholder*="验证"]',
|
||
"css:input[placeholder*='验证码']",
|
||
"css:input[placeholder*='短信']",
|
||
"css:input.code-input",
|
||
"css:input.verify-input",
|
||
"css:input[type='text']",
|
||
"css:input.inp-txt",
|
||
], timeout=8)
|
||
if not code_input:
|
||
raise RuntimeError("未找到验证码输入框")
|
||
|
||
code_input.input(code, clear=True)
|
||
time.sleep(0.2)
|
||
|
||
# 若有确认/提交按钮则点击
|
||
submit_btn = _find_first(page, [
|
||
'x://button[contains(text(),"确认")]',
|
||
'x://button[contains(text(),"提交")]',
|
||
'x://*[contains(text(),"确认")]',
|
||
'x://*[contains(text(),"登录")]',
|
||
'x://*[contains(text(),"绑定")]',
|
||
"css:button.btn-primary",
|
||
"css:button.btn-buy",
|
||
"css:.submit-btn",
|
||
"css:.confirm-btn",
|
||
"css:img.btn-buy",
|
||
], timeout=5)
|
||
if submit_btn:
|
||
_click_safe(submit_btn)
|
||
|
||
return {"success": True, "message": "验证码已填写"}
|
||
|
||
|
||
def connect_dp_to_tgebrowser(port: int) -> ChromiumPage:
|
||
"""通过调试端口连接 TgeBrowser 已启动的浏览器。"""
|
||
co = ChromiumOptions().set_local_port(port=port)
|
||
return ChromiumPage(addr_or_opts=co)
|
||
|
||
|
||
def connect_dp_to_ws(ws_url: str) -> ChromiumPage:
|
||
"""通过 WebSocket 地址连接 TgeBrowser。"""
|
||
return ChromiumPage(addr_or_opts=ws_url)
|