Cookie 模拟登录 3 大误区:从 17k.com 案例看 requests 与 Selenium 选择
📅 2026/7/11 23:21:53
👁️ 阅读次数
📝 编程学习
Cookie 模拟登录 3 大误区:从 17k.com 案例看 requests 与 Selenium 选择
在数据采集和自动化测试领域,模拟登录是绕不开的技术门槛。许多开发者在使用 Python 进行网页自动化时,往往会在 Cookie 处理环节踩坑。本文将以 17k.com 小说网站为例,深度剖析三种主流技术方案的优劣,并提供可落地的解决方案。
1. 模拟登录的核心机制解析
HTTP 协议本身是无状态的,网站通过 Cookie 维持用户会话。当用户首次登录时,服务器会在响应头中设置 Set-Cookie 字段,浏览器将这些信息保存并在后续请求中自动携带。模拟登录的本质就是复现这个过程。
关键验证点:
- 登录请求是否触发重定向(302 状态码)
- 响应头是否包含 Set-Cookie 字段
- 后续请求是否携带正确的 Cookie 头
以 17k.com 为例,其登录流程存在以下特点:
- 采用标准的表单提交(POST /ck/user/login)
- 登录成功后返回 JSON 格式响应
- 需要处理多个关联域名(passport.17k.com 和 user.17k.com)的 Cookie
2. 技术方案对比与选型
2.1 三种实现方式对比
| 特性 | 直接 requests | requests.Session | Selenium |
|---|---|---|---|
| 执行速度 | ⚡️⚡️⚡️⚡️⚡️ | ⚡️⚡️⚡️⚡️ | ⚡️ |
| 内存占用 | 低 | 中 | 高 |
| 反爬绕过能力 | ❌ | ✅ | ✅✅ |
| JS 渲染支持 | ❌ | ❌ | ✅✅ |
| 验证码处理 | 困难 | 困难 | 可行 |
| 适用场景 | 简单接口 | 标准网站 | 复杂SPA应用 |
2.2 决策流程图
graph TD A[目标网站特性] --> B{依赖JS渲染?} B -->|是| C[Selenium方案] B -->|否| D{有验证码?} D -->|是| C D -->|否| E[requests.Session] E --> F[简单接口?] F -->|是| G[直接requests] F -->|否| E注意:当遇到动态参数(如 _xsrf)或加密参数时,优先考虑 Selenium 方案
3. 实战代码实现
3.1 requests.Session 方案
import requests from urllib.parse import urljoin BASE_URL = 'https://passport.17k.com/' LOGIN_URL = urljoin(BASE_URL, '/ck/user/login') BOOKSHELF_URL = 'https://user.17k.com/ck/author/shelf' def login_with_session(username, password): session = requests.Session() session.headers.update({ 'Origin': 'https://passport.17k.com', 'Referer': 'https://passport.17k.com/login/' }) payload = { 'loginName': username, 'password': password, 'remember': 'on' } resp = session.post(LOGIN_URL, data=payload) if resp.json().get('status') != 1: raise ValueError('登录失败') # 获取书架数据 shelf_resp = session.get(BOOKSHELF_URL) return shelf_resp.json()关键点:
- 必须设置正确的 Origin 和 Referer
- 保持同一个 Session 对象发起连续请求
- 处理跨域 Cookie(domain=.17k.com)
3.2 Selenium 混合方案
from selenium import webdriver from selenium.webdriver.common.by import By from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC import requests def hybrid_login(username, password): # 第一阶段:Selenium 获取Cookie driver = webdriver.Chrome() driver.get('https://passport.17k.com/login/') WebDriverWait(driver, 10).until( EC.presence_of_element_located((By.NAME, 'loginName')) ).send_keys(username) driver.find_element(By.NAME, 'password').send_keys(password) driver.find_element(By.CSS_SELECTOR, 'button[type="submit"]').click() # 等待登录完成 WebDriverWait(driver, 10).until( EC.url_contains('user.17k.com') ) # 提取Cookie cookies = {c['name']: c['value'] for c in driver.get_cookies()} driver.quit() # 第二阶段:requests 持续使用 session = requests.Session() for name, value in cookies.items(): session.cookies.set(name, value) shelf_resp = session.get(BOOKSHELF_URL) return shelf_resp.json()优势:
- 可处理图形验证码(需人工干预)
- 自动执行页面 JavaScript
- 获取完整浏览器环境 Cookie
4. 常见误区与解决方案
误区一:Cookie 失效问题
现象:
- 登录成功但后续请求返回 403
- Cookie 存活时间极短
解决方案:
# 添加心跳保活机制 def keep_alive(session): ping_url = 'https://user.17k.com/ck/user/check_login' session.get(ping_url, timeout=3) # 在关键请求前调用 keep_alive(session)误区二:Header 校验不足
必要请求头:
headers = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)', 'X-Requested-With': 'XMLHttpRequest', 'Sec-Fetch-Dest': 'empty', 'Sec-Fetch-Mode': 'cors' }误区三:SSL 验证问题
解决方案:
# 禁用SSL验证(不推荐生产环境使用) session = requests.Session() session.verify = False # 或指定证书路径 session.verify = '/path/to/cert.pem'5. 高级技巧与优化
5.1 Cookie 持久化方案
import pickle import os def save_cookies(session, filename): with open(filename, 'wb') as f: pickle.dump(session.cookies, f) def load_cookies(filename): if os.path.exists(filename): with open(filename, 'rb') as f: return pickle.load(f) return None # 使用示例 session = requests.Session() if cookies := load_cookies('17k.cookies'): session.cookies.update(cookies) else: login_with_session(username, password) save_cookies(session, '17k.cookies')5.2 请求重试机制
from tenacity import retry, stop_after_attempt, wait_exponential @retry(stop=stop_after_attempt(3), wait=wait_exponential(multiplier=1, min=2, max=10)) def safe_request(url, session): resp = session.get(url) resp.raise_for_status() if '请登录' in resp.text: raise ValueError('Session expired') return resp5.3 性能优化对比
测试数据(100次请求):
- 直接 requests:平均 1.2s
- Session:平均 0.8s
- Selenium:平均 32.7s
内存占用:
- requests.Session:约 15MB
- Selenium:约 280MB
在实际项目中,建议根据目标网站的防护等级选择合适方案。对于 17k.com 这类中等防护网站,requests.Session 配合适当的请求头设置即可满足需求,而面对更复杂的反爬机制时,Selenium 仍是可靠选择。
编程学习
技术分享
实战经验