几乎每个学 Python 的人都会被"爬虫"这两个字吸引:几行代码就能把网页上的数据批量搬回来,听起来像魔法。但 2026 年的爬虫早已不是 requests 加正则就能打天下的时代——TLS 指纹、行为检测、AI 抽取、合规红线,每一条都值得认真了解。

把"Python 爬虫从入门到精通"当成一条可以逐段验证的路线,会比背一堆库名字有用得多:先弄清它做什么、边界在哪,再依次走过请求、解析、动态渲染、工程化和规模化五个台阶。这篇文章写给两类人——刚装好 Python、想知道第一个爬虫怎么写的新手,以及已经能跑脚本、却总卡在反爬与工程化上的进阶者。每一节都尽量留下能自己改两行就跑起来的代码,读到结尾你可以按最后那份阶段清单逐项打勾。

爬虫到底在做什么

爬虫听着玄,本质只有三步:拿到网页、从网页里挑出你要的数据、把数据存下来。你打开浏览器、搜索、复制文字、粘进表格,这套动作重复一千遍是人力采集;把它写成程序自动跑,就是爬虫。

换个更形象的比喻:浏览器打开一个网页,本质上是一次"取快递"——浏览器(取件人)向服务器(仓库)发出 HTTP 请求(取件单),服务器把 HTML、CSS、JS 等文件(包裹)发回来,浏览器再把这些零件组装渲染成你看到的页面。爬虫做的事情,就是写一段程序代替浏览器去取包裹,然后从包裹里把有用的数据挑出来。

看清楚这三步,坑基本也分三层:网页拿不到,是请求层的问题;数据挑不准,是解析层的问题;存下来又乱又重、跑一半就断,是工程层的问题。新手最常见的失误,是找一段"万能爬虫代码"贴上去,结果三层同时出错,连坏在哪里都看不出来。

静态页面与动态页面

这是新手必须分清的第一对概念,也是后面所有技术选型的起点。静态页面的数据直接写在 HTML 里,请求一次就能拿到全部内容;动态页面的 HTML 只是个空架子,真正的数据由 JavaScript 在浏览器里再发请求、再渲染填进去。

判断方法很简单:在浏览器里右键"查看网页源代码",能搜到目标数据就是静态,搜不到就是动态。两者要用不同的工具对付。

三条不能碰的红线

在写第一行请求代码之前,先把边界讲清楚,这比任何技巧都重要。

  • 遵守 robots.txt 和平台用户协议。robots.txt 放在站点根目录,用很简单的指令说明哪些路径可以被抓、哪些不行。它不是法律文件,但 2026 年的司法实践里,"是否违反 robots.txt 与平台服务协议中的反爬条款"已经是认定行为正当与否的关键事实之一。
  • 不抓取个人信息与非公开数据。手机号、身份证号、订单、行踪、征信这类能识别到自然人的信息,受《个人信息保护法》约束;放在后台、登录后才可见的数据,即便屏幕上"看得见",也不等于"可以随便抓"。相关行为可能触及《刑法》第二百五十三条之一侵犯公民个人信息罪。
  • 不破解网站的技术保护措施。逆向破解接口签名、暴力绕过验证码、伪造登录态,这类操作在司法上很容易被认定为"非法获取计算机信息系统数据"(《刑法》第二百八十五条)或"破坏计算机信息系统"(第二百八十六条)。2025 年修订的《反不正当竞争法》第十三条也明确禁止"避开技术措施获取、使用他人数据"。

一个务实的自我判断标准是两句问话:你访问的频率会不会让对方服务器难受?你的用途会不会实质性替代对方提供的服务?前者关乎《网络安全法》下的义务,后者是反不正当竞争案件里最常出现的"实质性替代"标准。技术本身中立,使用技术的行为不中立。

第一站:把环境搭对

Python 版本与虚拟环境

2026 年做爬虫,建议 Python 3.11 起步;想追新也可以用 3.14(3.14.0 于 2025 年 10 月发布,目前已有多个维护版本)。但真正要紧的不是版本号,而是别往系统全局环境里装包:爬虫依赖更新很快,几个项目之间的 requests、httpx 版本经常打架,用虚拟环境隔离是最省事的做法。

python -m venv crawler-env                        # 创建干净的虚拟环境
source crawler-env/bin/activate                   # 激活,Windows 用 crawler-env\Scripts\activate
pip install requests beautifulsoup4 lxml          # 基础组合
pip install httpx parsel selectolax curl_cffi     # 现代一点的组合

装库的原则:按需,而不是按清单

不要一次性装三十个库。推荐的推进顺序是:先用 requests 加 BeautifulSoup 加 lxml 把流程走通;确认要做并发再上 httpx 或 asyncio;确认被 TLS 指纹拦截再考虑 curl_cffi;确认页面确实靠 JavaScript 渲染才动用 Playwright。

新手容易犯的错是上来就囤一堆库。实际上入门阶段只要 requests 加 BeautifulSoup 加 lxml,就能解决八成静态页面的采集需求。库里多一个,出问题时你要排查的面就多一层。

先把整个工具链摆出来,建立全局印象,再逐一站过去:

工具定位一句话说明
requests / httpx发请求最经典的 HTTP 库;httpx 是其现代化、支持异步的同类库
BeautifulSoup / lxml / parsel / selectolax解析 HTML把网页源码变成可按标签、按路径查找的结构化对象
curl_cffi发请求(伪装版)能模拟浏览器 TLS 指纹,对付指纹级反爬
Playwright / DrissionPage浏览器自动化驱动真实浏览器渲染页面,动态网站的万能钥匙
Scrapy爬虫框架大规模、工程化采集的标准框架
Crawl4AI / FirecrawlAI 爬虫用大模型直接从页面抽取结构化数据的新范式

第二站:发起请求

第一个能跑的请求

import requests

url = "https://httpbin.org/get"
headers = {
    "User-Agent": (
        "Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
        "AppleWebKit/537.36 (KHTML, like Gecko) "
        "Chrome/131.0.0.0 Safari/537.36"
    ),
    "Accept-Language": "zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7",
}

resp = requests.get(url, headers=headers, timeout=10)
print(resp.status_code)
print(resp.text[:200])

三个细节值得记住:一定要写 timeout,否则对方不响应时你的脚本会挂在那里;状态码是 200 也不代表成功,很多反爬系统会返回一个状态码正常、内容却是验证提示页的响应;带上正常的浏览器标识是基本礼仪,也是降低误封概率的第一步。

请求头不是随便写写

2026 年的请求头校验,已经不只看 User-Agent 是不是浏览器,还会看它和其它字段是否自洽。你换一个 Chrome 的 UA,却带着操作系统语言对不上的 Accept-Language,或者缺少浏览器本该发送的客户端提示字段,反而更容易被判定为脚本。

headers = {
    "User-Agent": (
        "Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
        "AppleWebKit/537.36 (KHTML, like Gecko) "
        "Chrome/131.0.0.0 Safari/537.36"
    ),
    "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
    "Accept-Language": "zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7",
    "Referer": "https://example.com/",
}

核心思路是让一组特征逻辑自洽,而不是把每个字段都随机填一遍。同一个出口 IP 一秒内发出十几个互不相同的 UA,比固定一个正常 UA 更像异常流量。

同步还是异步:httpx 的取舍

requests 简单好用,但它是同步阻塞的:发一个请求,线程就停在那里等回包。需要同时处理成百上千个地址时,httpx 的异步模式会明显快一截。

import asyncio
import httpx


async def fetch(client, url):
    resp = await client.get(url, timeout=10)
    return resp.status_code, len(resp.text)


async def main():
    urls = [f"https://example.com/page/{i}" for i in range(1, 21)]
    async with httpx.AsyncClient(http2=True) as client:
        tasks = [fetch(client, u) for u in urls]
        for result in await asyncio.gather(*tasks):
            print(result)


asyncio.run(main())

有个容易踩的版本坑:httpx 在 0.28 之后把代理参数从 proxies= 改成了单数的 proxy=,照旧写法会直接报错。另外 httpx 原生支持 HTTP/2,对于默认走 HTTP/2 的站点,连接复用与头部压缩都能省下不少时间。

TLS 指纹:requests 为什么被一眼认出

有些站点不看 User-Agent,而是看更底层的东西。你的客户端在 HTTPS 握手时会交换一串参数——加密套件顺序、扩展列表、椭圆曲线偏好,再加上 HTTP/2 的帧参数,这些组合起来就是所谓的 TLS 指纹(常说的 JA3)。真实浏览器和 requests 底层生成的指纹差异很大,服务器拿指纹一比对,就知道来的不是浏览器。

curl_cffi 是目前解决这个问题最省事的库:它基于 libcurl 做了浏览器指纹模拟,一个参数就能让请求在握手层更接近 Chrome 或 Firefox。

from curl_cffi import requests as crequests

resp = crequests.get(
    "https://example.com/",
    impersonate="chrome",
    timeout=10,
)
print(resp.status_code)
print(resp.text[:200])

这里有个安全提醒:curl_cffi 在 0.15.0 之前的版本存在一个 SSRF 漏洞(CVE-2026-33752),会把请求重定向到内网或云元数据地址,务必使用 0.15.0 及以上(当前稳定版已到 0.16.x)。

还有一句题外话——指纹模拟只能让合规的请求不被误伤,它不是突破网站技术保护措施的通行证。它只解决传输层的伪装,不执行 JavaScript,也没有内置重试。行为层的检测要靠控制频率、随机间隔来化解,JS 挑战层的检测还是要回到浏览器方案。攻防是动态博弈,任何"一劳永逸绕过一切反爬"的说法都不可信。

会话、Cookie 与登录态

需要保持登录状态时,用 Session 复用连接与 Cookie,比每次手动拼 Cookie 靠谱。

import requests

session = requests.Session()
session.headers.update({"User-Agent": "YourBot/1.0"})

session.get("https://example.com/login-page", timeout=10)
resp = session.get("https://example.com/profile", timeout=10)
print(resp.status_code)

再强调一次:能拿到登录态,不代表有权限抓登录后的内容。把自动化脚本套在自己的账号上,和替平台批量采集其它用户的数据,是性质完全不同的两件事。

第三站:把网页变成结构化的行

解析器怎么选

工具定位适合的场景
BeautifulSoup容错好、API 友好,需搭配解析器一次性脚本、结构不规整的页面
lxml速度快,支持 XPath结构规整、数据量大
parsel同时支持 CSS 与 XPathScrapy 项目、两种选择器混用
selectolax基于 C 引擎,解析快、内存低高并发、超大 HTML

它们不是竞争关系。常见组合是 BeautifulSoup 搭配 lxml 作为底层解析器:比内置解析器快,又能修一些残缺的标签。别在开发时用内置解析器、上线换成 lxml,不同解析器对同一段不规范 HTML 的修复结果可能不同,这种切换会引入"换了解析器就少了几条数据"的诡异问题。全程统一一种。

三十行代码爬下第一个页面

练习爬虫有专门的"合法靶场",比如专门供练习的名言采集站。下面这段完整代码做三件事:请求页面、解析数据、存成文件:

import csv
import requests
from bs4 import BeautifulSoup

# 1. 发请求:带上正常的浏览器标识是基本礼仪
headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64)"}
resp = requests.get("https://quotes.toscrape.com/", headers=headers, timeout=10)
resp.raise_for_status()

# 2. 解析:把 HTML 变成可以按选择器查找的对象
soup = BeautifulSoup(resp.text, "lxml")
quotes = soup.select("div.quote")

rows = []
for q in quotes:
    text = q.select_one("span.text").get_text(strip=True)
    author = q.select_one("small.author").get_text(strip=True)
    rows.append([text, author])

# 3. 存储:写入 CSV 文件
with open("quotes.csv", "w", newline="", encoding="utf-8-sig") as f:
    writer = csv.writer(f)
    writer.writerow(["quote", "author"])
    writer.writerows(rows)

print(f"共采集 {len(rows)} 条名言")

这段代码里藏着三个好习惯:带 User-Agent 表明正常身份、设置 timeout 防止无限等待、用 utf-8-sig 编码避免 Excel 打开 CSV 时中文乱码。入门阶段把这三件事刻进肌肉记忆,能避开一半的"莫名其妙报错"。

CSS 选择器还是 XPath

两者都能定位元素。CSS 更短、读起来更顺,但它不能按文本内容选,也没有父节点、祖先节点这类轴;XPath 能做这些事,代价是写起来更长、更易错。日常解析优先用 CSS,遇到"按文字找节点""从子节点反查父节点"再换 XPath。

CSS 选择器是最直观的定位语言:div.quote 表示找 class 为 quote 的 div,div.quote span 表示找它内部的 span,div.quote > span 表示只找直接子级,#content 按 id 找,ul li:nth-child(2) 按位置找。日常八成的解析需求,掌握这五六个语法就够了。

from bs4 import BeautifulSoup

soup = BeautifulSoup(html, "lxml")

for card in soup.select("div.product-list article.product"):
    link = card.select_one("h3 a")
    price = card.select_one("span.price")
    if not link or not price:
        continue
    print(link.get("title"), price.get_text(strip=True))

这里的 html 就是前面请求拿到的页面文本。注意代码里那条 if not link or not price: continue——页面上少一个节点就整段报错,是新手脚本最常见的崩溃原因。

让解析代码抗改版

选择器写得越贴近页面结构,越容易碎。div:nth-child(3) 这种写法,对方一改版就全废;尽量抓语义化的锚点,比如带 id 或者带 data 属性的容器。也不要照抄浏览器复制的完整路径(那种从 html 一路写到目标元素的长串),正确做法是找目标数据最近的"稳定锚点",从锚点往下走一两级,路径越短越抗改版。

文本取值也有讲究:

import re


def clean(text):
    if text is None:
        return ""
    return re.sub(r"\s+", " ", text).strip()


raw = "  12.30\n元 "
print(clean(raw))

get_text(strip=True) 只去掉首尾空白,不会合并中间多余的换行和空格,稳妥做法是再用正则压一遍。

把结果存好

抓完就 print 等于白抓。小规模存 CSV 或 JSON 最方便,注意用 utf-8-sig 编码,否则 Excel 打开会乱码。

import csv

rows = [
    {"title": "示例一", "price": "12.30"},
    {"title": "示例二", "price": "45.00"},
]

with open("books.csv", "w", newline="", encoding="utf-8-sig") as f:
    writer = csv.DictWriter(f, fieldnames=["title", "price"])
    writer.writeheader()
    writer.writerows(rows)

数据量再大就该换数据库了。写入前顺手做一次去重和字段校验,比事后清洗省力得多。

第四站:动态页面

什么时候必须动用浏览器

如果数据是打开页面时由 JavaScript 拼出来的,你直接请求 HTML 只会拿到一个空壳。判断方法前面已经讲过:禁用 JavaScript 刷新页面,内容还在,就说明请求层能搞定;内容没了,才需要上浏览器。

但先别急着上。打开开发者工具的网络面板,刷新页面,在 XHR 或 Fetch 分类下找到返回 JSON 数据的那个请求——很多情况下,你可以直接模拟这个接口请求,拿到干净的 JSON 数据,连解析 HTML 都省了。能用接口解决的,就不要渲染整页:更快、更省资源,也更不容易被识别。这是效率最高的路径,值得每次动手前都先试一遍。

Playwright 起步

需要真正驱动浏览器时,Playwright 是当前的首选。它由微软维护,原生支持 Chromium、Firefox、WebKit,内置自动等待,比早期的 Selenium 稳不少。

pip install playwright
playwright install chromium
from playwright.sync_api import sync_playwright

with sync_playwright() as p:
    browser = p.chromium.launch(headless=True)
    page = browser.new_page()
    page.goto("https://example.com/", wait_until="networkidle")
    print(page.title())
    links = page.query_selector_all("a")
    print(len(links))
    browser.close()

两个 2026 年的实操要点:Playwright 现在要求 Python 3.10 及以上(1.62 与 1.63 分别在 2026 年 7 月、9 月发布);playwright install chrome 装的是带品牌的 Chrome,不会装 Chromium 本身,如果你装完直接以默认渠道启动,会报"找不到可执行文件"。另外,工作目录里放一个叫 playwright.py 的文件,会把真正的包遮蔽掉,这是很多人遇到过又想不到的导入错误。

自动化特征与它带来的问题

Playwright 默认识别特征比较明显:浏览器暴露的 navigator.webdriver 标记、通过 CDP 协议通信留下的痕迹,以及 WebGL、Canvas 渲染上的细微差异,都可能被检测出来。社区因此有了 stealth 类补丁,在页面加载前注入脚本,把这些特征改得更像一台普通浏览器。

需要说清楚的是:这类补丁的正当用途,是让合规采集不被误伤,而不是用来绕过网站的技术保护措施。绕过验证码、破解接口签名、伪造他人登录态,前面已经讲过它们的法律性质。指纹伪装能降低误封概率,但改变不了你行为的性质。

动态页面的性能代价

一个浏览器实例动辄占用几百兆内存,几十个并发一开,普通机器就撑不住了。经验法则是:能用接口就不用渲染,能渲染一个页面就不渲染十个——浏览器只用来解决"第一公里"的登录和渲染,后续翻页能回退到接口就回退。实践上还要控制并发数、复用浏览器上下文(context)、用完及时关闭。

第五站:工程化,交给 Scrapy

2026 年的 Scrapy 长什么样

当采集从"几百条"变成"几十万条",脚本式的写法会迅速失控:断点续爬、去重、限速、重试,全都得自己写。这时候该上框架。Scrapy 依然是这套场景里的主力,目前稳定版是 2.19.0(2026 年 9 月发布)。

近几个版本的几个变化值得留意:2.16.0 起官方支持 Python 3.14;2.18.0 起内置了基于 httpx2 的下载处理器,Twisted 版的 HTTP/2 处理器不再是实验特性;2.19 新增了 RemoteControl 扩展,可以通过 HTTP 检查和控制正在运行的爬虫。另外,老的 start_requests() 方法已经被 start() 取代。

scrapy startproject blog_spider
cd blog_spider
scrapy genspider example example.com
import scrapy


class ExampleSpider(scrapy.Spider):
    name = "example"
    start_urls = ["https://example.com/"]

    async def start(self):
        for url in self.start_urls:
            yield scrapy.Request(url=url, callback=self.parse)

    def parse(self, response):
        for item in response.css("article.product_pod"):
            yield {
                "title": item.css("h3 a::attr(title)").get(),
                "price": item.css("p.price_color::text").get(),
            }
        next_page = response.css("li.next a::attr(href)").get()
        if next_page:
            yield response.follow(next_page, callback=self.parse)

框架帮你把请求调度、并发控制、去重、重试这些"脏活"都接管了,你只需要写好"从哪开始爬"和"怎么解析"两件事。如果你还在用更早的版本,start() 需要换回 start_requests()。

中间件与管道

Scrapy 的扩展点主要就三处:爬虫中间件管请求与结果的加工,下载器中间件管出网请求(比如换 UA、加代理),Item 管道管数据的清洗与落库。

DOWNLOADER_MIDDLEWARES = {
    "blog_spider.middlewares.RotateUserAgentMiddleware": 543,
}

ITEM_PIPELINES = {
    "blog_spider.pipelines.CleanPipeline": 300,
    "blog_spider.pipelines.CsvPipeline": 800,
}

ROBOTSTXT_OBEY = True
DOWNLOAD_DELAY = 1.5
CONCURRENT_REQUESTS_PER_DOMAIN = 4

注意 ROBOTSTXT_OBEY = True 是新建项目时的默认值,别顺手改成 False——很多项目出事,就是从这个开关开始的。

去重、限速与重试

Scrapy 内置了基于请求指纹的去重,但限速和重试建议显式配置。别用固定延时硬等,用自动限速让它根据对方响应时间自己调节,比人手拍一个数字更友好,也更不容易触发风控。

AUTOTHROTTLE_ENABLED = True
AUTOTHROTTLE_START_DELAY = 1.0
AUTOTHROTTLE_MAX_DELAY = 10.0
AUTOTHROTTLE_TARGET_CONCURRENCY = 2.0

RETRY_ENABLED = True
RETRY_TIMES = 3

第六站:规模化

并发模型怎么选

简单说:IO 密集的采集任务,瓶颈几乎都在网络等待上,多开进程是浪费内存。中小规模用线程池,大规模用 asyncio 配合 httpx 或 aiohttp。但并发数不是越高越好——并发拉满换来的是更快的封禁,而不是更快的采集。把并发控制在对方能接受的范围内,是长期跑得稳的前提。

代理是怎么一回事

代理的作用是分散出口 IP,避免单个 IP 的请求量过大。常见的几类差别很大:

类型来源特点常见用途
数据中心代理云机房 IP 段快、便宜,但容易被标记公开数据、低防护站点
动态住宅代理真实家庭宽带更像普通用户,按流量计费防护较严站点的常规采集
静态住宅代理固定分配的住宅 IP稳定、可长期保持会话需要登录态的长期任务
移动代理4G 与 5G 基站隐蔽性最好,也最贵前几类都过不去的场景

有两个常见误用:一是全量上住宅代理,成本翻好几倍却未必需要;二是在需要保持会话的任务里用"每请求换一次 IP"的轮换,导致登录态频繁失效。选型前先想清楚你的任务是高频短请求,还是需要连续会话。

还要提醒一句:代理是让你分散压力、避免误伤正常服务,不是用来攒一堆 IP 对某个站点发起高频冲击的。后者既有封禁风险,也可能触碰前面讲过的法律边界。

增量采集与缓存

长期运行的项目,最省成本的一步是别重复抓已经抓过的页面。记录已处理的 URL,或者用响应头里的 ETag、Last-Modified 判断内容有没有变;开发阶段还可以把响应落盘做本地缓存,避免反复打扰目标站点。

import hashlib
import json
from pathlib import Path

CACHE = Path("cache")
CACHE.mkdir(exist_ok=True)


def cache_key(url: str) -> str:
    return hashlib.sha1(url.encode("utf-8")).hexdigest()


def save(url: str, text: str) -> None:
    payload = {"url": url, "text": text}
    (CACHE / f"{cache_key(url)}.json").write_text(
        json.dumps(payload, ensure_ascii=False),
        encoding="utf-8",
    )

第七站:AI 时代的新范式

不写选择器的爬虫

2025 年以来兴起了一类新工具,代表是开源的 Crawl4AI 和云服务 Firecrawl。它们的思路是把大模型引入采集流程:Crawl4AI 负责渲染页面并转成干净的 Markdown,再按你给的提示词让大模型直接抽出结构化字段;Firecrawl 则以 API 服务的形式提供类似能力,抓取、渲染、反爬都托管在云端。

最大的好处是免去了写选择器、适配改版的重复劳动,页面结构变了,提示词通常不用变。对于原型验证、低频采集、结构多变的页面,这条路线能省下大量时间。

新范式不是银弹

代价同样明显:

  • LLM 抽取按调用计费,大规模采集的成本远高于传统方案
  • 抽取结果存在小概率的幻觉和漏字段,关键业务需要校验
  • 把页面内容发给第三方模型还涉及数据出域问题

合理的定位是:原型验证、低频采集、结构多变的页面用 AI 爬虫快速搞定;高频、大规模、字段固定的采集,依然回归传统方案的成本优势。

学习路线:分三阶段打勾

入门阶段,一到两周

目标是能把一个静态页面从头抓到尾,不追求性能。

  • 会用 requests 发请求、设请求头、处理超时与状态码
  • 会用 BeautifulSoup 加 lxml 提取标题、价格这类单层数据
  • 会把结果存成 CSV 或 JSON,并处理中文编码
  • 会看 robots.txt,知道哪些路径不该碰
  • 在练习站上完成"请求、解析、存储"的完整闭环

进阶阶段,一到两个月

目标是能应对动态页面和中等规模任务,代码不再一碰就碎。

  • 会看浏览器的网络面板,判断数据来自接口还是渲染
  • 会用 asyncio 加 httpx 做并发请求,理解并发与频率控制的关系
  • 会用 Playwright 处理 JavaScript 渲染的页面
  • 会用 Scrapy 搭一个带中间件和管道的完整项目
  • 会写健壮的解析代码:缺字段跳过、正则清洗、异常捕获
  • 掌握 CSS 选择器,能处理翻页、登录 Cookie、简单加密参数

精通阶段,持续投入

目标是从"能抓"走到"长期稳定、合规、可维护"。

  • 能设计去重、断点续爬、增量更新的完整调度
  • 理解 TLS 指纹、请求特征一致性这类底层原理,知道什么该用、什么不该用
  • 能搭代理调度与监控,出错能定位到请求层还是解析层
  • 熟悉《个人信息保护法》《反不正当竞争法》和刑事红线的边界,能在动手前做合规判断
  • 会把采集结果接到下游的数据分析流程里,而不只是停在原始表格

常见坑排查清单

现象常见原因应对方向
返回 403 或 503请求头不完整、频率过高检查请求头自洽性,降低并发、加自动限速
状态码 200 但内容为空数据由 JavaScript 渲染找接口,或改用 Playwright
页面是验证提示被风控识别停下检查频率与合规性,不要硬闯
中文乱码编码判断错误显式设置 encoding,或直接解析原始字节
数据缺字段页面改版或选择器过脆用语义化选择器,解析时做空值判断
跑一段时间就断内存堆积、未做重试分批处理、及时释放、配置重试与超时
明明换了 IP 还被封请求特征与行为模式被关联让整组特征自洽,而不是只换 IP

法律与合规:技术无罪但边界清晰

司法实践的态度可以概括为:网络爬虫本身并不违法,其合法性取决于数据公开程度、抓取手段、使用目的及对平台运行和竞争利益的影响。公开的、无防护的数据以人类等效的频率采集,风险很低;而绕过验证码和登录墙、打爆对方服务器、把人家的核心数据搬来做同类生意,每一条都有真实判例。

近年公开的判例里,有企业因搬运数千万条电商商品数据被行政处罚,有平台因后台数据被技术手段穿透获取获赔五百余万元,也有因全文截取他站评论、实质替代原站服务被判赔三百万元的案例。

robots.txt 是网站声明"哪些路径不希望被抓"的约定文件。它不是法律条文,但无视它在诉讼中会被当作过错的佐证,养成动手前先看一下的习惯。真正的三条法律红线是:

  • 个人信息:刑法规定五十条敏感个人信息即可入刑,且需遵守个人信息保护法的明示同意要求
  • 著作权内容:未经授权的规模化复制和替代性使用
  • 计算机系统安全:绕过或破坏技术防护措施可能构成刑事犯罪

一份可执行的合规清单,每次动手前过一遍:

  • 数据是否公开可访问、是否需要登录或绕过验证
  • 请求频率是否控制在人类水平
  • 是否只取需要的字段而非整站镜像
  • 采集结果的用途是否与原站构成直接竞争
  • 涉及个人信息是否已获授权
  • 目标网站有没有官方开放 API 可以优先使用

清单任何一项亮起红灯,就该停下来重新评估,而不是想办法"绕过去"。

结语

爬虫技术这两年最大的变化,不是"怎么抓得更快",而是"哪些能抓、怎么抓才站得住"。库在迭代,反爬在升级,法律边界也在收紧,唯一不变的是那三步基本功:请求、解析、存储。把这三步练扎实,再往上叠加框架、并发和调度,你会发现自己不再需要到处找"万能代码"——因为每一个问题出在哪一层,你心里都有数。

技术能带你走多远,取决于你愿不愿意在动手前先问一句:这件事,该不该我做。

参考资料与延伸阅读

发表评论

验证码图片,点击可更换