📌 适合人群:完全没有爬虫经验的初学者 🛠️ 涉及工具:Python 3、requests、BeautifulSoup4、浏览器开发者工具 🎯 学习目标:看懂网页本质 → 发送请求 → 解析数据 → 批量爬取
一、爬虫到底是什么?先看大图景
在写任何代码之前,先建立整体认知。所谓网络爬虫,本质上是模拟浏览器向服务器发送请求,获取网页内容,然后从中提取我们想要的数据。
整个流程可以拆成三步(这也是几乎所有爬虫的灵魂框架):
plain
① 获取网页内容 —— 向服务器要数据,服务器把网页"源代码"返回给我们 ↓ ② 解析网页内容 —— 从一大堆 HTML 代码里,精准找到想要的数据 ↓ ③ 存储 / 分析数据 —— 存成表格(Excel/CSV)、做数据分析、画可视化图表对应到你的学习笔记,就是:获取 → 解析 → 存储分析。
二、前置知识一:HTTP协议(爬虫的地基)
2.1 什么是HTTP
HTTP(Hypertext Transfer Protocol,超文本传输协议)是客户端(浏览器)和服务器之间的请求-响应协议。
你每打开一个网页,背后都在发生一次对话:
浏览器:"服务器你好,我要
/movie/top250这个页面的内容" 服务器:"好的,给你(返回网页代码)"
爬虫的第一步,就是用代码代替浏览器去完成这个对话。
2.2 HTTP请求的结构
一个HTTP请求由三部分组成:
(1)请求行
POST /user/info?new_user=true HTTP/1.1POST:请求方法(常见的还有GET)/user/info?new_user=true:资源路径 + 查询参数HTTP/1.1:协议版本(还有 0.9、1.0、2.0 等)
(2)请求头(Headers)
Host: www.example.com User-Agent: curl/7.77.0 Accept: */*请求头是关于请求的附加信息。其中User-Agent(用户代理)告诉服务器"我是谁"。
⚠️关键点来了:如果不加User-Agent,服务器看到的就是一个"裸奔的程序",很可能直接拒绝你(比如豆瓣会返回418 错误)。解决办法是伪装成浏览器:
headers = { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64)" }(3)请求体(Body)
POST请求通常带有数据(比如登录时提交账号密码),格式常见为 JSON:
{ "username": "zhangsan", "email": "zhangsan@example.com" }2.3 GET 和 POST 的区别(面试高频)
表格
| 对比项 | GET | POST |
|---|---|---|
| 用途 | 获取数据 | 创建/提交数据 |
| 场景 | 浏览网页、翻页 | 提交账号、注册表单 |
| 参数位置 | 拼在URL里(?key=value) | 放在请求体里 |
| 爬虫常用度 | ⭐⭐⭐⭐⭐ | ⭐⭐ |
plain
# GET请求:参数直接写在URL上 www.douban.com/movie/top250?start=75&filter=unwatched └─────┬─────┘ 问号后面就是查询参数2.4 HTTP响应的结构
服务器返回的响应同样由三部分组成:
HTTP/1.1 200 OK ← 状态行 Date: Fri, 27 Jan 2023 02:10:48 GMT Content-Type: text/html; charset=utf-8 ← 响应头 <!DOCTYPE html> <html> <head><title>首页</title></head> <body><h1>Hello World</h1></body> </html> ← 响应体(真正的网页内容)状态行:协议版本 + 状态码 + 状态消息
响应头:比如
Content-Type告诉我们返回内容的格式是text/html响应体:就是我们要爬的 HTML 源码
2.5 常见状态码(务必背下来)
| 状态码 | 含义 |
|---|---|
| ✅ 200 OK | 请求成功,最理想 |
| ↪️ 301 Moved Permanently | 资源永久移动到新地址 |
| ⚠️ 400 Bad Request | 客户端请求不能被服务器理解 |
| ⚠️ 401 Unauthorized | 请求未经授权 |
| ⚠️ 403 Forbidden | 服务器拒绝提供服务(反爬常见) |
| ⚠️ 404 Not Found | 请求的资源不存在 |
| ❌ 500 Internal Server Error | 服务器内部错误 |
| ❌ 503 Server Unavailable | 服务器当前无法处理请求 |
在代码中用response.status_code查看,用response.ok快速判断是否成功(2xx 返回 True)。
三、前置知识二:HTML网页结构(解析的地图)
3.1 网页三大件
| 技术 | 作用 |
|---|---|
| HTML | 定义网页的结构和信息(骨架) |
| CSS | 定义网页的样式(皮肤) |
| JavaScript | 定义网页的行为和交互逻辑(肌肉) |
爬虫主要和 HTML 打交道。
3.2 HTML的基本结构
<html> <body> <h1>这是一个标题</h1> <p>这是一个段落</p> </body> </html>HTML 由一对一对的标签(tag)组成:<标签名>开始,</标签名>结束。<h1>和<p>是兄弟标签(同级关系)。
3.3 常用标签速查
| 标签 | 含义 |
|---|---|
<h1>~<h6> | 标题(1最大,6最小) |
<p> | 段落 |
<br> | 换行 |
<i> | 斜体 |
<u> | 下划线 |
<a> | 链接 |
<div> | 区块容器(最常用,页面布局) |
<ul>/<ol>/<li> | 无序列表 / 有序列表 / 列表项 |
<span> | 行内容器(常用来包裹小片段文字) |
<b>/<strong> | 加粗 |
<table>/<tr>/<td> | 表格 / 表格行(table row)/ 单元格 |
<thead>/<tbody> | 表头区 / 表体区 |
以表格为例,完整结构长这样:
<table> <thead> <tr> <td>表头1</td> <td>表头2</td> </tr> </thead> <tbody> <tr> <td>111</td> <td>222</td> </tr> <tr> <td>333</td> <td>444</td> </tr> </tbody> </table>💡爬虫提数据的核心思路:数据总是藏在某个标签里,我们要做的就是在 HTML 中找到那个标签,把它"抠"出来。
四、实战第一步:requests库获取网页内容
4.1 安装与导入
pip install requestsimport requests4.2 第一个爬虫:获取books.toscrape.com首页
import requests response = requests.get("http://books.toscrape.com/") if response.ok: print(response.text) else: print("请求失败")到这一步,你就已经完成了爬虫流程里的第一步——拿到了网页的源代码(一大串HTML文本)。
4.3 response对象的常用属性
response = requests.get("http://books.toscrape.com/") print(response) # 打印响应对象本身 print(response.status_code) # 状态码,200表示成功 print(response.text) # 响应体内容(字符串形式) print(response.headers) # 响应头 print(response.ok) # 是否成功(2xx → True)4.4 反爬入门:伪装User-Agent
直接爬豆瓣 Top250 会遇到 418("我是茶壶"),说明服务器识破了你:
import requests response = requests.get("https://movie.douban.com/top250") print(response) # <Response [418]> 被识破了!加 headers 伪装成浏览器:
import requests headers = { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64)" } response = requests.get("https://movie.douban.com/top250", headers=headers) print(response.status_code) # 200,成功! print(response.text)🔑 记忆口诀:先不加 headers 试试,不行就加 User-Agent;还不行就检查 Cookie、Referer 等。
五、实战第二步:BeautifulSoup解析网页内容
获取到的response.text是一大坨字符串,肉眼找数据是不可能的。我们用BeautifulSoup把它变成一个结构化、可查询的对象。
5.1 安装与导入
pip install beautifulsoup4from bs4 import BeautifulSoup import requests5.2 三步走:请求 → 做汤 → 找料
from bs4 import BeautifulSoup import requests # 1. 请求网页 content = requests.get("http://books.toscrape.com/").text # 2. 把字符串"熬成汤"(解析成结构化对象) soup = BeautifulSoup(content, "html.parser")经过BeautifulSoup处理后,得到的soup对象就拥有了非常多的方法和属性,可以像查字典一样查找标签。
5.3 findAll():批量查找标签
# 按标签名查找:找到所有 <p> 标签 all_prices = soup.findAll("p", attrs={"class": "price_color"}) for price in all_prices: print(price.string[2:])运行结果:
51.77 53.74 50.10 47.82 54.23 ...逐行拆解这个方法:
soup.findAll("p", attrs={"class": "price_color"}):找到所有class 为price_color的<p>标签,返回一个列表price.string:取标签里的文本内容(如£51.77)[2:]:字符串切片,去掉前2个字符£,留下纯数字
🔑 注意区分:
find()→ 找第一个,返回单个对象
findAll()→ 找所有,返回列表
5.4 案例:提取书名
在 books.toscrape 上,书名藏在<h3>标签里的<a>标签中:
<h3> <a href="catalogue/a-light-in-the-attic_1000/index.html" title="A Light in the Attic">A Light in the ...</a> </h3>对应代码:
all_titles = soup.findAll("h3") for title in all_titles: all_links = title.findAll("a") for link in all_links: print(link.string) # 通过string属性拿到标签内的文本输出:
A Light in the ... Tipping the Velvet Soumission Sharp Objects Sapiens: A Brief History ... ...💡嵌套查找是很常用的技巧:先找大容器(h3),再在容器内部继续找小标签(a)。
5.5 案例:爬豆瓣Top250电影名
先在浏览器里右键 → 检查(审查元素),找到标题对应的标签:
<span class="title">三傻大闹宝莱坞</span>对应代码:
import requests from bs4 import BeautifulSoup headers = { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64)" } response = requests.get("https://movie.douban.com/top250", headers=headers) html = response.text soup = BeautifulSoup(html, "html.parser") all_titles = soup.findAll("span", attrs={"class": "title"}) for title in all_titles: title_string = title.string # 豆瓣每部电影有中文名和英文名两个title,用"/"判断过滤 if "/" not in title_string: print(title_string)这里用了一个细节技巧:if "/" not in title_string——因为每部电影的中文名和英文名都是class="title",通过是否包含/来只保留中文名。
六、实战第三步:翻页爬取全部250部电影
豆瓣Top250每页只有25部,URL规律是:
第1页: https://movie.douban.com/top250?start=0 第2页: https://movie.douban.com/top250?start=25 第3页: https://movie.douban.com/top250?start=50 ...规律是start每次加 25。用range(0, 250, 25)生成翻页参数:
for start_num in range(0, 250, 25): print(start_num) # 输出: 0, 25, 50, 75, 100, 125, 150, 175, 200, 225完整代码:
import requests from bs4 import BeautifulSoup headers = { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64)" } for start_num in range(0, 250, 25): response = requests.get( f"https://movie.douban.com/top250?start={start_num}", headers=headers ) html = response.text soup = BeautifulSoup(html, "html.parser") all_titles = soup.findAll("span", attrs={"class": "title"}) for title in all_titles: title_string = title.string if "/" not in title_string: print(title_string)🛡️善意提醒:实际爬取时建议在循环中加入
time.sleep(1~3)降低请求频率,避免对目标网站造成压力。
七、爬虫法律与道德红线(重要!)
笔记里总结的三条底线,必须刻进DNA:
不要爬取公民隐私数据
要尊重版权保护的内容
国家事务、国防建设相关网站绝对不碰
技术层面的注意点:
⚡请求频率不能过高,否则无异于DDoS 攻击(通过给服务器发送海量高频请求,让网站资源被耗尽,无法服务其他正常用户)
🚧网站如果做出明显的反爬限制(如有些内容登录后可看),尊重限制机制,不要强行突破
📄 爬取前查看网站的
robots.txt文件(如www.example.com/robots.txt),它声明了允许/禁止爬取的网页路径范围
八、完整知识图谱(复习用)
┌─────────────────────────────────────────┐ │ 爬虫三步走 │ ├──────────────┬──────────────────────────┤ │ ① 获取网页内容 │ requests库 │ │ │ · requests.get(网址) │ │ │ · response.status_code │ │ │ · response.text / .ok │ │ │ · headers伪装User-Agent │ ├──────────────┼──────────────────────────┤ │ ② 解析网页内容 │ BeautifulSoup库 │ │ │ · BeautifulSoup(text, │ │ │ "html.parser") │ │ │ · find() / findAll() │ │ │ · attrs={"class": "..."} │ │ │ · .string 取文本 │ │ │ · 切片 [2:] 清洗数据 │ ├──────────────┼──────────────────────────┤ │ ③ 存储/分析 │ 存CSV/Excel → 数据分析 │ │ │ → 可视化图表 │ └──────────────┴──────────────────────────┘ 底层支撑: HTTP协议(请求行/请求头/请求体;状态行/响应头/响应体) HTML结构(标签、class属性、层级与兄弟关系)最小可运行模板(背下来,90%的简单爬虫都是它的变体):
import requests from bs4 import BeautifulSoup headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64)"} url = "目标网址" response = requests.get(url, headers=headers) if response.ok: soup = BeautifulSoup(response.text, "html.parser") items = soup.findAll("标签名", attrs={"class": "目标class"}) for item in items: print(item.string) else: print("请求失败:", response.status_code)九、常见问题速查(FAQ)
Q1:为什么返回418 / 403?服务器识别出你是爬虫。加上headers伪装 User-Agent,必要时补充 Cookie、Referer。
Q2:为什么findAll返回空列表?检查 class 名是否写对(浏览器里右键检查确认);注意有的class有多个值,要用完整字符串匹配。
Q3:price.string报错 NoneType?说明该标签内嵌套了子标签,string无法确定取哪个。改用.get_text()。
Q4:中文乱码?尝试response.encoding = response.apparent_encoding后再取.text。
十、写在最后
零基础学爬虫,其实就两条主线:
网络线:理解HTTP请求-响应 → 用requests发请求解析线:理解HTML标签结构 → 用BeautifulSoup查标签
把这两条线打通,再加一个for循环实现翻页,你就已经具备了爬取绝大多数静态网页的能力。下一步可以进阶学习:数据存储(CSV/Excel/MySQL)、XPath、正则表达式、Selenium动态页面爬取、Scrapy框架。
如果觉得本文对你有帮助,欢迎点赞收藏,复习时直接对照第八节的图谱过一遍即可。评论区欢迎交流爬取中遇到的问题,下期预告:《Python爬虫进阶:数据存储与清洗实战》。