使用 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 代表你自己的保存或分析逻辑。
已订阅但仍然拿不到正文怎么办
如果账号有访问权限,却仍然收到订阅页面,可以依次检查:
requests是否完成了网站要求的合法登录流程;- 登录后是否取得并保留了正确的会话 Cookie;
- 正文是否通过 JavaScript 动态加载;
- 接口是否要求 CSRF token 或其他正常认证字段;
- 网站是否明确禁止自动化客户端;
- 是否有面向订阅用户的专用 API。
不要把浏览器中的完整 Cookie、访问令牌或账号凭据直接写进源码。应使用环境变量或安全的凭据存储,并确保这些凭据属于你有权使用的账号。
注意事项
- 无痕模式只会隔离本地浏览数据,不代表获得了访问授权。
- 不要通过删除 Cookie、为每篇文章创建新会话、轮换代理或伪造客户端身份来处理 paywall。
robots.txt不是授权协议。即使其中允许抓取,也要遵守版权和服务条款。- HTTP
200不代表已经取得正文,paywall 页面也经常返回200。 - 付费访问通常不自动包含批量复制、再发布或建立数据集的权利,具体范围以网站许可条款为准。
备注:内容仅供参考。
