㊙️本期内容已收录至专栏《Python爬虫实战》,持续完善知识体系与项目实战,建议先订阅收藏,后续查阅更方便~持续更新中!🔥🔥 ㊗️爬虫难度指数:⭐ 🚫声明:数据仅供个人学习数据分析使用,严禁用于商业比价系统或倒卖数据等,一切后果皆由使用者本人承担。公开榜单数据一般允许访问,但请务必遵守“君子协议”。
全文目录:
-
-
- 🌟 开篇语
- 1️⃣ 标题 && 摘要(Abstract)
- 2️⃣ 背景与需求(Why)
- 3️⃣ 合规与注意事项(必写)
- 4️⃣ 技术选型与整体流程(What/How)
- 5️⃣ 环境准备与依赖安装(可复现)
- 6️⃣ 核心实现:请求层(Fetcher)—— ⚠️ 乱码重灾区
- 7️⃣ 核心实现:解析层(Parser)
- 8️⃣ 数据存储与导出(Storage)
- 9️⃣ 运行方式与结果展示(必写)
- 🔟 常见问题与排错(💡 强烈建议写)
- 1️⃣1️⃣ 进阶优化(可选但加分)
- 1️⃣2️⃣ 总结与延伸阅读
- 🌟 文末
-
- 📌 专栏持续更新中|建议收藏 + 订阅
- ✅ 互动征集
-
🌟 开篇语
哈喽,各位小伙伴们你们好呀~我是【喵手】。 运营社区: C站 / 掘金 / 腾讯云 / 阿里云 / 华为云 / 51CTO 欢迎大家常来逛逛,一起学习,一起进步~🌟
我长期专注 Python 爬虫工程化实战,主理专栏 👉 《Python爬虫实战》:从采集策略到反爬对抗,从数据清洗到分布式调度,持续输出可复用的方法论与可落地案例。内容主打一个“能跑、能用、能扩展”,让数据价值真正做到——抓得到、洗得净、用得上。
📌 专栏食用指南(建议收藏)
- ✅ 入门基础:环境搭建 / 请求与解析 / 数据落库
- ✅ 进阶提升:登录鉴权 / 动态渲染 / 反爬对抗
- ✅ 工程实战:异步并发 / 分布式调度 / 监控与容错
- ✅ 项目落地:数据治理 / 可视化分析 / 场景化应用
📣 专栏推广时间:如果你想系统学爬虫,而不是碎片化东拼西凑,欢迎订阅/关注专栏《Python爬虫实战》 订阅后更新会优先推送,按目录学习更高效~
1️⃣ 标题 && 摘要(Abstract)
项目名称:DangdangRankSpider_v1.0 核心工具:Python 3 + Requests + lxml (XPath) + Pandas 产出目标:自动化遍历当当畅销榜(共 25 页,500 本书),提取核心信息并生成 Excel/CSV 报表。
读完这篇文章,你将获得:
2️⃣ 背景与需求(Why)
为什么要爬?
畅销榜是图书市场的风向标。对于出版从业者或阅读爱好者来说,分析榜单可以知道现在大家都在看什么书?平均书价是多少?哪个出版社霸榜了?手动统计 500 本书太累,Python 脚本 30 秒就能搞定。
🎯 目标站点:http://bang.dangdang.com/books/bestsellers/01.00.00.00.00.00-recent7-0-0-1-1 (近 7 日畅销榜) 📋 目标字段清单:
- Rank (排名):1, 2, 3…
- Title (书名):去处多余的“推荐语”
- Author (作者):需要清洗掉“著”、“编”等杂质
- Price (价格):当前售价(需转为数字)
- Discount (折扣):通过原价和售价计算(可选)
3️⃣ 合规与注意事项(必写)
- Robots 协议:电商网站通常对爬虫比较敏感。虽然公开榜单数据一般允许访问,但请务必遵守“君子协议”。
- 频率控制:严禁多线程高并发!当当的反爬策略虽然不如豆瓣严格,但过快的请求依然会导致连接重置或 IP 封禁。我们依旧保持单线程 + 1秒延时。
- 用途限制:数据仅供个人学习数据分析使用,严禁用于商业比价系统或倒卖数据。
4️⃣ 技术选型与整体流程(What/How)
技术栈:
- Requests:处理 HTTP 请求。
- lxml (XPath):当当的 HTML 结构甚至比豆瓣还乱(各种嵌套),XPath 的层级定位能力(如 //li/div)在这里非常好用。
🌊 整体流程: 生成 1-25 页 URL ➔ 请求网页 (注意编码) ➔ 解析列表 (<li>标签循环) ➔ 提取字段 ➔ Pandas 汇总 ➔ Result.csv
5️⃣ 环境准备与依赖安装(可复现)
Python 版本:3.8+
依赖安装:
pip install requests lxml pandas
实际依赖安装如下:

📂 项目结构:
dangdang_spider/
├── results/
│ └── dangdang_top500.csv
├── spider.py
└── requirements.txt
6️⃣ 核心实现:请求层(Fetcher)—— ⚠️ 乱码重灾区
当当网的部分老旧页面依然沿用 GBK 或 GB2312 编码,而 requests 默认推测可能是 ISO-8859-1,如果不手动设置,爬下来的中文全是乱码。
import requests
import time
from random import uniform
def fetch_page(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://bang.dangdang.com/'
}
try:
response = requests.get(url, headers=headers, timeout=10)
# ⚠️ 关键点:当当网大部分页面是 GBK 编码
# 如果不放心,可以用 response.apparent_encoding 自动检测
# 但手动指定通常更稳
response.encoding = 'gbk'
if response.status_code == 200:
return response.text
else:
print(f"❌ 状态码异常: {response.status_code}")
return None
except Exception as e:
print(f"💥 请求出错: {e}")
return None
7️⃣ 核心实现:解析层(Parser)
这里我们需要处理一个列表://ul[@class="bang_list"]/li。每一个 li 都是一本书。
from lxml import etree
import re
def parse_html(html):
if not html:
return []
parser = etree.HTMLParser()
tree = etree.HTML(html, parser=parser)
# 更稳:只要包含 bang_list 就行
items = tree.xpath('//ul[contains(@class,"bang_list_mode")]/li[.//div[@class="name"]/a]')
# 如果这里还是 0:说明拿到的 html 不是榜单页(风控/跳转/空壳)
if not items:
# 帮你快速定位问题:打印页面 title
title = tree.xpath('//title/text()')
print("⚠️ 未匹配到榜单列表,页面 title:", title[0].strip() if title else "None")
return []
books = []
for item in items:
try:
# 排名
rank = item.xpath('.//div[contains(@class,"list_num")]/text()')
rank = rank[0].strip().strip('.') if rank else ""
# 书名:优先 title,其次 text
title = item.xpath('.//div[@class="name"]/a/@title')
if not title:
title = item.xpath('.//div[@class="name"]/a/text()')
title = title[0].strip() if title else "未知书名"
# 作者(第一块 publisher_info)
author = item.xpath('.//div[contains(@class,"publisher_info")][1]//a[1]/text()')
author = author[0].strip() if author else "未知作者"
# 现价
price_str = item.xpath('.//span[contains(@class,"price_n")]/text()')
price_str = price_str[0] if price_str else ""
price = float(re.sub(r"[^\\d.]", "", price_str) or 0)
# 原价(可无)
price_r_str = item.xpath('.//span[contains(@class,"price_r")]/text()')
price_r_str = price_r_str[0] if price_r_str else ""
price_r = float(re.sub(r"[^\\d.]", "", price_r_str) or 0)
books.append({
"Rank": rank,
"Title": title,
"Author": author,
"Price": price,
"Original_Price": price_r
})
except Exception as e:
print(f"⚠️ 解析单条出错: {e}")
continue
return books
8️⃣ 数据存储与导出(Storage)
import pandas as pd
import os
def save_to_csv(data_list):
df = pd.DataFrame(data_list)
# 简单的目录检查
if not os.path.exists('results'):
os.makedirs('results')
path = 'results/dangdang_top500.csv'
# 依然使用 utf-8-sig 防止 Excel 乱码
df.to_csv(path, index=False, encoding='utf-8-sig')
print(f"📊 数据已保存,共 {len(df)} 条。路径: {path}")
9️⃣ 运行方式与结果展示(必写)
这里涉及翻页逻辑。当当榜单 URL 规律是 …-recent7-0-0-1-{页码}。
def main():
base_url = "http://bang.dangdang.com/books/bestsellers/01.00.00.00.00.00-recent7-0-0-1-{}"
all_books = []
print("🚀 开始抓取当当畅销榜 Top 500…")
# 榜单共 25 页
for page in range(1, 26):
url = base_url.format(page)
print(f"📄 正在处理第 {page}/25 页…")
html = fetch_page(url)
if html:
data = parse_html(html)
all_books.extend(data)
print(f" ✅ 本页提取 {len(data)} 本书")
# 礼貌延时
time.sleep(uniform(0.5, 1.5))
save_to_csv(all_books)
print("🎉 任务全部完成!")
if __name__ == "__main__":
main()
📊 结果示例 (Console):
🚀 开始抓取当当畅销榜 Top 500…
📄 正在处理第 1/25 页…
✅ 本页提取 20 本书
📄 正在处理第 2/25 页…
✅ 本页提取 20 本书
📄 正在处理第 3/25 页…
✅ 本页提取 20 本书
📄 正在处理第 4/25 页…
✅ 本页提取 20 本书
📄 正在处理第 5/25 页…
✅ 本页提取 20 本书
📄 正在处理第 6/25 页…
✅ 本页提取 20 本书
📄 正在处理第 7/25 页…
✅ 本页提取 20 本书
📄 正在处理第 8/25 页…
✅ 本页提取 20 本书
📄 正在处理第 9/25 页…
✅ 本页提取 20 本书
📄 正在处理第 10/25 页…
✅ 本页提取 20 本书
📄 正在处理第 11/25 页…
✅ 本页提取 20 本书
📄 正在处理第 12/25 页…
✅ 本页提取 20 本书
📄 正在处理第 13/25 页…
✅ 本页提取 20 本书
📄 正在处理第 14/25 页…
✅ 本页提取 20 本书
📄 正在处理第 15/25 页…
✅ 本页提取 20 本书
📄 正在处理第 16/25 页…
✅ 本页提取 20 本书
📄 正在处理第 17/25 页…
✅ 本页提取 20 本书
📄 正在处理第 18/25 页…
✅ 本页提取 20 本书
📄 正在处理第 19/25 页…
✅ 本页提取 20 本书
📄 正在处理第 20/25 页…
✅ 本页提取 20 本书
📄 正在处理第 21/25 页…
✅ 本页提取 20 本书
📄 正在处理第 22/25 页…
✅ 本页提取 20 本书
📄 正在处理第 23/25 页…
✅ 本页提取 20 本书
📄 正在处理第 24/25 页…
✅ 本页提取 20 本书
📄 正在处理第 25/25 页…
✅ 本页提取 20 本书
📊 数据已保存,共 500 条。路径: results/dangdang_top500.csv
🎉 任务全部完成!
实际本地运行结果如下:

📋 CSV 文件预览:
| 1 | 蛤蟆先生去看心理医生 | 罗伯特·戴博德 | 23.90 | 38.00 |
| 2 | 三体:全三册 | 刘慈欣 | 51.50 | 93.00 |
| 3 | 长安的荔枝 | 马伯庸 | 29.80 | 45.00 |
实际CSV保存数据展示如下:

🔟 常见问题与排错(💡 强烈建议写)
中文乱码(最常见)
- 现象:书名全是 `` 或者 锟斤拷。
- 解决:必须在 requests.get 后执行 response.encoding = 'gbk'。如果 GBK 报错,尝试 gb18030(GBK 的超集)。
XPath 提取为空
- 当当的 HTML 很乱,有时作者栏没有链接 <a>,只有纯文本。
- 优化 XPath:div[@class="publisher_info"][1]//text() 可以提取该 div 下所有文本,然后再做字符串清洗。
价格提取失败
- 有些书可能是电子书或缺货,价格标签的 class 可能会变。建议使用 try-except 给价格默认赋值 -1 或 0,方便后续清洗。
1️⃣1️⃣ 进阶优化(可选但加分)
- 数据清洗 pro 版:现在的作者列可能包含“[日]”、“著”、“译”等字样。可以使用 Pandas 配合正则表达式 re.sub(r'[\\[\\(].*?[\\)\\]]', '', name) 一键清洗掉方括号里的国籍。
- 多维度分析:拿到 Price 和 Original_Price 后,可以计算一个 Discount_Rate(折扣率),看看前 10 名的书是不是因为打折狠才卖得好?🤔
1️⃣2️⃣ 总结与延伸阅读
当当网的爬取难度在于“脏数据”的处理和编码的坑。完成了这个任务,说明你已经具备了处理老旧网站和批量翻页列表的能力。
下一步挑战:
- 京东图书:京东的价格通常是 JS 动态加载的(为了防止爬虫比价),requests 抓不到价格。你需要去 Network 面板找那个隐藏的 API 接口,难度瞬间提升一个 Level!🧗♂️
代码写好了,快去试试能抓到什么好书吧!如果有报错,记得看乱码那一部分哦!
🌟 文末
好啦~以上就是本期 《Python爬虫实战》的全部内容啦!如果你在实践过程中遇到任何疑问,欢迎在评论区留言交流,我看到都会尽量回复~咱们下期见!
小伙伴们在批阅的过程中,如果觉得文章不错,欢迎点赞、收藏、关注哦~ 三连就是对我写作道路上最好的鼓励与支持! ❤️🔥
📌 专栏持续更新中|建议收藏 + 订阅
专栏 👉 《Python爬虫实战》,我会按照“入门 → 进阶 → 工程化 → 项目落地”的路线持续更新,争取让每一篇都做到:
✅ 讲得清楚(原理)|✅ 跑得起来(代码)|✅ 用得上(场景)|✅ 扛得住(工程化)
📣 想系统提升的小伙伴:强烈建议先订阅专栏,再按目录顺序学习,效率会高很多~
✅ 互动征集
想让我把【某站点/某反爬/某验证码/某分布式方案】写成专栏实战?
评论区留言告诉我你的需求,我会优先安排更新 ✅
⭐️ 若喜欢我,就请关注我叭~(更新不迷路) ⭐️ 若对你有用,就请点赞支持一下叭~(给我一点点动力) ⭐️ 若有疑问,就请评论留言告诉我叭~(我会补坑 & 更新迭代)
免责声明:本文仅用于学习与技术研究,请在合法合规、遵守站点规则与 Robots 协议的前提下使用相关技术。严禁将技术用于任何非法用途或侵害他人权益的行为。



