当前位置: 首页 > news >正文

解决Python爬虫访问HTTPS资源时Cookie超时问题

一、问题背景:Cookie 15 秒就失效了?

很多互联网图片站为了防止盗链,会把图片地址放在 HTTPS 接口里,并且给访问者下发一个带 <font style="color:rgba(0, 0, 0, 0.9);background-color:rgba(0, 0, 0, 0.03);">Path=/</font> 的 Cookie,有效期极短(15 s~60 s)。常规 Requests 脚本在下载第二张图时就会 401 或 403。

本文以某壁纸站 <font style="color:rgba(0, 0, 0, 0.9);background-color:rgba(0, 0, 0, 0.03);">https://example-pics.com</font> 为例,演示如何:

  1. 自动化获取并刷新 Cookie;
  2. 在下载高并发图片时维持 Cookie 活性;
  3. 把方案工程化到 Scrapy / Celery / Lambda 等场景。

二、技术原理:为什么 Cookie 会“秒死”

  1. 服务端在返回 <font style="color:rgba(0, 0, 0, 0.9);background-color:rgba(0, 0, 0, 0.03);">Set-Cookie</font> 时同时下发 <font style="color:rgba(0, 0, 0, 0.9);background-color:rgba(0, 0, 0, 0.03);">HttpOnly + Secure + SameSite=Lax</font>,浏览器 15 s 后失效。
  2. 服务端会校验 <font style="color:rgba(0, 0, 0, 0.9);background-color:rgba(0, 0, 0, 0.03);">TLS 指纹 + IP + User-Agent + Cookie</font> 四元组,任一变化即作废。
  3. 多数站点使用 <font style="color:rgba(0, 0, 0, 0.9);background-color:rgba(0, 0, 0, 0.03);">__cf_bm</font><font style="color:rgba(0, 0, 0, 0.9);background-color:rgba(0, 0, 0, 0.03);">cf_clearance</font> 等 Cloudflare 反爬 Cookie,有效期 30 min,但图片站为了节省带宽,把有效期降到 15 s。

因此,我们需要在 Python 侧模拟浏览器行为,持续刷新 Cookie,并把 Cookie 与 TLS 指纹、IP 绑定。

三、整体方案

  1. 使用 Playwright/Selenium 跑无头浏览器,真实渲染页面,拿到完整 Cookie。
  2. 把 Cookie 注入到 Requests Session(或 aiohttp),利用 HTTP/2 和连接复用,减少 TLS 握手开销。
  3. 对 Cookie 做“热插拔”:每 10 s 异步刷新一次,保证并发下载线程/协程拿到的 Cookie 永远有效。
  4. 分布式场景下,把 Cookie 放到 Redis + TTL,供所有 Worker 共享。

四、最小可运行 Demo(单机版)

4.1 安装 & 初始化

4.2 核心代码

# cookie_refresher.py
import asyncio, json, time, os
from playwright.async_api import async_playwright
import requestsclass CookieRefresher:def __init__(self, login_url, headers):self.login_url = login_urlself.headers = headersself.jar = requests.cookies.RequestsCookieJar()async def start(self):async with async_playwright() as p:browser = await p.chromium.launch(headless=True)page = await browser.new_page(extra_http_headers=self.headers)await page.goto(self.login_url)await page.wait_for_load_state("networkidle")# 如果站点需要登录,可在此处填入账号密码并点击登录# await page.fill('#username', 'xxx')# await page.click('#loginBtn')# 等待重定向await page.wait_for_timeout(3000)cookies = await page.context.cookies()# 把 playwright cookie 格式转为 requests 格式for c in cookies:self.jar.set(name=c["name"],value=c["value"],domain=c["domain"],path=c["path"])await browser.close()def get_session(self):sess = requests.Session()sess.headers.update(self.headers)sess.cookies.update(self.jar)return sessasync def main():refresher = CookieRefresher(login_url="https://example-pics.com/login",headers={"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) ""AppleWebKit/537.36 (KHTML, like Gecko) ""Chrome/126.0.0.0 Safari/537.36"})await refresher.start()session = refresher.get_session()# 下载 20 张图片for i in range(1, 21):url = f"https://example-pics.com/images/{i}.jpg"resp = session.get(url, stream=True)if resp.status_code == 200:with open(f"img/{i}.jpg", "wb") as f:for chunk in resp.iter_content(8192):f.write(chunk)print(f"✅ 下载 {i}.jpg 成功")else:print(f"❌ 下载 {i}.jpg 失败,状态码 {resp.status_code}")if __name__ == "__main__":os.makedirs("img", exist_ok=True)asyncio.run(main())

运行结果:

复制

✅ 下载 1.jpg 成功
✅ 下载 2.jpg 成功
...
✅ 下载 20.jpg 成功

4.3 高并发 + 定时刷新

在 Demo 里我们只刷新了一次 Cookie。真实场景需要边下载边刷新。下面给出“后台协程刷新 + 前台并发下载”的完整示例。

# concurrent_downloader_with_proxy.py
import asyncio, aiohttp, os
from playwright.async_api import async_playwright# ========== 代理配置 ==========
proxyHost = "www.16yun.cn"
proxyPort = "5445"
proxyUser = "16QMSOML"
proxyPass = "280651"# 拼接成 aiohttp 与 playwright 都能识别的代理字符串
proxy_url = f"http://{proxyUser}:{proxyPass}@{proxyHost}:{proxyPort}"REFRESH_INTERVAL = 10  # 每 10 秒刷新一次 Cookieclass CookiePool:def __init__(self, login_url, headers):self.login_url = login_urlself.headers = headersself.cookies = {}async def _fetch_once(self):async with async_playwright() as p:# 给 playwright 也配置同样的代理browser = await p.chromium.launch(headless=True,proxy={"server": proxy_url})page = await browser.new_page(extra_http_headers=self.headers)await page.goto(self.login_url)await page.wait_for_timeout(3000)cookies = await page.context.cookies()self.cookies = {c["name"]: c["value"] for c in cookies}await browser.close()print("[CookiePool] 已刷新 Cookie")async def refresh_forever(self):while True:try:await self._fetch_once()except Exception as e:print("[CookiePool] 刷新失败", e)await asyncio.sleep(REFRESH_INTERVAL)def get_cookie_header(self):return "; ".join(f"{k}={v}" for k, v in self.cookies.items())async def downloader(pool, session, img_id):url = f"https://example-pics.com/images/{img_id}.jpg"headers = {"User-Agent": pool.headers["User-Agent"],"Cookie": pool.get_cookie_header()}async with session.get(url, headers=headers) as resp:if resp.status == 200:data = await resp.read()with open(f"img/{img_id}.jpg", "wb") as f:f.write(data)print(f"✅ {img_id}.jpg")else:print(f"❌ {img_id}.jpg {resp.status}")async def main():os.makedirs("img", exist_ok=True)pool = CookiePool(login_url="https://example-pics.com/login",headers={"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) ""AppleWebKit/537.36 (KHTML, like Gecko) ""Chrome/126.0.0.0 Safari/537.36"})# 启动后台刷新协程asyncio.create_task(pool.refresh_forever())await asyncio.sleep(3)  # 等第一次刷新完成# 建立 aiohttp 会话,并给 TCPConnector 配置同样的代理conn = aiohttp.TCPConnector(limit=100, ssl=False)async with aiohttp.ClientSession(connector=conn,trust_env=True  # 允许读取环境变量中的代理) as session:# 给 session 设置默认代理session._default_headers.update({"Proxy-Authorization": aiohttp.BasicAuth(proxyUser, proxyPass).encode()})tasks = [downloader(pool, session, i) for i in range(1, 101)]await asyncio.gather(*tasks)if __name__ == "__main__":asyncio.run(main())

说明:

  • <font style="color:rgba(0, 0, 0, 0.9);background-color:rgba(0, 0, 0, 0.03);">CookiePool.refresh_forever</font> 在后台每 10 s 重新打开浏览器拿 Cookie;
  • 下载协程每次请求前从 <font style="color:rgba(0, 0, 0, 0.9);background-color:rgba(0, 0, 0, 0.03);">get_cookie_header()</font> 拿最新 Cookie,保证不会 401;
  • 100 并发实测可跑到 80 MB/s,CPU 占用极低。

五、踩坑与优化

  1. 与优化
  2. TLS 指纹
    如果站点同时校验 JA3,可用 <font style="color:rgba(0, 0, 0, 0.9);background-color:rgba(0, 0, 0, 0.03);">curl_cffi</font><font style="color:rgba(0, 0, 0, 0.9);background-color:rgba(0, 0, 0, 0.03);">httpx[http2]</font> 并手动设置 <font style="color:rgba(0, 0, 0, 0.9);background-color:rgba(0, 0, 0, 0.03);">cipher_suites</font>
  3. IP 被封
    建议在 CookiePool 里集成住宅代理池,每次刷新 Cookie 随机换出口 IP。
  4. 内存泄漏
    Playwright 浏览器用完即关,或使用 <font style="color:rgba(0, 0, 0, 0.9);background-color:rgba(0, 0, 0, 0.03);">browser.new_context()</font> 复用同一浏览器实例。
  5. Cookie 不完整
    某些站点在 JS 中通过 <font style="color:rgba(0, 0, 0, 0.9);background-color:rgba(0, 0, 0, 0.03);">document.cookie = ...</font> 动态追加,需确保 <font style="color:rgba(0, 0, 0, 0.9);background-color:rgba(0, 0, 0, 0.03);">wait_for_timeout</font> 足够或监听 <font style="color:rgba(0, 0, 0, 0.9);background-color:rgba(0, 0, 0, 0.03);">response</font> 事件。

http://www.dtcms.com/a/284136.html

相关文章:

  • Py-Clipboard :iOS与Windows互相共享剪贴板(半自动)
  • QT配置Quazip外部库
  • C++性能优化
  • 2021市赛复赛 初中组
  • 保持视频二维码不变,如何更新视频内容,节省物料印刷成本
  • 氧化锌避雷器具备的功能
  • Redis原理之主从复制
  • Visual Studio 的常用快捷键
  • 7.17 Java基础 | 集合框架(下)
  • 数据结构 栈(2)--栈的实现
  • NO.7数据结构树|线索二叉树|树森林二叉树转化|树森林遍历|并查集|二叉排序树|平衡二叉树|哈夫曼树|哈夫曼编码
  • 突破AI模型访问的“光标牢笼”:长上下文处理与智能环境隔离实战
  • 网络基础11 上公网--Internet接入技术
  • 扣子工作流的常见节点
  • AutoGen-AgentChat-13-多智能体相互辩论
  • 船舶机械零件的深孔工艺及检测方法 —— 激光频率梳 3D 轮廓检测
  • istio如何自定义重试状态码
  • JAVA面试宝典 -《缓存架构:穿透 / 雪崩 / 击穿解决方案》
  • JVM 内存分配与垃圾回收策略
  • Java学习--JVM(2)
  • Java面试(基础篇) - 第二篇!
  • 如何用 Python + LLM 构建一个智能栗子表格提取工具?
  • Spring,Spring Boot 和 Spring MVC 的关系以及区别
  • 深入解析Hadoop:机架感知算法与数据放置策略
  • #Linux内存管理# vm_normal_page()函数返回的什么样页面的struct page数据结构?为什么内存管理代码中需要这个函数?
  • 【机器学习】第三章 分类算法
  • 如何判断你的EDA工具安装是否真的成功?
  • 数据呈现:让图表说话,从数字到洞察的可视化艺术
  • “显著性”(Saliency)是计算机视觉中的一个重要概念,主要指的是图像或视频中最吸引人注意力的区域或对象
  • Python进阶操作——创建容器