PAYATHON 2026

使用 Python requests 抓取时如何避免触发 paywall?

技术老齐

结论

不要通过反复清除 Cookie、伪造新会话、轮换 IP 或模拟无痕窗口来避开按访问次数触发的 paywall。这些操作属于绕过网站访问控制,可能违反服务条款和版权许可,也可能触犯当地法律。

正确的做法是使用订阅账号或网站提供的官方 API。如果只需获取少量公开内容,应保持正常会话、控制抓取频率,并在检测到订阅提示后立即停止。

为什么只有第一个页面有正文

这类 paywall 通常会根据以下信息统计免费阅读次数:

  • Cookie 或会话标识;
  • 登录账号;
  • IP 地址和请求频率;
  • 浏览器指纹或 JavaScript 写入的本地状态;
  • 服务端保存的访问记录。

requests.Session() 会在多个请求之间保留 Cookie,因此网站可能把后续请求识别为同一次访问会话。无痕窗口有时能多读一篇,通常是因为它创建了新的临时浏览器存储,并不意味着网站允许程序不断重置访问状态。

还有一种可能:服务器返回的 HTML 只有页面框架,正文需要 JavaScript 调用接口后才能加载。即使响应状态是 200,Beautiful Soup 也无法从初始 HTML 中提取正文。可以通过浏览器开发者工具确认这种情况,但调用相关接口时仍要遵守授权和订阅要求。

推荐的处理步骤

1. 确认网站提供的合法访问方式

先检查:

  • 网站是否提供订阅、机构访问或试用;
  • 是否有官方 API、RSS 或数据导出功能;
  • 是否允许搜索引擎、研究机构或合作方获取内容;
  • robots.txt 和服务条款是否允许自动抓取;
  • 能否获得网站运营方的书面许可。

如果需要批量下载,使用官方 API 或取得数据授权,通常比解析网页更稳定。

2. 使用正常会话访问,不要规避计数

如果你有有效订阅,而且网站允许自动化访问,可以使用 requests.Session 保持已授权的会话。登录时应采用网站公开支持的认证方式,不要复制他人账户的 Cookie,也不要绕过验证码或其他访问限制。

import requests
from bs4 import BeautifulSoup

session = requests.Session()
session.headers.update({
    "User-Agent": "ExampleResearchClient/1.0 (contact: you@example.com)"
})

def fetch_article(url: str) -> str | None:
    response = session.get(url, timeout=20)

    if response.status_code in {401, 402, 403, 429}:
        print(f"Access unavailable: HTTP {response.status_code}")
        return None

    response.raise_for_status()

    soup = BeautifulSoup(response.text, "html.parser")

    paywall_markers = (
        "buy a subscription",
        "subscribe to continue",
        "subscription required",
    )
    page_text = soup.get_text(" ", strip=True).lower()

    if any(marker in page_text for marker in paywall_markers):
        print("Subscription wall detected; stopping retrieval.")
        return None

    article = soup.select_one("article")
    if article is None:
        print("Article body was not found.")
        return None

    return article.get_text("\n", strip=True)

选择器 article 和订阅提示文本仅用于演示,实际使用时要根据目标网站返回的 HTML 调整。网站文案可能变化,因此不要只靠提示语判断是否出现 paywall。

3. 遇到 paywall 后停止批量任务

批量处理时,如果找不到正文,应停止任务或转为人工检查,不要自动创建新身份并继续请求。

urls = [
    "https://example.com/article/1",
    "https://example.com/article/2",
]

for url in urls:
    content = fetch_article(url)

    if content is None:
        break

    print(content[:500])

同时应设置合理的请求间隔,并正确处理 429 Too Many Requests:

import time

for url in urls:
    content = fetch_article(url)

    if content is None:
        break

    process(content)
    time.sleep(2)

这里的 process 代表你自己的保存或分析逻辑。

已订阅但仍然拿不到正文怎么办

如果账号有访问权限,却仍然收到订阅页面,可以依次检查:

  1. requests 是否完成了网站要求的合法登录流程;
  2. 登录后是否取得并保留了正确的会话 Cookie;
  3. 正文是否通过 JavaScript 动态加载;
  4. 接口是否要求 CSRF token 或其他正常认证字段;
  5. 网站是否明确禁止自动化客户端;
  6. 是否有面向订阅用户的专用 API。

不要把浏览器中的完整 Cookie、访问令牌或账号凭据直接写进源码。应使用环境变量或安全的凭据存储,并确保这些凭据属于你有权使用的账号。

注意事项

  • 无痕模式只会隔离本地浏览数据,不代表获得了访问授权。
  • 不要通过删除 Cookie、为每篇文章创建新会话、轮换代理或伪造客户端身份来处理 paywall。
  • robots.txt 不是授权协议。即使其中允许抓取,也要遵守版权和服务条款。
  • HTTP 200 不代表已经取得正文,paywall 页面也经常返回 200。
  • 付费访问通常不自动包含批量复制、再发布或建立数据集的权利,具体范围以网站许可条款为准。

备注:内容仅供参考。