Python网络爬虫入门:从HTTP原理到Scrapy框架实战
2026/9/9 14:39:33 网站建设 项目流程

如果你每天需要手动打开几十个网页,把标题、价格、评分一条条复制到表格里,那么这篇文章就是为你准备的。网络爬虫(Web Crawler)说白了就是一个自动帮你浏览网页、摘取信息的程序,它不会累、不会看错行、也不会在复制第两百条数据时突然想刷手机。我从完全不懂 HTTP 协议的小白,到能独立写 Scrapy 框架抓取完整站点,走了不少弯路,踩过不少坑。这篇入门指南会把我认为最关键的知识点逐一拆开讲清楚,覆盖请求原理、Python 爬虫开发、动态页面处理、Scrapy 框架、数据存储,以及一个新手最容易忽略的合规边界。不管你是做数据分析、产品调研、毕业论文数据收集,还是单纯想提升技能,这篇内容都能帮你建立起完整的爬虫知识骨架。

1. 先搞清楚爬虫的本质:它就是一个定时帮你看网页的程序

1.1 浏览器做了什么,爬虫就做什么

理解爬虫之前,先想一个问题:当你打开一个网页时,电脑和服务器之间到底发生了什么?

你用浏览器输入网址、按下回车,浏览器会向目标服务器发送一个"请求",服务器处理之后返回一份"响应",浏览器把响应内容渲染成你能看到的图文页面。整个过程中,浏览器就是一个会展示的客户端。

爬虫做的事情完全一样:它也是向服务器发送请求、接收响应,只不过它不去"渲染"页面,而是直接读取响应里的原始内容(HTML、JSON、CSS、JavaScript 等),再从中提取你关心的数据。

理解这一点特别重要,因为它决定了你学爬虫时的思维方式。我见过太多新手一上来就去背 XPath 语法、记 BeautifulSoup 方法,但实际上爬虫的核心瓶颈根本不在解析,而在请求这层。你能不能让服务器认为"来访的是一个正常用户",能不能在正确的时机、以正确的频率发请求,直接决定了你抓数据是顺利还是被限制。

沿用同一个类比:你手动浏览网页时不会在 10 秒内狂点 100 次刷新,不会一进网站就把所有链接瞬间点完,爬虫也一样。学爬虫,本质上就是把你"人工浏览网页"的动作拆解成程序逻辑,再加一点工程化手段让它更快、更稳定。

1.2 一个爬虫的三步工作流:请求、解析、存储

不管你的爬虫写得多复杂,最终都逃不出下面三步:

  1. 请求:向目标 URL 发送 HTTP 请求,拿到响应内容。
  2. 解析:从响应中提取需要的数据。静态页面通常是 HTML 结构,动态接口通常是 JSON 结构。
  3. 存储:把提取的数据保存到 Excel、CSV、数据库等地方,方便后续使用。

我刚开始学的时候,总觉得要掌握很多框架,其实一个最简单的爬虫,用两个 Python 库就能完成:requests负责请求,BeautifulSoup负责解析。等需求复杂了再上 Scrapy,等需要自动化浏览器操作了再上 Selenium。循序渐进,不要一上来就搞全家桶。

我在第一周做的小练习是抓取一个图书网站的标题和价格列表。那时候连 CSS 选择器是什么都不知道,照着教程一步步敲,跑通那一刻确实挺有成就感的。后来回头看,那个程序大概只有二十行代码,但正是这二十行代码让我把"请求—解析—存储"这条链路彻底想明白了。

1.3 哪些人需要学爬虫,哪些人其实不需要

这也是新人最容易忽略的问题。学任何技能前先判断自己是否真的需要,能帮你少走大量弯路。

需要学的典型场景:

  • 做数据分析或商业分析,需要从公开网站获取结构化数据集;
  • 做产品调研,要横向对比多个平台的商品价格、用户评分;
  • 学生做论文或课程项目,需要批量收集公开信息;
  • 需要定时监控某些页面变化(比如抢票提醒、价格变动提醒)。

不一定需要学爬虫的场景:

  • 只需要偶尔抓一两次数据,量也不大,完全可以考虑可视化采集工具(这类工具下文会提),没必要写代码;
  • 目标网站已经提供了官方 API,优先用 API,又快又合法,我去接一些数据源时会先翻开发者文档,确认有没有 API 可用;
  • 数据量极大且更新频繁,这种往往涉及商业合作或更复杂的工程方案,已经不属于入门范畴了。

你属于哪种?想清楚了再往下学,效率会高很多。

2. 底层通信拆解:一个 HTTP 请求从发出到返回经历了什么

2.1 URL、请求方法、状态码:三个最基础的概念

爬虫的一切操作都建立在 HTTP 协议之上。不用把它想得多高深,你只需要理解几个核心概念。

URL 的结构,以https://example.com/products?category=book&page=1为例:

  • https:协议,表明通信方式;
  • example.com:域名,定位到服务器;
  • /products:路径,定位到服务器上的某个资源;
  • ?category=book&page=1:查询参数,两个参数键值对,以&分隔。

抓取带翻页的列表页时,我经常会在循环里动态拼接 URL,查询参数的正确理解直接影响你能不能让翻页逻辑跑通。

请求方法你平时只需要关心两个:GET 和 POST。GET 一般是获取数据,参数拼在 URL 上;POST 一般是提交数据,参数放在请求体里。登录、搜索、筛选这类功能,很多都走 POST。写爬虫时如果发现 GET 请求拿不到数据,可以打开开发者工具看看实际请求是不是 POST。

状态码是服务器给你的一个"反馈",判断请求是否成功就靠它。实用主义地记几个:

状态码含义爬虫中常见场景
200请求成功正常拿到数据
301/302重定向网页迁移,requests 默认会跟随
403禁止访问大概率被反爬拦截,或被限制权限
404页面不存在URL 写错或资源删除
429请求过于频繁被限速了,需要放慢频率
500/502/503服务器错误服务器自身问题,偶尔是反爬手段

遇到 403 和 429,先别急着骂服务器,回头检查自己的请求头、频率,多半能找到原因。

2.2 Header 里藏着什么:User-Agent、Cookie、Referer

很多新手第一次写爬虫,请求头什么都不设置,直接用默认的python-requests去访问,结果轻而易举地返回 403。问题就出在:服务器一眼就能认出这不是一个正常浏览器发出的请求。

HTTP 请求头里最关键的三样东西:

  • User-Agent(UA):客户端身份标识。正常浏览器会带上类似Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36...这样的字符串。requests 默认的 UA 是python-requests/x.x.x,非常"扎眼"。
  • Cookie:网站用来维持会话状态的凭证。涉及登录后才能看到的内容时,必须带上登录后拿到的 Cookie,否则服务器认定你未登录,返回的数据自然不完整。
  • Referer:表明你从哪个页面跳转过来。部分网站会校验这个字段,防止跨域盗链或者直接访问资源,带上它能大幅降低被拦截的概率。

我习惯的做法是:先用浏览器正常打开目标页面,打开开发者工具的 Network 面板,看一次真实请求长什么样,然后把自己的请求头模仿到位。这样既省事,又能避免很多无头无脑的拦截问题。

注意一点:模仿请求头是为了让你的爬虫表现得像一个正常访客,而不是试图伪装成别人或绕过验证。这个度要把握好,这也是我后面会提到的合规底线的一部分。

2.3 响应内容:HTML 是结构,JSON 是数据

拿到响应之后,你要能判断"好东西到底在哪个格式里"。

HTML 是网页的结构化标记,数据往往嵌套在标签之中。比如:

<div class="prod-item"> <span class="price">29.90</span> <a href="/item/1001">无线鼠标</a> </div>

解析 HTML 时,你的目标是找到承载数据的标签、类名、属性,然后用选择器把它们提取出来。

JSON 则是现代的"数据格式"。很多网站的前端页面上看到的数字,其实是从后端接口拿到的 JSON 数据,再渲染上去的。JSON 结构清晰,通常长这样:

{ "code": 0, "data": { "list": [ {"name": "无线鼠标", "price": "29.90"} ], "total": 100 } }

如果响应是 JSON,直接用 Python 的json模块或者response.json()就能解析成字典,比解析 HTML 省力得多。

所以拿到一个响应后,我的第一步永远都是:看一眼 Content-Type。如果是application/json,走数据提取流程;如果是text/html,重新找数据所在的位置。这个习惯帮我省了无数时间。

3. 第一个 Python 爬虫:requests + BeautifulSoup 抓取静态页面

3.1 环境准备:Python、pip、常用库安装

假设你已经装好了 Python 3(如果没装,去官网下载对应版本,安装时记得勾选 Add Python to PATH)。然后打开命令行,安装本次要用到的库:

pip install requests beautifulsoup4 lxml

lxml是解析器,速度比 BeautifulSoup 默认的 Python 解析器快很多,强烈建议一起装上。装完之后可以用一行代码验证:

python -c "import requests; print(requests.__version__)"

能输出版本号,说明环境就绪了。

我当年踩过最大的坑是没搞懂"虚拟环境"。如果你同时在维护多个 Python 项目,不同项目依赖的库版本可能冲突,这时候用python -m venv myenv创建独立环境,然后source myenv/bin/activate(Windows 是myenv\Scripts\activate)激活,再安装依赖。现在养成习惯,每个项目单独一个虚拟环境,能少掉很多头发。

3.2 代码拆解:从发起请求到提取数据

我们用一个专门用于爬虫练习的公开网站quotes.toscrape.com做例子,它上面是名人名言列表,没有任何敏感内容,非常适合练手。

第一段完整代码:

import requests from bs4 import BeautifulSoup # 1. 请求 url = "http://quotes.toscrape.com/" headers = { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0 Safari/537.36" } response = requests.get(url, headers=headers, timeout=10) print("状态码:", response.status_code) # 2. 解析 soup = BeautifulSoup(response.text, "lxml") quotes = soup.select("div.quote") # 3. 提取并存储 for quote in quotes: text = quote.select_one("span.text").get_text() author = quote.select_one("small.author").get_text() print(f"{author}: {text}")

逐行解释几个关键点:

  • headers字典里模拟了浏览器 UA,这是最基本的礼貌;
  • timeout=10:设置超时时间,防止某个请求卡死导致程序挂起,这是写爬虫几乎必加的参数;
  • soup.select("div.quote"):用 CSS 选择器定位所有 class 为 quote 的 div 容器;
  • quote.select_one("span.text").get_text():在每个容器内继续定位第一个 span 标签,取出文本。

跑完这段代码,你就能在命令行里看到一串名言和作者名。没错,你的第一个爬虫跑通了。

3.3 翻页循环:把"爬一个页面"升级成"爬一整个网站"

单页爬虫意义有限,绝大多数实战场景都要处理翻页。看这个站点的翻页 URL:http://quotes.toscrape.com/page/1/,页码直接反映在 URL 里,所以循环构造 URL 即可:

import requests from bs4 import BeautifulSoup base_url = "http://quotes.toscrape.com/page/{}/" all_data = [] for page in range(1, 11): url = base_url.format(page) response = requests.get(url, headers=headers, timeout=10) soup = BeautifulSoup(response.text, "lxml") quotes = soup.select("div.quote") if not quotes: print(f"第 {page} 页没有数据,停止翻页") break for quote in quotes: text = quote.select_one("span.text").get_text() author = quote.select_one("small.author").get_text() all_data.append({"author": author, "text": text}) print(f"第 {page} 页完成,累计 {len(all_data)} 条") time.sleep(0.5) # 每页之间停顿,避免请求过密

这里我刻意加了time.sleep(0.5)。很多教程不会告诉你这个小停顿有多重要:服务器对请求频率很敏感,爬得快不等于爬得稳。你以为的"效率",在服务器眼里可能是一次小规模攻击。适度的延迟,是爬虫能长期稳定运行的基础。

3.4 解析技巧:CSS 选择器和 XPath 怎么选

BeautifulSoup 支持两种主流的元素定位方式。

CSS 选择器写法直观,和前端选择器一致:

  • div.quote:class 为 quote 的 div;
  • #content:id 为 content 的元素;
  • ul li a:ul 下的所有 li 下的 a;
  • span.text::text这种是 Scrapy 的写法,不是 BeautifulSoup 的,注意区分。

XPath 则更强大,适合复杂层级关系。比如取第二条引言的正文:

quote_text = soup.xpath('(//div[@class="quote"]/span[@class="text"])[2]/text()')

这里 XPath 公式做了几件事://在任意层级查找,[@class="quote"]属性筛选,[2]取第二个节点,/text()取节点文本。

我的建议:能看懂 CSS 选择器的前提下,先把selectselect_one用熟。遇到实在复杂的选择需求,再用 XPath 做补充。实际开发中 80% 的场景用 CSS 选择器就够了,不必一开始就平均用力。

还要注意一个隐蔽的坑:get_text()得到的结果经常带首尾空格和换行符,建议统一strip()一下。另外,某些字段可能是动态内容(稍后介绍),用 requests 拿到的是渲染前的结果,找不到数据时不要怀疑代码,先怀疑页面是不是动态的。

4. 遇到动态加载怎么办:Selenium 渲染与接口逆向两条路

4.1 怎么判断一个页面是不是动态加载

有些网站打开网页时,数据是空的,页面上会转圈,等几秒后内容才出现。这类页面通常不是服务端直接返回完整 HTML,而是页面里的 JavaScript 脚本在加载完后,再向后端接口请求数据,然后把数据渲染到页面上。

用 requests 去请求这类页面,拿到的 HTML 里只有无关紧要的框架,没有真正的数据。

判断方法很简单:在浏览器里右键"查看网页源代码",如果源代码里找不到你在页面上看到的关键数据,而页面渲染后却能看到,那基本就是动态加载。另一个更专业的办法是打开开发者工具 Network 面板,刷新页面,筛选 XHR 或 Fetch 请求,观察有没有返回 JSON 数据的接口。

我见过太多新手对着一个动态页面猛改解析代码,折腾半天没有任何进展。记住:先定位数据到底在不在初始 HTML 里,再决定用什么策略。

4.2 方案一:Selenium 模拟浏览器

Selenium 是自动化测试工具,它会真正启动一个浏览器(如 Chrome),执行页面里的 JavaScript,渲染完成后你可以直接读取页面内容。这样,动态加载的问题从根源上消失了,因为浏览器帮你把"渲染"这件事做完了。

安装和基础使用:

pip install selenium

还需要一个 ChromeDriver,让它与浏览器版本匹配。启动和应用的示例:

from selenium import webdriver from selenium.webdriver.common.by import By import time driver = webdriver.Chrome() driver.get("https://example.com/dynamic-page") time.sleep(3) # 等待页面渲染,也可以用显式等待 items = driver.find_elements(By.CSS_SELECTOR, ".item") for item in items: print(item.text) driver.quit()

显式等待比time.sleep(3)更优雅,它能等到某个条件满足再继续:

from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC wait = WebDriverWait(driver, 10) element = wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, ".item")))

Selenium 的优点是快、简单,不用理解前端逻辑;缺点是慢、资源占用高,一个浏览器实例几百 MB 内存是常事,大规模抓取时并不划算。

4.3 方案二:抓接口——更高效的逆向思路

如果你打开 Network 面板,发现页面加载后发起了几个 XHR 请求,返回的是干净的 JSON 数据,那恭喜你,最优解出现了:直接模拟那个接口请求,就能拿到数据,完全不需要渲染页面。

做法是:

  1. 在 Network 面板里找到返回 JSON 的那个请求;
  2. 记录它的完整 URL、请求方法(GET/POST)、请求头和请求体;
  3. 用 requests 模拟它,解析 JSON 数据。

拿带翻页的接口来说,通常长这样:

import requests ajax_url = "https://example.com/api/list" headers = { "User-Agent": "Mozilla/5.0 ...", "Referer": "https://example.com/list", } params = { "page": 1, "size": 20, } resp = requests.get(ajax_url, headers=headers, params=params, timeout=10) data = resp.json() # 直接得到字典 for item in data["data"]["list"]: print(item["name"], item["price"])

这种方式比 Selenium 快一个数量级,也是我平时最推荐优先尝试的方案。

4.4 两种方案的取舍建议

简单总结一下我的决策流程:

  • 先刷新 Network 面板,查 XHR 请求,看看有没有现成的 JSON 接口。有,走接口方案;
  • 没有接口,或者接口加密严重、签名逻辑复杂,走浏览器渲染方案;
  • 需要操作页面(点击、输入、滚动加载)才能触发数据,也考虑浏览器方案。

在接口方案中,偶尔会遇到参数被加密的情况,这时需要去读前端 JavaScript 代码,理解加密逻辑。这是进阶内容,入门阶段不必强求。但记住一个大原则:优先用开发者工具去观察,而不是闭着眼睛写代码。观察网络请求的能力,才是爬虫技术中真正值钱的核心能力。

5. 规模化抓取:Scrapy 框架的核心结构与调度机制

5.1 为什么用框架:requests 脚本的痛点

requests 脚本写多了,会发现几个问题:并发请求要自己管理线程;异常重试逻辑要自己写;解析、去重、存储逻辑混在同一个脚本里,改一处崩三处;爬取中途断了,不知道哪些页面已经爬过。

Scrapy 正是为了解决这些问题而生的。它是一个异步网络框架,内置了调度器、下载器、爬虫、管道、中间件等模块,你只需要关注两件事:定义爬虫规则(Spider)和定义数据管道(Pipeline)。它天然支持高并发,内置去重,代码结构清晰,是 Python 爬虫最主流的工程化方案。

5.2 Scrapy 项目结构解读

先创建项目:

pip install scrapy scrapy startproject tutorial cd tutorial

生成的项目结构大致如下:

tutorial/ ├── scrapy.cfg └── tutorial/ ├── __init__.py ├── items.py # 定义数据结构 ├── middlewares.py # 中间件:处理请求/响应钩子 ├── pipelines.py # 管道:存储数据 ├── settings.py # 配置文件 └── spiders/ # 爬虫代码目录 └── __init__.py

新手的理解思路是这样的:

  • spiders/是核心,写爬虫逻辑;
  • items.py定义你希望输出的数据结构,类似"字段清单";
  • pipelines.py处理数据的清洗与入库;
  • settings.py里配置并发数、下载延迟、是否遵守 robots 协议等。

5.3 一个完整 Spider 的写法

以同一个名言网站为例,在 spiders 目录下新建quotes_spider.py

import scrapy class QuotesSpider(scrapy.Spider): name = "quotes" start_urls = ["http://quotes.toscrape.com/page/1/"] def parse(self, response): for quote in response.css("div.quote"): yield { "text": quote.css("span.text::text").get(), "author": quote.css("small.author::text").get(), } next_page = response.css("li.next a::attr(href)").get() if next_page: yield response.follow(next_page, callback=self.parse)

这段代码比 requests 版本精巧了很多:

  • name是爬虫的唯一标识;
  • parse是默认回调函数;
  • response.css(...)直接返回选择器对象,::text表示取文本,::attr(href)表示取属性;
  • 翻页通过response.follow构造下一个请求,Scrapy 会自动处理 URL 拼接;
  • yield出去的数据,Scrapy 会自动交给 Pipeline 处理。

运行:

scrapy crawl quotes -o quotes.json

-o参数直接导出 JSON 文件,非常方便。我还会配合-L WARNING减少日志输出,看得更清爽。

5.4 设置下载延迟和并发:做一个礼貌的爬虫

Scrapy 的高并发能力很强,但"能跑多快"不等于"应该跑多快"。在settings.py里,我最常调整的参数是这几个:

ROBOTSTXT_OBEY = True # 遵守 robots.txt CONCURRENT_REQUESTS = 8 # 并发请求数 DOWNLOAD_DELAY = 1.0 # 每个请求之间的间隔(秒) COOKIES_ENABLED = False # 没有登录需求时保持关闭 DEFAULT_REQUEST_HEADERS = { "User-Agent": "Mozilla/5.0 ...", }

ROBOTSTXT_OBEY = True是 Scrapy 默认开启的,它会读取目标站点的 robots.txt 文件,并遵守其中的爬虫访问规则。有些站点明确禁止爬虫访问某些目录,这个设置会帮你自动避开。

关于并发数和下载延迟的经验:先保守,CONCURRENT_REQUESTS = 4DOWNLOAD_DELAY = 2起步,观察目标服务器的反应,再逐步调整。不要一上来就把并发调到 32,那是对服务器极大的不尊重。

如果你之后真的要大规模抓取,Scrapy 可以和 Redis 结合变成分布式爬虫,多个节点共享请求队列。这是进阶方向,但理解单机版 Scrapy 的调度机制后,分布式其实只是把调度器挪到了共享队列上,核心逻辑完全一样。

6. 数据落地:从 CSV 到 MySQL 的存储方案

6.1 轻量方案:CSV 和 JSON

数据量小、只做一次性分析时,CSV 和 JSON 是最省事的存储方式。CSV 可以直接用 Excel 打开,JSON 方便程序读取。

用 Python 标准库写 CSV 非常简洁:

import csv rows = [ {"author": "Albert Einstein", "text": "The world is a dangerous place."}, {"author": "Mark Twain", "text": "Never regret anything."}, ] with open("quotes.csv", "w", newline="", encoding="utf-8-sig") as f: writer = csv.DictWriter(f, fieldnames=["author", "text"]) writer.writeheader() writer.writerows(rows)

encoding="utf-8-sig"是为了让 Excel 打开时中文不乱码。这个小细节是我第一次交付数据文件时被同事提醒的,后来每次写 CSV 都带着。

如果数据量达到几十万条,CSV 就开始力不从心了:打开慢、筛选慢、无法并发读写。这时候就该上数据库。

6.2 结构化存储:SQLite

SQLite 是 Python 自带支持的嵌入式数据库,不需要安装服务,一个文件就是一个库,非常适合爬虫的中间存储。SQL 查询能力完整,性能也比 CSV 好得多。

import sqlite3 conn = sqlite3.connect("quotes.db") conn.execute(""" CREATE TABLE IF NOT EXISTS quotes ( id INTEGER PRIMARY KEY AUTOINCREMENT, author TEXT, text TEXT UNIQUE ) """) data = [("Albert Einstein", "The world is..."), ("Mark Twain", "Never regret...")] conn.executemany("INSERT OR IGNORE INTO quotes (author, text) VALUES (?, ?)", data) conn.commit() conn.close()

注意两个细节:

  • text TEXT UNIQUE给文本加了唯一约束,配合INSERT OR IGNORE可以天然去重,反复运行爬虫不会产生重复数据;
  • executemany批量插入,比一条条execute快几十倍。

SQLite 适合几百万条以内的数据量。你的数据超过这个级别,或者需要跟业务系统对接,再考虑上正式的数据库服务。

6.3 生产环境:MySQL 的选择

MySQL 是生产环境最常见的选择。用pymysql连接:

pip install pymysql
import pymysql conn = pymysql.connect( host="127.0.0.1", user="root", password="your_password", database="crawler_db", charset="utf8mb4", ) cursor = conn.cursor() cursor.execute(""" INSERT INTO quotes (author, text) VALUES (%s, %s) """, ("Albert Einstein", "The world is...")) conn.commit() cursor.close() conn.close()

charset="utf8mb4"必须写上,否则遇到某些特殊字符(比如 emoji)会写入失败。这是我从"乱码报错半小时"里总结出来的教训。

在 Scrapy 中,把数据写入 MySQL 的位置在pipelines.py。典型做法:

class MysqlPipeline: def open_spider(self, spider): self.conn = pymysql.connect(...) def process_item(self, item, spider): self.cursor.execute( "INSERT INTO quotes (author, text) VALUES (%s, %s)", (item["author"], item["text"]) ) self.conn.commit() return item def close_spider(self, spider): self.conn.close()

settings.py里启用这个 Pipeline:

ITEM_PIPELINES = { "tutorial.pipelines.MysqlPipeline": 300, }

后面的数字代表执行优先级,数值越小越先执行。多个 Pipeline 可以串成一个数据加工链,比如一个用于清洗,一个用于入库。

7. 避坑与边界:反爬机制、robots 协议和合规底线

7.1 常见的反爬机制长什么样

写爬虫一定会遇到反爬,这是网站保护自身资源的手段。理解它们不是为了"攻破"谁,而是为了知道自己的访问为什么会被拒绝,从而规范自己的行为。

最常见的几类:

  • UA 校验:识别到非浏览器 UA 直接拒绝。对策是设置规范的请求头。
  • 请求频率限制:单位时间内请求次数过多,触发限流甚至封禁。对策是降低频率、增加合理间隔。
  • 登录校验:必须登录才能看到内容。对策是登录后携带 Cookie,但前提是你有合法账号。
  • 验证码:这是比较强的一层,说明服务器已经把你判定为可疑访客了。
  • JS 渲染挑战:页面动态生成访问凭证,静态请求拿到的是无效数据。

我的建议非常明确:遇到反爬,第一反应永远应该是"我是不是太急了、太像机器了",而不是"我要怎么绕过它"。把频率降下来、把请求头做规范、把抓取范围限制在公开信息,大多数情况根本不会触发反爬。这不是软弱,这是让爬虫能长期稳定工作的智慧。

7.2 怎么判断自己是不是被限制了

请求突然失败时,新手容易慌。先冷静按顺序排查:

  1. 看状态码:403、429 基本就是被限制;404 多半是 URL 问题;
  2. 看响应体:有些被限制页面返回 200,但内容是一段验证提示或跳转脚本,此时response.text里根本没有你要的数据;
  3. 看响应速度:如果服务器响应时间突然从 100 毫秒变成 10 秒,也可能是被降速了;
  4. 用浏览器手动访问同一个页面,确认页面本身没有变动。

我调试时最常用的手法是在代码里打印状态码和响应前 500 个字符,一眼就能看出返回的是什么。信息足够再下结论,不要瞎猜。

7.3 爬虫的合规底线和道德标准

这部分我觉得有必要认真写,因为我见过太多教程只教技术、不谈边界,导致新手在不知情的情况下给自己惹麻烦。几条务实的底线:

  • 遵守 robots 协议。站点通过 robots.txt 声明哪些路径允许爬虫访问,这是互联网的通行规则。Scrapy 的ROBOTSTXT_OBEY = True就是干这个的。
  • 尊重访问频率。你可以在 1 秒内发 100 个请求,但你没有理由这么做。对别人服务器造成的压力,本质上是用别人的资源换取自己的利益。
  • 只获取公开数据。需要登录、付费、授权才能看的内容,不属于"公开"范畴。
  • 不用于商业用途。即使数据是公开的,大量抓取后用于商业转售,也可能涉及侵权和反不正当竞争问题。
  • 识别个人信息。涉及个人隐私的数据,抓取和处理都有严格的法律风险,入门阶段最好完全避开。

简单的判断标准:如果一个数据在网站上有一个明确的下载按钮或者官方接口,你用那个;如果没有,那你抓取之前先问自己一句——网站的运营者会希望我这么做吗?如果答案是"不希望",那就停下来。

7.4 进阶方向:分布式爬虫与可视化工具

学完基础后,你可以根据自己的需求选择方向。

一个方向是工程化、规模化:把单机 Scrapy 升级为分布式爬虫,用 Redis 共享请求队列,用 Celery 做任务调度,用 Docker 做环境隔离。这些都是真实生产环境会用到的技术,适合打算走向专业方向的读者。分布式爬虫的难点不在代码,而在运维稳定性:节点挂了你怎么办、重复数据你怎么处理、目标站点压力你怎么控制。

另一个方向是工具化、效率化:如果你不想写代码,或者只想少量采集,可视化采集工具是很好的补充。像我之前了解过的集搜客(GooSeeker),它提供了点选式操作界面,你可以像使用 Excel 一样定义采集规则,适合快速抓取简单页面。这类工具确实解决了一部分业务人员的需求,但它的灵活性和可控性远不如自己写代码。我的看法是:先判断你的需求到底需要哪种,不要因为"大家都在学爬虫"就盲目投入。

我个人在实际项目里的体会是,爬虫最大的成本从来不是"写代码",而是"维护"。目标网站改版、接口参数变化、数据格式调整,任何一个变化都能让之前的代码失效。所以选技术方案时,永远把"好不好改"放在"跑起来多快"前面。这也是一路踩坑下来,我最想分享给你的经验。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询