16  案例:当当网 Python 类图书销售数据分析

16.1 背景

我想写一本有关 Python 应用的书。目前我有很多想法,拿不定主意。比如,

  • 目标读者是谁?经管类博士生,还是中小学生?
  • 书的内容是什么?是 Python 基础知识,还是 Python 数据分析?亦或是机器学习?
  • 书的形式是什么?是电子书,还是纸质书?亦或是两者都有?
  • 书的价格是多少?是 50 元,还是 100 元?亦或是 200 元?
  • 找哪家出版社?

在不了解这些信息的情况下,贸然画一年的时间写一本书是一个非常冒险的决定。为了降低风险,我决定先做一些市场调研,看看读者对 Python 书籍的需求。

我先通过百度搜索和 Google 搜索,了解到当当网是中国最大的图书销售网站之一,拥有丰富的图书数据。我决定从当当网获取一些 Python 类图书的销售数据,进行分析,看看读者对 Python 书籍的需求。

问题来了,我只有一些非常基础的 Python 知识 (大体上限于能对着课本或帮助文档提供的实例进行一些微小的修改,然后运行)。而且,我目前掌握的 Python 知识基本上进局限于回归分析。对于网络爬虫、数据分析、数据可视化等知识,我几乎一无所知。

我听说 ChatGPT, DeepSeek 等 AI 工具可以帮助我完成一些复杂的任务,比如网络爬虫、数据分析、数据可视化等。我决定试试这些工具,看看它们能否帮助我完成我的市场调研。

于是,我写一个提示词,发给了 ChatGPT:

与 ChatGPT 的完整对话

提示词
- 目的:从当当网获取Python相关书籍的销售量信息,最终输出一份市场调研报告。
- 语言:Python
- 网站:[https://search.dangdang.com/](https://search.dangdang.com/)
- 关键词:'python'
- 排序关键词:'销量'
- 排序方式:从高到低
- 信息条数:前 20 条
- 目前搜索结果页面URL:[https://search.dangdang.com/?key=python\&act=input\&sort\_type=sort\_sale\_amt\_desc#J\_tab](https://search.dangdang.com/?key=python&act=input&sort_type=sort_sale_amt_desc#J_tab)

- 预期输出: 
  - 书名,作者,年份,出版社,评论数,原价、折后价

以上是我目前想到的。
请帮我做一个全面的规划,包括分析目标、收集那些变量,分析流程,以及最终的报告形式、核心内容等。

以下是 ChatGPT 的回复:

AI 的回复

好的,以下是一份较为系统的规划方案,适用于以 Python 编写程序从当当网采集“Python”相关书籍的销售量数据,并撰写一份结构完整的市场调研报告。


17 Python 相关图书市场调研项目规划

17.1 一、分析目标

  • 核心问题:当前在当当网上热销的 Python 图书有哪些?主要出版时间集中在哪些年份?哪些出版社主导了市场?

  • 业务用途

    • 为出版商、培训机构、书店提供选书参考;
    • 了解当前市场偏好(入门/进阶、编程/数据分析/人工智能);
    • 为课程教材选择提供数据支持。

17.2 二、数据采集计划

17.2.1 1. 数据来源

17.2.2 2. 待采集字段(建议)

字段名 说明
书名 完整书名
作者 主作者及其他合著者
出版社 出版机构
出版时间 年份或具体日期
原价 划线价
折后价 实际售价
评论数 用户评论数,反映活跃度
星级评分 若有星级则采集
封面图链接 便于后续可视化展示
图书详情页链接 为后续扩展字段或人工校验提供入口

17.3 三、分析流程

17.3.1 Step 1:数据采集(Python 编程)

  • 使用库:

    • requests 模拟访问
    • BeautifulSoup 解析 HTML
    • relxml 辅助提取字段
  • 反爬措施应对:

    • 合理设置 headers(如模拟浏览器 UA)
    • 增加 time.sleep() 随机延迟
    • 如遇 JavaScript 动态加载,可考虑 selenium

17.3.2 Step 2:数据清洗与规范化

  • 作者、出版社等字段统一格式
  • 出版时间转为 datetime
  • 价格字段转换为 float
  • 评论数转换为整数(去除“条评论”字样)

17.3.3 Step 3:数据分析

17.3.3.1 3.1 描述性统计

  • 平均价格、评论数中位数
  • 出版年份分布(近几年为主)
  • 高频作者、出版社统计

17.3.3.2 3.2 分类分析

  • 入门类 vs 进阶类
  • 数据分析 vs Web 编程 vs 机器学习
  • 是否为教材或考试参考书

17.3.3.3 3.3 排行榜可视化

  • 前 10 图书销量/评论/价格对比柱状图
  • 出版时间折线图
  • 价格 vs 评论数散点图

17.4 四、报告输出格式

17.4.1 1. 技术报告形式(推荐)

  • 格式:Markdown / Quarto / PDF(可由 Jupyter Notebook 或 Quarto 生成)
  • 结构建议
# Python 图书销售市场调研报告(当当网)

## 1. 项目背景与目标
## 2. 数据采集与字段说明
## 3. 市场总体概况
   - 销售 Top20 图书一览
   - 平均价格、评分、评论数等统计
## 4. 出版特征分析
   - 出版社分布、出版时间分布
   - 作者贡献度分析
## 5. 图书类型对比分析
   - 入门、进阶分布
   - 专业方向分布
## 6. 结论与建议
   - 当前趋势总结
   - 出版或选书建议

17.4.2 2. 附录部分

  • 原始数据表格(20 本书)
  • 数据采集代码(附说明)
  • 图形可视化汇总图(Matplotlib / Seaborn / Plotly)

17.5 五、可扩展方向(可选)

  • 同步采集京东、淘宝图书频道以对比
  • 分析定价策略(原价 vs 折扣价规律)
  • 抽取关键词构建词云图
  • 构建图书推荐系统雏形

如你同意,我可以继续为你编写:

  1. Python 抓取前 20 条当当网图书数据的爬虫脚本;
  2. 可视化分析和 Markdown 报告生成模板;
  3. 教学型讲义或课程材料用版本。

是否现在开始写代码部分?是否使用 requests + BeautifulSoup,还是你希望使用 selenium 模拟浏览器?

17.6 爬取数据

我们让 ChatGPT 执行第一步:

Step 1:使用 Python 抓取当当网「Python」相关图书销量前 20 名信息 的完整采集代码。

17.6.1 技术选择说明

17.6.2 Python 代码

# 设置项目路径
#   [./data]: 存储数据文件
#   [./output]: 存储输出文件


#---------------- 项目路径,酌情修改 -------------------
#
path = r'D:\Github\dslian\cases\web_dangdang_book' 
#
#------------------------------------------------------

import os
import sys
path_data = os.path.join(path, 'data')
path_output = os.path.join(path, 'output')

os.chdir(path)
os.getcwd()
'd:\\Github\\dslian\\cases\\web_dangdang_book'
import requests
from bs4 import BeautifulSoup
import pandas as pd
import time
import os

# 设置目标搜索 URL(销量降序排序)
url = "https://search.dangdang.com/?key=python&sort_type=sort_sale_amt_desc"

# 设置请求头,模拟浏览器访问
headers = {
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 \
                   (KHTML, like Gecko) Chrome/117.0.0.0 Safari/537.36"
}

# 发送请求并获取页面内容
response = requests.get(url, headers=headers)
response.encoding = "gb2312"

# 使用 BeautifulSoup 解析 HTML 内容
soup = BeautifulSoup(response.text, "html.parser")

# 所有图书的信息在 ul.bigimg li 中
book_list = soup.select("ul.bigimg > li")

# 用于保存每本书的信息
books_data = []

# 遍历每本书的条目,提取信息
for item in book_list:
    try:
        title_tag = item.select_one("a[name='itemlist-title']")
        title = title_tag.get("title").strip() if title_tag else ""
        link = title_tag.get("href") if title_tag else ""

        author_tag = item.select_one("p.name a")
        author = author_tag.text.strip() if author_tag else ""

        pub_info_tag = item.select_one("p.search_book_author")
        pub_info = pub_info_tag.text.strip() if pub_info_tag else ""
        pub_info_parts = pub_info.split("\xa0\xa0")

        publisher = pub_info_parts[1].strip() if len(pub_info_parts) > 1 else ""
        pubdate = pub_info_parts[2].strip() if len(pub_info_parts) > 2 else ""

        comment_tag = item.select_one("p.search_star_line a")
        comment = comment_tag.text.strip() if comment_tag else ""

        price_tag = item.select_one("p.price")
        orig_price_tag = price_tag.select_one("span.search_pre_price") if price_tag else None
        disc_price_tag = price_tag.select_one("span.search_now_price") if price_tag else None
        orig_price = orig_price_tag.text.strip("¥") if orig_price_tag else ""
        disc_price = disc_price_tag.text.strip("¥") if disc_price_tag else ""

        books_data.append({
            "书名": title,
            "作者": author,
            "出版社": publisher,
            "出版时间": pubdate,
            "原价": orig_price,
            "折后价": disc_price,
            "评论数": comment,
            "详情页链接": link
        })

        time.sleep(0.5)  # 加入短暂停顿防止访问过快

    except Exception as e:
        print(f"解析失败:{e}")
        continue

# 转换为 DataFrame
df_books = pd.DataFrame(books_data)

# 确保保存路径存在
output_path = "data"
os.makedirs(output_path, exist_ok=True)
output_file = os.path.join(output_path, "dangdang_python_books_top20.csv")

# 保存为 CSV 文件
df_books.to_csv(output_file, index=False, encoding="utf-8-sig")

# 打印成功信息
print(f"已成功下载 {len(df_books)} 条数据,
        点击 [file://{os.path.abspath(output_file)}] 
        查看 dangdang_python_books_top20.csv 文件")
已成功下载 60 条数据,点击 [file://d:\Github\dslian\cases\web_dangdang_book\data\dangdang_python_books_top20.csv] 查看 dangdang_python_books_top20.csv 文件
import requests
from bs4 import BeautifulSoup
import pandas as pd
import time
import os

url = "https://search.dangdang.com/?key=python&sort_type=sort_sale_amt_desc"
headers = {
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 \
                   (KHTML, like Gecko) Chrome/117.0.0.0 Safari/537.36"
}

response = requests.get(url, headers=headers)
response.encoding = "gb2312"
soup = BeautifulSoup(response.text, "html.parser")
book_list = soup.select("ul.bigimg > li")

books_data = []

for item in book_list:
    try:
        # 1. 主标题和简介
        title_tag = item.select_one("a[name='itemlist-title']")
        full_title = title_tag.get("title").strip() if title_tag else ""
        if ":" in full_title:
            title, brief = full_title.split(":", 1)
        elif "(" in full_title:
            title, brief = full_title.split("(", 1)
            brief = "(" + brief
        else:
            title, brief = full_title, ""

        # 2. 详情页链接
        link = title_tag.get("href") if title_tag else ""

        # 3. 作者 / 出版社 / 出版时间
        pub_info_tag = item.select_one("p.search_book_author")
        pub_info_parts = pub_info_tag.text.strip().split("\xa0\xa0") if pub_info_tag else []
        author = pub_info_parts[0].replace("作者", "").strip() if len(pub_info_parts) > 0 else ""
        publisher = pub_info_parts[1].strip() if len(pub_info_parts) > 1 else ""
        pubdate = pub_info_parts[2].strip() if len(pub_info_parts) > 2 else ""

        # 4. 评论数
        comment_tag = item.select_one("p.search_star_line a")
        comment = comment_tag.text.strip() if comment_tag else ""

        # 5. 价格
        price_tag = item.select_one("p.price")
        orig_price_tag = price_tag.select_one("span.search_pre_price") if price_tag else None
        disc_price_tag = price_tag.select_one("span.search_now_price") if price_tag else None
        orig_price = orig_price_tag.text.strip("¥") if orig_price_tag else ""
        disc_price = disc_price_tag.text.strip("¥") if disc_price_tag else ""

        books_data.append({
            "书名": title,
            "简介": brief,
            "作者": author,
            "出版社": publisher,
            "出版时间": pubdate,
            "原价": orig_price,
            "折后价": disc_price,
            "评论数": comment,
            "详情页链接": link
        })

        time.sleep(0.5)  # 加入短暂停顿防止访问过快

    except Exception as e:
        print(f"解析失败:{e}")
        continue

df_books = pd.DataFrame(books_data)

output_path = "data"
os.makedirs(output_path, exist_ok=True)
output_file = os.path.join(output_path, "dangdang_python_books_cleaned.csv")
df_books.to_csv(output_file, index=False, encoding="utf-8-sig")

print(f"已成功下载 {len(df_books)} 条数据,点击 [file://{os.path.abspath(output_file)}] 查看 dangdang_python_books_cleaned.csv 文件")
已成功下载 60 条数据,点击 [file://d:\Github\dslian\cases\web_dangdang_book\data\dangdang_python_books_cleaned.csv] 查看 dangdang_python_books_cleaned.csv 文件
# 读入本地数据
df_books = pd.read_csv(r"data\dangdang_python_books_cleaned.csv", encoding="utf-8-sig")

# 展示前几行结果
print(df_books.columns)
print(df_books.head(3))
print(df_books.info())
Index(['书名', '简介', '作者', '出版社', '出版时间', '原价', '折后价', '评论数', '详情页链接'], dtype='object')
                                                  书名  \
0                                     Python编程三剑客第3版   
1                                      小学生Python创意编程   
2  Python编程从入门到实践 第3版 Python编程入门经典,自学利器,数据分析、网络爬虫...   

                                                  简介  \
0          Python编程从入门到实践第3版+快速上手第2版+极客项目编程(当当套装共3册)   
1  (视频教学版) 本书按照入门开发者的思维方式编写,非常适合孩子学习Python编程的基础知识...   
2                                                NaN   

                                                  作者  出版社  出版时间     原价   折后价  \
0                                            加入购物车收藏  NaN   NaN  267.8  21.6   
1              刘凤飞 /2024-01-01  /清华大学出版社加入购物车购买电子书收藏  NaN   NaN   89.0  45.2   
2  [美]埃里克・马瑟斯(Eric Matthes) /2023-05-01  /人民邮电出版社...  NaN   NaN  109.8  69.8   

         评论数                                    详情页链接  
0  124999条评论  //product.dangdang.com/12100010244.html  
1    7157条评论     //product.dangdang.com/29666296.html  
2   19970条评论     //product.dangdang.com/29564789.html  
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 60 entries, 0 to 59
Data columns (total 9 columns):
 #   Column  Non-Null Count  Dtype  
---  ------  --------------  -----  
 0   书名      60 non-null     object 
 1   简介      29 non-null     object 
 2   作者      60 non-null     object 
 3   出版社     0 non-null      float64
 4   出版时间    0 non-null      float64
 5   原价      57 non-null     float64
 6   折后价     60 non-null     float64
 7   评论数     60 non-null     object 
 8   详情页链接   60 non-null     object 
dtypes: float64(4), object(5)
memory usage: 4.3+ KB
None

17.7 后续

我花了 30 分钟做到目前的样子,基本上搞懂了使用 Copilot 编写 Python 代码实现爬虫的流程。

刚好当时在教授「数据分析与 Python 应用」的课程,我索性把这个案例作为课堂作业,要求学生们完成。