Initial commit: 项目初始代码

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-02-21 19:53:04 +08:00
commit 47ba116ba1
5 changed files with 232 additions and 0 deletions

26
.gitignore vendored Normal file
View File

@@ -0,0 +1,26 @@
# Python
__pycache__/
*.py[cod]
*$py.class
*.so
.Python
venv/
.venv/
env/
.env.local
# 敏感/本地数据(勿提交)
cookies.txt
# 调试生成的截图
click_position.png
# IDE / 编辑器
.idea/
.vscode/
*.swp
*.swo
# 系统
.DS_Store
Thumbs.db

BIN
box.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 703 B

112
cookies_loader.py Normal file
View File

@@ -0,0 +1,112 @@
# 从根目录 cookies.txt 加载并注入 cookies支持 Netscape 与 nodriver 原生格式)
from pathlib import Path
# 项目根目录(本文件所在目录的父目录即 audcf
ROOT_DIR = Path(__file__).resolve().parent
COOKIES_FILE = ROOT_DIR / "cookies.txt"
def get_cookies_path() -> Path:
return COOKIES_FILE
def cookies_file_exists() -> bool:
return COOKIES_FILE.is_file()
def is_netscape_format(path: Path) -> bool:
"""根据首行判断是否为 Netscape cookies.txt 格式。"""
if not path.is_file():
return False
try:
with open(path, "r", encoding="utf-8", errors="ignore") as f:
first = f.readline().strip()
return first.startswith("# Netscape") or first.startswith("# HTTP Cookie File")
except Exception:
return False
def parse_netscape_cookies(path: Path) -> list[dict]:
"""
解析 Netscape 格式 cookies.txt含 Cookie-Editor 等导出的多行注释)。
每行非注释domain, subdomain, path, secure, expires, name, value制表符分隔value 可含制表符故用 maxsplit=6
返回字典列表供转为 CookieParam。
"""
cookies = []
with open(path, "r", encoding="utf-8", errors="ignore") as f:
for line in f:
line = line.strip()
if not line or line.startswith("#"):
continue
parts = line.split("\t", 6)
if len(parts) < 7:
continue
domain, _subdomain, path_val, secure_val, expires_str, name, value = parts
secure = secure_val.upper() == "TRUE"
try:
expires = float(expires_str) if expires_str and expires_str != "0" else None
except ValueError:
expires = None
cookies.append({
"name": name,
"value": value,
"domain": domain,
"path": path_val or "/",
"expires": expires,
"http_only": False,
"secure": secure,
"same_site": "Lax",
})
return cookies
async def load_cookies_nodriver_native(browser, path: Path) -> bool:
"""
使用 nodriver 原生格式加载 cookiesbrowser.cookies.load
成功返回 True失败返回 False。
"""
try:
await browser.cookies.load(file=str(path))
return True
except Exception:
return False
def _dict_to_cookie_param(d: dict):
"""将解析后的 cookie 字典转为 nodriver CDP 所需的 CookieParam。"""
from nodriver.cdp import network
expires = d.get("expires")
if expires is not None:
expires = network.TimeSinceEpoch(expires)
same_site = d.get("same_site")
if same_site:
same_site = network.CookieSameSite(same_site)
return network.CookieParam(
name=d["name"],
value=d["value"],
domain=d.get("domain"),
path=d.get("path") or "/",
secure=d.get("secure"),
http_only=d.get("http_only"),
same_site=same_site,
expires=expires,
)
async def inject_netscape_cookies(tab, path: Path) -> bool:
"""
解析 Netscape 格式的 cookies.txt 并通过 CDP 注入到当前 tab。
应在已打开目标域页面后调用。
成功返回 True失败返回 False。
"""
try:
from nodriver import cdp
cookies_list = parse_netscape_cookies(path)
if not cookies_list:
return True
cookies_param = [_dict_to_cookie_param(d) for d in cookies_list]
await tab.send(cdp.storage.set_cookies(cookies=cookies_param))
return True
except Exception:
return False

93
main.py Normal file
View File

@@ -0,0 +1,93 @@
"""
使用 Nodriver 访问 Cloudflare 保护的 audiences.me/torrents.php
启动时从根目录 cookies.txt 加载 Cookie采用等待自动通过 + 选择器点击 Turnstile 过验证。
"""
import nodriver as uc
from cookies_loader import (
get_cookies_path,
cookies_file_exists,
is_netscape_format,
load_cookies_nodriver_native,
inject_netscape_cookies,
)
TARGET_URL = "https://dash.cloudflare.com/sign-up"
async def try_click_cf_turnstile(page):
"""
通过截图+模板匹配定位 CF Turnstile 复选框并点击(与语言无关)。
复选框在跨域 iframe + Shadow DOM 内,用选择器点不到,需用坐标点击。
依赖pip install opencv-python
"""
try:
# 用自定义模板 box.png 在截图中匹配复选框区域,返回中心坐标(截图是设备像素)
pos = await page.template_location(template_image="box.png")
if pos is None:
return
x, y = int(pos[0]), int(pos[1])
# 保存带点击位置标记的截图(便于调试)
try:
await page.save_screenshot("click_position.png")
import cv2
im = cv2.imread("click_position.png")
if im is not None:
r = max(15, min(im.shape[:2]) // 30)
cv2.circle(im, (x, y), r, (0, 0, 255), 3)
cv2.line(im, (x - r - 5, y), (x + r + 5, y), (0, 0, 255), 2)
cv2.line(im, (x, y - r - 5), (x, y + r + 5), (0, 0, 255), 2)
cv2.imwrite("click_position.png", im)
except Exception:
pass
# 截图坐标是设备像素CDP 鼠标事件要 CSS 像素,需除以 devicePixelRatio
try:
raw = await page.evaluate("window.devicePixelRatio")
dpr = float(getattr(raw, "value", raw) if hasattr(raw, "value") else raw) if raw is not None else 1.0
except Exception:
dpr = 1.0
if dpr <= 0:
dpr = 1.0
x_css, y_css = x / dpr, y / dpr
# 先模拟人类移动再点击,提高通过率
await page.mouse_move(x_css, y_css, steps=10)
await page.sleep(0.3)
await page.mouse_click(x_css, y_css)
await page.sleep(3)
except Exception:
pass
async def main():
# 建议非无头模式以提高 CF 通过率
browser = await uc.start(headless=False)
path = get_cookies_path()
if cookies_file_exists():
if is_netscape_format(path):
# Netscape 格式:先打开目标页再注入 cookies再重新打开使 cookies 生效(避免 reload 导致 Not attached
page = await browser.get(TARGET_URL)
await inject_netscape_cookies(page, path)
page = await browser.get(TARGET_URL)
else:
# 先尝试 nodriver 原生格式加载,再打开目标页
await load_cookies_nodriver_native(browser, path)
page = await browser.get(TARGET_URL)
else:
page = await browser.get(TARGET_URL)
# 等待 CF 自动通过(约 3~8 秒)
await page.sleep(15)
# 若仍为挑战页,尝试点击 Turnstile
await try_click_cf_turnstile(page)
await page.sleep(3)
# 后续可在此对 page 做操作,或保存截图等
# await page.save_screenshot("bypass_result.png")
# content = await page.get_content()
return page
if __name__ == "__main__":
uc.loop().run_until_complete(main())

1
requirements.txt Normal file
View File

@@ -0,0 +1 @@
nodriver