learn-spider/spider/mail_qq_bak.py

175 lines
7.2 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import os
import argparse
from playwright.sync_api import sync_playwright
def save_login_state(auth_file):
with sync_playwright() as p:
os.makedirs(os.path.dirname(auth_file), exist_ok=True)
# 1. 启动浏览器
# headless=False 表示显示浏览器窗口,方便用户扫码登录
browser = p.chromium.launch(headless=False)
# 2. 创建浏览器上下文Context
# Context 类似于一个独立的浏览器会话,包含 cookies、localStorage 等
context = browser.new_context()
# 3. 创建新页面Tab页
page = context.new_page()
# ---------- 核心:设置网络请求监听 ----------
# 标记是否已登录成功(用于跨函数修改)
login_success = False
def on_response(response):
"""
监听所有网络响应的回调函数
每当浏览器收到任何 HTTP 响应时,都会调用这个函数
Args:
response: Playwright 的 Response 对象,包含响应的所有信息
"""
# 使用 nonlocal 声明,允许修改外层函数的 login_success 变量
nonlocal login_success
# 检查响应URL是否包含登录相关的关键词
# response.url 是请求的完整地址,如 "https://mail.qq.com/cgi-bin/login"
if 'login' in response.url or 'auth' in response.url:
# response.status 是 HTTP 状态码200 表示成功
# 其他常见状态码301(重定向)、302(临时重定向)、400(错误请求)、401(未授权)、403(禁止)、404(未找到)、500(服务器错误)
if response.status == 200:
print(f"[网络监听] 检测到登录API响应成功")
print(f" - 请求地址: {response.url}")
print(f" - 状态码: {response.status}")
login_success = True
# 将回调函数注册到 page 对象
# 每当有网络响应时Playwright 会自动调用 on_response 函数
page.on('response', on_response)
# 其他可用的事件:'request'(请求发送时)、'requestfailed'(请求失败时)、'requestfinished'(请求完成时)
# ---------- 可选:监听 URL 变化 ----------
def on_url_change(frame):
"""
监听页面导航URL变化的回调函数
每当页面跳转或刷新时都会调用
Args:
frame: 页面框架对象
"""
nonlocal login_success
# 获取当前页面的完整URL
current_url = page.url
# 检查URL是否已进入邮箱系统
# 登录前是 'https://mail.qq.com'登录后通常会变成包含其他路径的URL
# 例如:'https://mail.qq.com/cgi-bin/frame_html?sid=xxx'
if 'mail' in current_url and current_url != 'https://mail.qq.com':
print(f"[URL监听] 检测到页面已跳转到邮箱系统")
print(f" - 当前URL: {current_url}")
login_success = True
# 注册 URL 变化监听器
# 'framenavigated' 事件在页面导航(跳转)时触发
page.on('framenavigated', on_url_change)
# 4. 访问QQ邮箱首页
print("正在打开QQ邮箱登录页面...")
page.goto('https://mail.qq.com')
# goto() 会等待页面加载完成(直到网络空闲)
# 5. 提示用户扫码登录
print("=" * 50)
print("请使用手机QQ扫码登录")
print("系统将自动检测登录状态,无需手动操作")
print("等待登录中...")
print("=" * 50)
# 6. 等待登录成功或超时
try:
# wait_for_function 会持续检查 JavaScript 表达式是否为真
# 这里检查两个条件:
# 1. 页面是否出现"收件箱"文字(登录成功的典型标志)
# 2. 页面是否出现"inbox"文字(英文版)
# timeout=300000 表示最多等待 300 秒5分钟
# 如果5分钟内条件满足则继续执行否则抛出异常
page.wait_for_function(
'document.body.innerText.includes("收件箱") || document.body.innerText.includes("inbox")',
timeout=300000 # 毫秒单位300000ms = 300秒 = 5分钟
)
# 如果 wait_for_function 正常返回,说明检测到了"收件箱"文字
print("[文本监听] 检测到'收件箱'文字,登录确认成功!")
except Exception as e:
# 超时或其他异常会执行这里
print(f"[警告] 未检测到'收件箱'文字,可能页面结构有变化或登录超时")
print(f" 错误信息: {e}")
# 7. 检查是否通过其他方式检测到登录成功
if login_success:
print("\n✅ 已通过多种方式确认登录成功!")
else:
print("\n⚠️ 未明确检测到登录成功标志,但仍将保存当前状态")
print(" 如果登录成功,状态应该是有效的")
# 8. 额外等待2秒确保状态稳定
# 目的等待所有异步请求完成、cookies 完全写入、页面状态稳定
print("等待2秒确保状态稳定...")
page.wait_for_timeout(2000) # 2000毫秒 = 2秒
# 9. 保存登录状态cookies、localStorage、sessionStorage 等)
# storage_state 会保存当前 context 的所有存储数据
# 保存后,下次可以直接用 storage_state 参数加载,无需重新登录
context.storage_state(path=auth_file)
print(f"✅ 登录状态已保存到: {auth_file}")
# 10. 关闭浏览器,释放资源
browser.close()
print("浏览器已关闭")
def crawl_with_saved_state(auth_file):
with sync_playwright() as p:
# 加载之前保存的登录状态
browser = p.chromium.launch(headless=False) # 可以无头模式了
context = browser.new_context(storage_state=auth_file)
page = context.new_page()
# 直接访问需要登录的页面
page.goto('https://mail.qq.com')
# 现在已经是登录状态了
print("当前URL:", page.url)
# 获取页面内容
content = page.content()
print("页面长度:", len(content))
# 可以使用 BeautifulSoup 解析
# from bs4 import BeautifulSoup
# soup = BeautifulSoup(content, 'html.parser')
page.wait_for_timeout(5000)
browser.close()
if __name__ == "__main__":
parser = argparse.ArgumentParser(description='爬虫脚本')
parser.add_argument('account', help='用户')
# parser.add_argument('-p', '--password', help='密码(可选)')
args = parser.parse_args()
account = args.account
print(f"用户名{account}")
if not account:
print("请输入用户名")
exit(1)
auth_file_path = "./auth/mail"
file_path = f"{auth_file_path}/{account}.json"
# 判断文件是否存在
if os.path.exists(file_path):
crawl_with_saved_state(file_path)
else:
save_login_state(file_path)