使用 Python 爬取论坛而不触发反爬虫措施
抱歉,我需要您提供要翻译的完整文本内容(除代码块和 URL 之外),才能为您进行翻译。请将文章的正文粘贴在这里,我会按照您的要求保留源链接、格式和技术术语,并将内容翻译成简体中文。
Scraping Forums Without Getting Flagged
我花了多年时间在论坛的缝隙中爬行——那些已经被遗忘、但只要仔细聆听仍在低声嗡鸣的老论坛,稍有好奇就会弹出验证码的前沿板块,只有在归档中才能复活的死社区,PHPBB 的伤痕,vBulletin 的幽灵,以及 Cloudflare 紧盯着你的每一步。它们都有一个共同点:想要知道有人在窥探,即使只是为了阅读。
核心思路
像人类一样抓取。
枯燥、重复、稍有分心。是人类,但那种没人注意到的类型。
1. 手动探索优先
- 在普通浏览器中打开论坛。
- 随意点击、滚动、翻页、查看用户资料。
- 打开 DevTools → Network 并重新加载一个主题帖。
- 观察:
- 哪些请求会被触发,哪些不会?
- 请求头中是否有轮换的 token?
- Cookie 是否只在第一页出现?
- 是否有隐藏的 POST 请求?
- 记录 每一条观察——不写代码,不做推理。反机器人系统是模式匹配器;你的任务是避开它们预期的模式。
2. 使用持久会话
import requests
session = requests.Session()
session.headers.update({
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64)",
"Accept-Language": "en-US,en;q=0.9",
"Accept": "text/html,application/xhtml+xml",
"Connection": "keep-alive"
})
# 第一次请求 = 握手(设置 cookie 等)
session.get("https://exampleforum.com/")
选定一个 User‑Agent 并 永远不要 在运行中更换。人类不会每隔几分钟就换浏览器。
3. 类人暂停
import time
import random
def human_pause(base: float = 3) -> None:
"""Sleep for a random interval that feels human."""
time.sleep(base + random.uniform(0.5, 2.5))
- 在每一次有意义的请求之间调用
human_pause()。 - 如果你要抓取上百个主题,预计任务会花 数小时 而不是几分钟。
4. 随机化 URL 顺序
import random
thread_urls = list(collected_threads) # pre‑collected set of URLs
random.shuffle(thread_urls) # jump around, don’t go sequentially
人类会从主题 7 → 主题 2 → 一个用户资料 → 再回到首页。模仿这种行为。
5. 解析前的延迟
response = session.get(url)
human_pause() # pause **before** you touch the DOM
soup = BeautifulSoup(response.text, "html.parser")
瞬间解析是明显的信号。稍作停顿会让抓取器看起来更“思考”。
6. (可选)Selenium / Playwright
- 仅在论坛大量依赖 JavaScript 时使用。
- 关闭无头模式,设置真实的窗口尺寸,并在操作之间加入
human_pause()。 - 大多数传统论坛都是纯 HTML →
requests+BeautifulSoup已足够。
7. 尊重 robots.txt
虽然它不是法律,但它告诉你站点期望哪些页面被慢速爬取。
- 宽松 → 当作“慢速、枯燥的用户”。
- 严格 → 假设监控更严,格外小心。
8. 检测软封锁
常见迹象:
- 空响应
- 登录重定向
- 隐藏的 captcha HTML
- HTTP 200 但正文异常短
if "captcha" in response.text.lower():
raise RuntimeError("Soft blocked")
遇到封锁时:
- 暂停(几分钟到几小时)。
- 不要 激进地轮换 IP 或 User‑Agent。
- 稍后以更慢的速度恢复。
9. 避免高负载接口
- 搜索 接口监控严格,视为“仅限人类”。
- 只使用分类页、索引页和最近主题列表。
10. 将状态持久化到数据库
import sqlite3
conn = sqlite3.connect("forum.db")
c = conn.cursor()
c.execute('''
CREATE TABLE IF NOT EXISTS posts (
thread_id TEXT,
post_id TEXT,
content TEXT,
timestamp TEXT
)
''')
conn.commit()
- 将每条帖子保存到数据库,以便后续恢复或增量抓取。
Source: …
11. 变更你的抓取时间表
- 不要像 cron 任务一样每晚 02:00 AM 固定抓取。
- 随机跳过某些天,改变开始时间,插入额外的暂停。
- 人类的浏览模式本身就是不规律的。
12. 精简请求负载
- 忽略头像、签名、徽章,除非你真的需要它们。
- 避免下载图片——图片会增加带宽消耗并提升被察觉的风险。
- 通常只有主题的前几页包含有价值的讨论内容。
13. 需要时的登录抓取
- 只使用 一个 已登录会话;在已登录状态下不要轮换 IP 或 User‑Agent。
- 尊重账户的速率限制——速度要 比匿名访客慢得多。
14. 最小化主题抓取示例
from bs4 import BeautifulSoup
def scrape_thread(url: str):
response = session.get(url)
human_pause()
soup = BeautifulSoup(response.text, "html.parser")
posts = soup.select(".post") # adjust selector to the forum's markup
data = []
for post in posts:
post_id = post.get("data-post-id")
content = post.select_one(".content").get_text(strip=True)
timestamp = post.select_one(".date").get_text(strip=True)
data.append((url, post_id, content, timestamp))
# Store immediately
c.execute(
"INSERT INTO posts (thread_id, post_id, content, timestamp) VALUES (?,?,?,?)",
(url, post_id, content, timestamp)
)
conn.commit()
return data
TL;DR
- 先手动观察 再写代码。
- 使用 单一持久会话 并配上稳定的 User‑Agent。
- 在所有地方 插入类人暂停。
- 随机化 URL 顺序 与导航模式。
- 边爬边将数据持久化 到 SQLite(或其他数据库)。
- 遵守 robots.txt,避免搜索,并且变换你的爬取时间表。
- 被封锁时,放慢速度 而不是立刻升级。
content = post.select_one(".content")
if content:
data.append(content.get_text(strip=True))
return data
注意缺少的内容:没有并发、没有重试、没有提速技巧。这些以后再说,甚至可能永远不需要。
这会发生,即使你把一切都做对了。
不要立刻升级。只改变时间安排。延长等待时间,缩小爬取范围,甚至完全暂停。频繁更换 IP 或 User‑Agent 只会让你更显眼。有时最正确的做法就是保持沉闷。
反爬系统并不聪明——它们很焦虑。它们寻找速度、规律、流量和持久性。去掉这些信号,你就会消失在无尽刷屏的人群噪音中。
目标不是隐形,而是不重要。安静、缓慢、对任何人都不构成困扰。
爬取论坛并不是要突破技术壁垒,而是对一个想装作不在乎的系统进行社会工程。像一个无关紧要的人一样行动,你就会被放任自流。观察、暂停、打乱、阅读、等待、重复。
这就是用 Python 爬取论坛而永不触发反爬措施的方式。慢慢来,悄悄来,耐心来。带着一种“我永远也完成不了,但我不在乎,因为过程才是意义”的耐心。