爬虫是什么:原理、边界与合规
爬虫就是一段自动化的 HTTP 客户端:代替人在浏览器里点击、翻页、复制,把公开网页或接口返回的数据按规则提取并保存下来。技术上它不神秘,真正决定一个采集项目能不能长期跑下去的是边界意识——抓什么、抓多少、以什么身份抓、抓完能用来做什么。
一个爬虫的三步循环
无论用 requests 脚本、Selenium 还是 Scrapy,骨架永远是这三步:
请求(Request) → 解析(Parse) → 存储(Store)
↑ │
└────── 发现新 URL,回到队列 ←────────┘
- 请求:构造 URL 与请求头,发起 HTTP 请求,拿到 HTML、JSON 或文件。
- 解析:提取结构化字段,同时发现下一页与详情页链接,形成待抓队列。
- 存储:写入 CSV、JSONL 或数据库,并记录已处理状态,便于断点续爬。
requests 负责第一步,BeautifulSoup、lxml、parsel 负责第二步,csv、sqlite3 或数据库负责第三步;Selenium / Playwright 只是把第一步换成"驱动真实浏览器",Scrapy 则把三步装进一个自带调度与限速的框架。
能做什么,不能做什么
| 可以做 | 不可以做 |
|---|---|
| 采集无需登录即可访问的公开数据 | 抓取需要登录或付费才能看到的内容 |
| 读取开放数据、政府公开信息 | 采集他人隐私信息(手机号、身份证、位置轨迹) |
| 按官方开放 API 与配额调用数据 | 绕过验证码、付费墙、IP 封禁等访问控制 |
| 用于个人学习、学术研究与内部效率工具 | 原样转卖抓取内容、二次分发受版权保护的作品 |
| 合理频率下访问,像正常读者一样 | 高并发式抓取,实质性影响对方服务 |
先问有没有官方接口,再考虑写爬虫
动手写代码之前先花十分钟做这件事,往往能省掉几天的活:
1. 找官方开放平台或 API 文档,申请 API Key(有配额、有文档、有授权依据)
2. 找开放数据集(政府开放数据、数据交易所、公开数据集社区)
3. 找站点主动提供的 RSS、sitemap.xml 或结构化数据
4. 联系站点商务或数据合作,购买授权数据
5. 以上都没有,再评估自写爬虫,并接受它随时可能因改版而失效
官方接口比爬虫稳得多:字段有文档、版本可控、不怕改版,还省掉整条反爬对抗的链路。爬虫是最后手段,不是第一选择。
robots.txt 怎么读
robots.txt 是站点放在根目录(例如 https://example.com/robots.txt)的访问约定,用 User-agent 分组声明哪些路径允许或禁止抓取,部分站点还会给出 Crawl-delay 与 Sitemap。
User-agent: * # 对所有爬虫生效的默认分组
Disallow: /search # 禁止抓取 /search 开头的路径
Allow: /search/about # 更具体的允许规则优先
Disallow: /private/
Crawl-delay: 1 # 建议的请求间隔(秒)
Sitemap: https://example.com/sitemap.xml
读法的三个要点:
- 分组按
User-agent匹配,最具体者优先;Disallow: /是全站禁止,Disallow:(空值)是全站允许。 - 它只约束"抓取",不授权"使用"。允许抓取不等于允许转载、商用或再分发。
- 它没有强制力,但它是站点表达意愿的方式。无视它,意味着在明知对方不同意的情况下继续访问。
Python 标准库自带解析器,把判断交给它,比自己用字符串硬拼可靠:
from urllib.parse import urljoin
from urllib.robotparser import RobotFileParser
import requests # pip install requests
UA = "RuilinToolsBot/1.0 (+https://example.com/bot; contact@example.com)"
def load_robots(base: str) -> RobotFileParser:
"""下载并解析 robots.txt;取不到就按禁止处理,宁可保守。"""
rp = RobotFileParser()
rp.set_url(urljoin(base, "/robots.txt"))
try:
resp = requests.get(rp.url, headers={"User-Agent": UA}, timeout=(3, 10))
resp.raise_for_status()
rp.parse(resp.text.splitlines())
except requests.RequestException as exc:
print(f"robots.txt 获取失败:{exc},按禁止处理")
rp.disallow_all = True
return rp
if __name__ == "__main__":
site = "https://example.com"
parser = load_robots(site)
for path in ("/", "/search?q=python", "/api/list"):
target = urljoin(site, path)
print(target, "允许" if parser.can_fetch(UA, target) else "禁止")
print("Crawl-delay:", parser.crawl_delay(UA)) # 可据此设置请求间隔
频率控制与身份标识
把请求间隔与并发上限写进代码,而不是靠"我抓得不多"的直觉:
| 控制项 | 建议起点 | 说明 |
|---|---|---|
| 请求间隔 | 单线程 1~3 秒 | 站点声明了 Crawl-delay 时取两者较大值 |
| 单域名并发 | 不超过 4~5 | 并发是给别人的服务器加压力,不是给自己刷分 |
| 单轮总量 | 先小批量试跑 50~100 条 | 字段与频率确认正常后再放开 |
| 抓取时段 | 避开对方业务高峰 | 凌晨抓取对双方都更友好 |
| 失败退避 | 429/5xx 时指数退避并暂停 | 对方在说"慢一点",不要硬顶 |
| 身份标识 | 带联系方式的自定义 UA | 出问题时能被联系,也便于对方按组放行 |
合规判断的三个问题
动手前依次回答,任何一个是"不确定",就先停下来弄清楚:
| 问题 | 判断依据 | 答"是"怎么办 |
|---|---|---|
| 数据是公开的吗 | 不登录、不付费、不猜参数就能看到 | 仍需遵守 robots.txt 与站点条款 |
| 会影响对方服务吗 | 并发与频率是否显著高于正常用户 | 降频、错峰、改用官方接口 |
| 涉及个人信息吗 | 是否含可识别到具体自然人的字段 | 不采集;确需采集则取得同意并最小化 |
法律边界:三部法律
| 法律 | 与数据采集相关的要点 |
|---|---|
| 《个人信息保护法》 | 处理个人信息需有合法性基础与告知同意,遵循最小必要原则,禁止非法买卖个人信息 |
| 《数据安全法》 | 数据分类分级保护,重要数据的交易与出境受限,不得以窃取等非法方式获取数据 |
| 《著作权法》 | 作品受法律保护,复制与传播需授权,合理使用范围有限,不覆盖大规模转载 |
再叠加上刑法中侵犯公民个人信息、非法获取计算机信息系统数据的相关条款,可以概括成一句话:突破访问控制去拿本不该看到的数据,事情就从技术问题变成了法律问题。
常见误区
| 误区 | 后果 | 正确做法 |
|---|---|---|
| 能打开页面就等于能抓 | 忽略条款与 robots 约定,风险自担 | 先看 robots.txt 与用户协议 |
| 伪装成浏览器 UA 就安全 | 只隐去了身份,不改变行为性质 | 用可识别的自定义 UA,并控制频率 |
| 并发越高效率越高 | 触发限流,甚至被临时封禁 | 先测单请求耗时,再定并发上限 |
| 抓到的数据可以直接商用 | 侵权风险,图文与付费内容尤甚 | 找授权、买数据,或只用开放数据 |
| robots.txt 没强制力就可以无视 | 民事乃至刑事风险由自己承担 | 把它当作对方明确的意愿声明 |
小结:爬虫只是把「请求—解析—存储」自动化,技术门槛不高,真正决定项目寿命的是边界——先找官方 API 与开放数据,读 robots.txt 与站点条款,控制频率并标明身份,不碰个人隐私与付费内容,不绕过任何访问控制。