㊗️本期内容已收录至专栏《Python爬虫实战》,持续完善知识体系与项目实战,建议先订阅收藏,后续查阅更方便~
㊙️本期爬虫难度指数:⭐⭐⭐
🉐福利: 一次订阅后,专栏内的所有文章可永久免费看,持续更新中,保底1000+(篇)硬核实战内容。

全文目录:
-
-
- [🌟 开篇语](#🌟 开篇语)
- [1️⃣ 摘要(Abstract)](#1️⃣ 摘要(Abstract))
- [2️⃣ 背景与需求(Why)](#2️⃣ 背景与需求(Why))
- [3️⃣ 合规与注意事项(必写)](#3️⃣ 合规与注意事项(必写))
- [4️⃣ 技术选型与整体流程(What/How)](#4️⃣ 技术选型与整体流程(What/How))
- [5️⃣ 环境准备与依赖安装](#5️⃣ 环境准备与依赖安装)
- [6️⃣ 核心实现:请求层(Fetcher)](#6️⃣ 核心实现:请求层(Fetcher))
- [7️⃣ 核心实现:解析层(Parser)](#7️⃣ 核心实现:解析层(Parser))
- [8️⃣ 数据存储与导出(Storage)](#8️⃣ 数据存储与导出(Storage))
- [9️⃣ 运行方式与结果展示(必写)](#9️⃣ 运行方式与结果展示(必写))
- [🔟 常见问题与排错(建议收藏)](#🔟 常见问题与排错(建议收藏))
- [1️⃣1️⃣ 进阶优化(可选但加分)](#1️⃣1️⃣ 进阶优化(可选但加分))
- [1️⃣2️⃣ 总结与延伸阅读](#1️⃣2️⃣ 总结与延伸阅读)
- [🌟 文末](#🌟 文末)
-
- [✅ 专栏持续更新中|建议收藏 + 订阅](#✅ 专栏持续更新中|建议收藏 + 订阅)
- [✅ 互动征集](#✅ 互动征集)
- [✅ 免责声明](#✅ 免责声明)
-
🌟 开篇语
哈喽,各位小伙伴们你们好呀~我是【喵手】。
运营社区: C站 / 掘金 / 腾讯云 / 阿里云 / 华为云 / 51CTO
欢迎大家常来逛逛,一起学习,一起进步~🌟
我长期专注 Python 爬虫工程化实战 ,主理专栏 《Python爬虫实战》:从采集策略 到反爬对抗 ,从数据清洗 到分布式调度 ,持续输出可复用的方法论与可落地案例。内容主打一个"能跑、能用、能扩展 ",让数据价值真正做到------抓得到、洗得净、用得上。
📌 专栏食用指南(建议收藏)
- ✅ 入门基础:环境搭建 / 请求与解析 / 数据落库
- ✅ 进阶提升:登录鉴权 / 动态渲染 / 反爬对抗
- ✅ 工程实战:异步并发 / 分布式调度 / 监控与容错
- ✅ 项目落地:数据治理 / 可视化分析 / 场景化应用
📣 专栏推广时间 :如果你想系统学爬虫,而不是碎片化东拼西凑,欢迎订阅专栏👉《Python爬虫实战》👈,一次订阅后,专栏内的所有文章可永久免费阅读,持续更新中。
💕订阅后更新会优先推送,按目录学习更高效💯~
1️⃣ 摘要(Abstract)
本文将带你手把手构建一个稳健的单线程爬虫,目标是攻克 books.toscrape.com 这一电商沙盒站点。我们将抛弃花里胡哨的框架,回归最扎实的 requests + BeautifulSoup 组合,实现从列表页到详情页的数据穿透。
读完本文,你将获得:
- 一套可以直接复用于 80% 静态网站的**"生产者-消费者"**式代码模板。
- 关于如何优雅处理 404、网络超时及字段缺失的容错机制。
- 一份格式完美、无乱码的 CSV 数据交付物。
2️⃣ 背景与需求(Why)
为什么要爬?
假设我们是一名书店经营者,我们需要分析市场上的图书定价策略、库存情况以及评分分布。手动复制粘贴 1000 本书的数据简直是噩梦,自动化采集是唯一出路。
目标站点 :http://books.toscrape.com/
目标字段清单:
- Book Name (书名)
- Price (价格,需清洗掉货币符号)
- Rating (评分,星级转换)
- Availability (库存状态)
- UPC (产品唯一编码)
3️⃣ 合规与注意事项(必写)
这里必须严肃三秒钟 😐。作为一名有职业操守的爬虫工程师,我们要遵守"互联网礼仪":
- Robots.txt :虽然靶场允许所有爬取,但在实战中,请务必先访问
domain/robots.txt查看站长设定的禁区。 - 频率控制 :不要做服务器的"DDoS 攻击者"! 我们在代码中会强制加入
time.sleep,让爬虫像人类一样有阅读间隙。 - 红线原则:绝对不采集个人隐私数据(手机号、身份证),绝对不尝试绕过登录撞库,涉及付费内容请老实付费。
4️⃣ 技术选型与整体流程(What/How)
技术选型:
这次的目标是静态网页(HTML 源码里就有数据),所以:
- ❌ Selenium/Playwright:太重了,像用牛刀杀鸡,速度慢。
- ✅ Requests + BeautifulSoup (bs4):轻量、极速、精准。这是处理静态 HTML 的黄金搭档。
整体流程图:
text
[入口 URL]
↓
[Fetcher: 获取列表页 HTML]
↓
[Parser: 解析提取书籍详情 URL]
↓
[Fetcher: 遍历请求详情页] → (加入随机延时)
↓
[Parser: 清洗提取字段 (Price/Rating/UPC)]
↓
[Storage: 写入 CSV 文件]
5️⃣ 环境准备与依赖安装
确保你的 Python 版本 >= 3.8(人生苦短,我用 Python 3)。
项目结构推荐:
text
book_spider_pro/
├── main.py # 入口文件
├── data/ # 数据存储目录
│ └── books_data.csv
└── requirements.txt # 依赖表
安装依赖 :
打开终端,执行以下命令(记得用国内源加速哦~):
bash
pip install requests beautifulsoup4
6️⃣ 核心实现:请求层(Fetcher)
这一层是爬虫的"嘴",必须要结实,能吃得下各种网络异常。
python
import requests
from requests.adapters import HTTPAdapter
from requests.packages.urllib3.util.retry import Retry
import random
import time
def get_html(url):
"""
稳健的网页抓取函数,带重试机制和伪装
"""
# 伪装成浏览器,这是最基本的礼貌
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36',
'Referer': 'http://books.toscrape.com/'
}
# 建立会话,复用 TCP 连接
session = requests.Session()
# 设置重试策略:如果遇到 500, 502, 504 错误,重试 3 次
retry = Retry(total=3, backoff_factor=1, status_forcelist=[500, 502, 503, 504])
adapter = HTTPAdapter(max_retries=retry)
session.mount('http://', adapter)
session.mount('https://', adapter)
try:
# 随机休眠 1-2 秒,模拟人类阅读
time.sleep(random.uniform(1, 2))
response = session.get(url, headers=headers, timeout=10)
response.raise_for_status() # 如果状态码不是 200,直接抛异常
# 处理编码,防止乱码(requests 自动推测有时不准)
response.encoding = response.apparent_encoding
return response.text
except Exception as e:
print(f"💥 请求失败: {url}, 错误信息: {e}")
return None
7️⃣ 核心实现:解析层(Parser)
这一层是"胃",负责消化 HTML 并吸收营养。我们要处理详情页提取 和容错。
python
from bs4 import BeautifulSoup
import re
def parse_book_detail(html_content):
"""
解析书籍详情页,返回字典
"""
if not html_content:
return None
soup = BeautifulSoup(html_content, 'html.parser')
item = {}
try:
# 1. 提取书名 (H1 标签)
item['title'] = soup.select_one('div.product_main h1').text.strip()
# 2. 提取价格 (清洗掉 £ 符号)
price_raw = soup.select_one('p.price_color').text
item['price'] = float(price_raw.replace('£', '').replace('Â', '')) # 这里的Â是常见的编码脏字符
# 3. 提取评分 (CSS 类名转换数字)
# HTML 示例: <p class="star-rating Three">
star_tag = soup.select_one('p.star-rating')
star_map = {'One': 1, 'Two': 2, 'Three': 3, 'Four': 4, 'Five': 5}
# 获取 class 列表中的第二个元素 (即 'Three')
rating_class = [c for c in star_tag.get('class') if c != 'star-rating'][0]
item['rating'] = star_map.get(rating_class, 0)
# 4. 提取库存 (文本处理)
# 示例: "In stock (19 available)"
availability = soup.select_one('p.instock.availability').text.strip()
item['stock'] = re.search(r'\d+', availability).group() if re.search(r'\d+', availability) else 0
# 5. 提取 UPC (在表格里)
item['upc'] = soup.select_one('table.table-striped > tr:nth-of-type(1) > td').text
except AttributeError as e:
# 容错处理:如果某个字段找不到,不要崩,记录日志并跳过
print(f"⚠️ 解析字段缺失: {e}")
return None
return item
8️⃣ 数据存储与导出(Storage)
不做花哨的数据库,先用 CSV 把数据落袋为安。
python
import csv
import os
def save_to_csv(data_list, filename="books_data.csv"):
"""
将字典列表写入 CSV
"""
if not data_list:
return
# 确保 data 目录存在
os.makedirs("data", exist_ok=True)
file_path = os.path.join("data", filename)
headers = ['title', 'price', 'rating', 'stock', 'upc']
# newline='' 是为了防止 Windows 下出现空行
with open(file_path, 'w', newline='', encoding='utf-8-sig') as f:
writer = csv.DictWriter(f, fieldnames=headers)
writer.writeheader()
writer.writerows(data_list)
print(f"🎉 数据已成功保存至: {file_path}, 共 {len(data_list)} 条")
9️⃣ 运行方式与结果展示(必写)
把上面的代码串起来,放在 main 块中。
python
# ... (上面定义的函数) ...
if __name__ == "__main__":
base_url = "http://books.toscrape.com/catalogue/page-{}.html"
all_books_data = []
print("🚀 爬虫启动!目标:Books to Scrape")
# 为了演示,我们只爬取前 2 页 (真实场景可以用 while 循环判断下一页)
for page in range(1, 3):
print(f"📄 正在抓取第 {page} 页列表...")
list_url = base_url.format(page)
list_html = get_html(list_url)
if list_html:
soup = BeautifulSoup(list_html, 'html.parser')
# 获取当前页所有书籍的详情链接
articles = soup.select('article.product_pod h3 a')
for link in articles:
# 拼接完整 URL
detail_url = "http://books.toscrape.com/catalogue/" + link['href']
print(f" -> 正在处理详情: {detail_url}")
detail_html = get_html(detail_url)
book_data = parse_book_detail(detail_html)
if book_data:
all_books_data.append(book_data)
# 保存数据
save_to_csv(all_books_data)
print("✨ 全部任务搞定!喝杯咖啡去吧!☕")
示例运行输出:
text
🚀 爬虫启动!目标:Books to Scrape
📄 正在抓取第 1 页列表...
-> 正在处理详情: http://books.toscrape.com/catalogue/a-light-in-the-attic_1000/index.html
-> 正在处理详情: http://books.toscrape.com/catalogue/tipping-the-velvet_999/index.html
...
🎉 数据已成功保存至: data/books_data.csv, 共 40 条
✨ 全部任务搞定!喝杯咖啡去吧!☕
生成的 CSV 文件内容预览:
| title | price | rating | stock | upc |
|---|---|---|---|---|
| A Light in the Attic | 51.77 | 3 | 22 | a897fe39b1053632 |
| Tipping the Velvet | 53.74 | 1 | 20 | 90fa61229261140a |
| Soumission | 50.10 | 1 | 20 | 6957f44c3847a760 |
🔟 常见问题与排错(建议收藏)
-
HTML 抓到空壳?
- 现象 :返回状态码 200,但
soup.select什么都找不到。 - 原因:网站可能是 Vue/React 动态渲染的。
- 解法 :这时候 requests 不管用了,要上 Selenium 或者直接并在 F12 的 Network 选项卡里找 API 接口(JSON 格式)。
- 现象 :返回状态码 200,但
-
403 Forbidden / 429 Too Many Requests?
- 原因:你爬太快了,或者 User-Agent 还是 Python 默认的。
- 解法 :加长
time.sleep,或者购买代理 IP 池(Proxy Pool),在 requests 中添加proxies参数。
-
解析报错
AttributeError: 'NoneType' object has no attribute 'text'- 原因:选择器没定位到元素(页面结构变了,或者该商品确实没数据)。
- 解法 :一定要像我在
parse_book_detail里那样用try-except包裹,或者用if element:先判断再取值。
1️⃣1️⃣ 进阶优化(可选但加分)
如果你想让你的爬虫更"专业",可以尝试:
- 多线程并发 :使用
concurrent.futures.ThreadPoolExecutor,将串行爬取改为并行,速度能提升 5-10 倍(但要注意频率控制!)。 - 断点续跑:在数据库或 Redis 中记录已经爬过的 URL 的指纹(MD5),下次启动时先比对,跳过已爬取的链接。
- 日志监控 :使用 Python 的
logging模块替代print,将报错信息写入spider.log,方便夜间无人值守运行时排错。
1️⃣2️⃣ 总结与延伸阅读
今天我们用不到 100 行代码就攻克了一个标准的电商类网站。复盘一下,我们完成了从请求伪装 、DOM 解析 到数据持久化的全过程。
下一步你可以挑战什么?
- Scrapy 框架:如果你要爬取 10 万+ 的数据,手写 requests 会很累,Scrapy 是工业级的选择。
- JS 逆向:去挑战一下那些加密了参数的接口,那才是爬虫工程师的"掉发之源"。👴
加油!代码在手,数据我有!如果在运行过程中遇到任何报错,随时把错误截图发给我,我来帮你 Debug!
🌟 文末
好啦~以上就是本期的全部内容啦!如果你在实践过程中遇到任何疑问,欢迎在评论区留言交流,我看到都会尽量回复~咱们下期见!
小伙伴们在批阅的过程中,如果觉得文章不错,欢迎点赞、收藏、关注哦~
三连就是对我写作道路上最好的鼓励与支持! ❤️🔥
✅ 专栏持续更新中|建议收藏 + 订阅
墙裂推荐订阅专栏 👉 《Python爬虫实战》,本专栏秉承着以"入门 → 进阶 → 工程化 → 项目落地"的路线持续更新,争取让每一期内容都做到:
✅ 讲得清楚(原理)|✅ 跑得起来(代码)|✅ 用得上(场景)|✅ 扛得住(工程化)
📣 想系统提升的小伙伴 :强烈建议先订阅专栏 《Python爬虫实战》,再按目录大纲顺序学习,效率十倍上升~

✅ 互动征集
想让我把【某站点/某反爬/某验证码/某分布式方案】等写成某期实战?
评论区留言告诉我你的需求,我会优先安排实现(更新)哒~
⭐️ 若喜欢我,就请关注我叭~(更新不迷路)
⭐️ 若对你有用,就请点赞支持一下叭~(给我一点点动力)
⭐️ 若有疑问,就请评论留言告诉我叭~(我会补坑 & 更新迭代)
✅ 免责声明
本文爬虫思路、相关技术和代码仅用于学习参考,对阅读本文后的进行爬虫行为的用户本作者不承担任何法律责任。
使用或者参考本项目即表示您已阅读并同意以下条款:
- 合法使用: 不得将本项目用于任何违法、违规或侵犯他人权益的行为,包括但不限于网络攻击、诈骗、绕过身份验证、未经授权的数据抓取等。
- 风险自负: 任何因使用本项目而产生的法律责任、技术风险或经济损失,由使用者自行承担,项目作者不承担任何形式的责任。
- 禁止滥用: 不得将本项目用于违法牟利、黑产活动或其他不当商业用途。
- 使用或者参考本项目即视为同意上述条款,即 "谁使用,谁负责" 。如不同意,请立即停止使用并删除本项目。!!!
