Skip to content

小红书数据分析 ​

原文:2025 计算概论 C 钱丽艳班助教团队,版本 1.0,日期 2025 年 11 月 27 日。本页保留 PDF 对应部分全文,章节编号沿用原文。

课程与提交说明 · 公共要求与 Python 基础 · 完整 PDF(网盘链接待补充)

3 . 选题三:小红书数据分析 ​

利用爬虫工具获取小红书特定关键词下的笔记数据,结合统计学方法与大模型进行深度分析,包括但不限于:

  • 数据获取:使用提供的爬虫工具,设置关键词(如“北大美食”、“高校生活”等)和爬取数量,获取笔记的详细内容、评论数、点赞数、收藏数等信息。

  • 数据分析任务:

    • 可视化数据分析:针对“点赞数”等互动指标绘制条形统计图。由于此类数据往往符合长尾分布,建议设置不等间距的区间(数值越大间距越大)以优化展示效果。

    • 相关性分析:绘制“点赞数量”与“收藏数量”的散点图,直观感受两个变量间的关系;进一步可使用sklearn 库进行线性回归拟合,探究其线性关系。

  • 探索性任务:

    • 大模型辅助的词频分析:利用jieba 分词提取名词后,结合大语言模型(如Gemini、DeepSeek)进行语义筛选。例如在分析“北大美食”时,让大模型从高频词中精准筛选出“牛肉”、“面”、“砂锅”等具体的食物名称。

    • 词云生成:基于筛选后的关键词,加载中文字体文件与停用词表,绘制词云图以直观展示热门内容。

D 小红书数据分析示例 ​

D.1 爬虫的使用 ​

为了获取小红书中的笔记,我们可以使用爬虫来爬取笔记的详细内容、评论、点赞数、收藏数等信息。本项作业我们使用https://github.com/cv-cat/Spider_XHS 的爬虫来获取数据,此代码已打包为Spider_XHS-master.zip(已经略作修改,更方便使用),解压后用VSCode 或PyCharm 打开就能使用,具体使用方法如下:

  1. 配置项目环境在VSCode 或PyCharm 的终端中,依次执行如下两行命令,用来分别配置Python 和JavaScript的环境:

text
 pip install -r requirements.txt
 npm install
  1. 获取cookies在Chrome 浏览器中,打开小红书网页主页(https://www.xiaohongshu.com/explore)并登录个人帐号(必须登录个人账号,否则无法搜索内容)后,依次点击:(1)右上角的三个点的图案(2)更多工具(3)开发者工具,如图12 所示:接着依次点击:(1)左上角的刷新图案(2)Network(3)Fetch/XHR(4)Name 列中任意一个图案为橙色的“{;}”项(5)Headers,往下拉,在Request Headers 中找到Cookie,如

图13 所示:

原文插图(PDF 第 20 页)

图12: 找到开发者工具

原文插图(PDF 第 20 页)

图13: 获取cookie将cookie 的内容复制下来,放入爬虫代码的main.py 文件的第129 行的cookies_str 字符串中,如图14 所示:

原文插图(PDF 第 21 页)

图14: 获取cookie

如果小红书网页没有关闭,也没有退出个人账号,则再次使用爬虫爬取网页内容时,无需再次获取cookies,否则需要按上述方法重新获取并修改main.py 文件中的cookies_str 字符串。

  1. 修改main.py 文件参数对于第130-141 行的参数,可按自己的喜好修改,其中query 字符串是要搜索的关键词,query_num 指的是将搜索结果中前多少条笔记爬取。如图14 所示。

  2. 运行并查看结果运行爬虫项目,会在项目文件夹中生成datas 文件夹,其中包含excel_datas 文件夹和media_datas 文件夹,分别是提取的笔记的表格式汇总信息和每个笔记的详细信息。注:在VSCode 或PyCharm 的终端中输出若干个“爬取笔记信息”后,会有一段时间没有任何输出,这是在保存数据文件,时间较长,请耐心等待,之后会输出“数据保存至”、“搜索关键词**: True, msg: 成功”,表示数据保存成功。

在使用爬虫前,最好先在小红书app 或网页版上先搜索一些关键词,想好你要探索的关键词是什么,然后使用爬虫爬取这个关键词。尽量不要使用太多次爬虫,因为小红书反爬虫机制很严,你的账号爬取过多的话,可能会被监测到导致爬取失败。这里我们以搜索关键词“北大美食”为例,将爬取到的小红书的数据进行分析。本次我们设置了爬取1000 篇笔记的参数,但爬虫只爬取到了220 篇笔记。爬取到的文件是“北大美食.xlsx”。(你也可以选择搜索关键词“北大自习”、“北大课程”、“北京高校食堂”、“全国高校食堂”或其他关键词,只要能满足大作业的基本要求即可。)

D.2 选题背景(必做) ​

可以简略写一些内容,写出自己为什么想选择这个关键词来进行数据分析。例如想了解北大有什么美食,有哪些需要避雷的食物等。

D.3 数据获取(必做) ​

给出爬虫代码的参数设置(130-141 行的参数),以及爬取得到的datas 文件夹。

D.4 数据分析(必做) ​

在数据分析和探索性任务部分,我们需要用Python 代码来对数据及其性质进行一些简单的描述,在这一过程中的代码我们可以通过大模型辅助来完成(其实其他涉及到代码的部分也都可以使用大模型辅助)。

D.4.1 绘制条形统计图 ​

对于点赞数量,我们可以使用条形统计图来直观感受各个数量区间的笔记的数量。代码文件是barChart.py。

对于点赞数量,其往往符合长尾分布(较小的值数量很多,较大的值数量较少)。因此如果条形统计图的各个数量区间是等间距的,画出来的图将明显呈现出左边很高,中间及右边很低甚至没有值的情况,结果将不够美观。因此我们设置各个数量区间不等间距,数值越大,间距也越大。绘图结果如图15 所示。

原文插图(PDF 第 22 页)

图15: 分享数量条形统计图

D.4.2 绘制两个变量的散点图 ​

对于一个笔记的点赞数量和收藏数量,可以认为这两个值都表现了对这个笔记的积极观点。为了直观地感受这两个变量之间的关系,我们可以画出这两个变量的散点图。代码文件是scatterPlot.py。绘图结果如图16 所示(本次绘图剔除了值较大但数量较少的点)。

D.5 探索性任务(选做) ​

D.5.1 词频分析与大模型筛选 ​

为了统计哪北大的哪些食物在小红书中的频率更高,我们可以使用Jieba 库来进行中文分词。食物名称是名词,因此我们可以先筛选出所有名词,再交给大模型进一步筛选哪些是食物

原文插图(PDF 第 23 页)

图16: 点赞数量与收藏数量散点图

名称。代码文件是wordsFrequency.py。筛选出的名词保存在“词频统计.txt”文件中。本次任务我们使用的大模型是Gemini 2.5,使用的提示词是:

这里有词频统计前100 名的数据,请返回这些词中属于食物的词,返回前10 名即可。要求{word}:{count} 格式不要变。

话题:1282食堂:509...(一共100 个词语及频率)

最终大模型返回的结果是:

  1. 牛肉:78

  2. 面:58

  3. 鸡:55

  4. 汤:51

  5. 肉:48

  6. 鸡肉:43

  7. 菜:40

  8. 砂锅:36

  9. 肥牛:35

  10. 鸡蛋:33

D.5.2 词云生成 ​

为了更直观地看出词频分析中提取到的名词的频率,我们可以生成词云。代码文件是wordsCloud.py。

在生成中文词云图时,字体设置是最大的难点:如果未设置代码文件中第25 行的font_path,词云图中的所有中文字符都会显示为方块。对于不同操作系统,有不同的字体路径文件:

  • Windows: 字体文件通常在C:\Windows\Fonts 目录下,例如simhei.ttf。

  • macOS: 字体文件通常在/System/Library/Fonts/ 或/Library/Fonts/ 目录下,例如Songti.ttc。

  • Linux: 字体位置各异,可以搜索系统中的.ttf 或.ttc 文件。对于本任务,我们不再像词频分析中那样只提取名词,而是将所有词性的词全部提取,然后使用停用词(介词等没有实际意义的词)等方法来筛选。本次我们使用的停用词来自于https://github.com/Northriven/Stopwords/blob/main/stopwords_cn.txt ,保存在stopwords.txt文件中。词云绘制效果如图17 所示。

原文插图(PDF 第 24 页)

图17: 点赞数量与收藏数量散点图

D.5.3 探究两个变量之间的线性关系 ​

对于在D.4.2 中提到的两个变量,我们可以进一步探究其关系,例如用线性回归方程来拟合两个变量。这里我们可以用机器学习中常用的sklearn 库来计算回归方程,而无需手动计算。代码文件是linearRegression.py。绘图结果如图18 所示(本次绘图剔除了值较大但数量较少的点)。

这个探索性任务是对机器学习的抛砖引玉,本任务使用的线性回归可能各位同学在高中阶段就学习过,是一个固定的计算方式,没有优化参数等步骤,相对而言比较容易理解。有兴趣的同学可以继续探索机器学习的更深奥的理论。

D.6 总结(必做) ​

总结一下自己在分析收集到的数据这一过程中的收获,可以是学会了某个Python 包,也可以是学会了如何处理大量数据,或者是对北大的美食有了更进一步的了解,言之有理即可。

原文插图(PDF 第 25 页)

图18: 点赞数量与收藏数量线性回归图

附件参考代码(PDF 之外的配套源文件) ​

以下分析代码按附件原样收录,未执行。爬虫项目及其安装说明请在下载包中查看。

readExcel.py
python
import pandas as pd


def getText(target_columns):
    """
    :param target_columns: 想要提取的列的列表,大写字母'A','B','C'等
    :return: 二维列表,其中行数是提取的笔记数量,列数是 len(target_columns)
    """
    # 1. 定义文件路径
    file_path = '北大美食.xlsx'
    try:
        # 2. 读取 Excel 文件
        # skiprows=[0] 跳过第 1 行
        # usecols=target_columns 指定要读取的列
        df = pd.read_excel(
            file_path,
            sheet_name=0,  # 第一个 Sheet
            usecols=target_columns  # 指定要读取的列
        )
        # 3. 结果处理
        # 此时 df 包含 len(target_columns) 列数据
        print(f"--- 提取的列名: {list(df.columns)} ---")
        print(f"--- 提取的行数: {len(df)} ---")
        print("\n--- 提取的数据(前5行)---")
        print(df.head())  # 打印前 5 行数据进行查看
        # 4. 将所有列的数据都转换为二维列表
        all_data = df.values.tolist()
        return all_data
    except FileNotFoundError:
        print(f"错误:文件未找到。请检查路径:{file_path}")
        return None
    except Exception as e:
        print(f"读取文件时发生错误:{e}")
        return None
barChart.py
python
import matplotlib.pyplot as plt
import pandas as pd
import numpy as np
from readExcel import getText

# 获取数据
raw_data = getText([12])
data_shares = np.array([int(x[0]) for x in raw_data])

# 定义分组的边界
bins = [0, 10, 50, 100, 500, 1000, 2000]

# 定义每个条形图的标签 (用于X轴)
bin_labels = ['0-9', '10-49', '50-99', '100-499', '500-999', '1000-1999']

# 使用 pandas.cut 将数据划分到各个区间,并计算每个区间的数量
# right=False 表示区间是左闭右开,例如 [1, 5) 包含 1, 2, 3, 4
binned_data = pd.cut(data_shares, bins=bins, right=False, labels=bin_labels[:len(bins) - 1])

# 计算每个分组的频率/计数
counts = binned_data.value_counts().sort_index()

# 整理数据为绘图格式
categories = counts.index.tolist()
values = counts.values.tolist()

print("分组结果:")
for cat, val in zip(categories, values):
    print(f"分享人数 {cat}:有 {val} 人")

plt.figure(figsize=(10, 6))

# 以 SimHei 字体为例
plt.rcParams['font.sans-serif'] = ['SimHei']

# 解决保存图像时负号 '-' 显示为方块的问题
plt.rcParams['axes.unicode_minus'] = False

# 绘制条形图
plt.bar(categories, values, color='forestgreen', alpha=0.8)

# 添加标题和标签
plt.title('用户分享人数分布统计 (非等宽分组)', fontsize=16)
plt.xlabel('分享人数区间', fontsize=12)
plt.ylabel('用户数量', fontsize=12)

# 可选:在每个条形图上显示数值
for i, v in enumerate(values):
    plt.text(i, v + 0.5, str(v), ha='center', va='bottom', fontsize=10)

plt.grid(axis='y', linestyle='--', alpha=0.5)
plt.xticks(rotation=0)  # X轴标签不旋转
plt.tight_layout()  # 调整布局,防止标签重叠
plt.show()
scatterPlot.py
python
import numpy as np
import matplotlib.pyplot as plt
from readExcel import getText

# --- 1. 数据准备 ---
# 点赞数量和收藏数量分别在第 9 和第 10 列
raw_data = getText([9, 10])
data = []
for nums in raw_data:
    # 剔除一些值偏大,但样本较少的点
    if int(nums[0]) <= 250 and int(nums[1]) <= 200:
        data.append([int(nums[0]), int(nums[1])])
print(f"筛选后的样本数是{len(data)}")
# X 必须是二维数组
X = np.array([num[0] for num in data]).reshape(-1, 1)
y = np.array([num[1] for num in data])

# --- 2. 绘图 ---
plt.figure(figsize=(8, 6))

# 以 SimHei 字体为例
plt.rcParams['font.sans-serif'] = ['SimHei']

# 解决保存图像时负号 '-' 显示为方块的问题
plt.rcParams['axes.unicode_minus'] = False

# 绘制原始散点图
# X.flatten() 用于将二维数组转换回一维,方便 matplotlib 绘图
plt.scatter(X.flatten(), y, color='blue', label='原始数据点 $(x_i, y_i)$', marker='o', s=100)

# 添加图表标题和标签
plt.title('散点图')
plt.xlabel('自变量 X')
plt.ylabel('因变量 Y')
plt.legend()
plt.grid(True, linestyle='--', alpha=0.6)

# 显示图表
plt.show()
wordsFrequency.py
python
import jieba.posseg as psg
from collections import Counter
from readExcel import getText

# 1. 读取小红书笔记正文
# 第 8 列是描述
raw_text = getText([8])
# 每个笔记正文都在二维列表中
text = ''.join([s[0] for s in raw_text])

# 2. 设置只保留名词
allowed_flags = {'n'}

# 3. 提取分好的名词
clean_words = []
for word, flag in psg.cut(text):
    if flag in allowed_flags:
        clean_words.append(word)

# 4. 统计词频
counter = Counter(clean_words)
# 查看最高频出现的 5 个词
print(f"词频统计: {Counter(clean_words).most_common(5)}")

# 5. 将词频结果写入文件
file_name = "词频统计.txt"
# 词频结果转为列表
lines = sorted(counter.items(), key=lambda x: -x[1])
lines_to_write = [f"{count[0]}:{count[1]}\n" for count in lines]
try:
    with open(file_name, 'w', encoding='utf-8') as f:
        # writelines() 接受一个字符串列表,并将其内容连续写入文件
        f.writelines(lines_to_write)
    print(f"列表内容已成功写入到 {file_name} 中,每行一个元素。")
except Exception as e:
    print(f"写入文件时发生错误: {e}")
wordsCloud.py
python
import jieba
import re
from wordcloud import WordCloud
import matplotlib.pyplot as plt
from readExcel import getText

def load_stopwords(filepath='stopwords.txt'):
    # 以 UTF-8 编码读取文件,并去除每行的空白符,生成一个集合(set)
    # 使用集合(set)进行查找,速度比列表(list)快得多
    with open(filepath, 'r', encoding='utf-8') as f:
        stopwords = {line.strip() for line in f}
    return stopwords


# 定义一个函数用于过滤
def filter_words(word_list, stopwords):
    filtered_words = []
    for word in word_list:
        # a. 转换为小写并去除空白字符
        word = word.lower()
        word = re.sub(r'\s+', '', word)
        # b. 过滤停用词:检查词语是否在停用词集合中
        if word in stopwords:
            continue
        # c. 过滤单字:通常单字意义不大,但需根据项目需求决定是否保留
        if len(word) < 2:
            continue
        # d. 过滤纯数字或标点符号:使用正则表达式检查是否为纯中文、英文或数字的组合
        if re.fullmatch(r'[\d\W]+', word):  # 匹配纯数字或纯标点符号
            continue
        # 如果通过所有检查,则保留
        filtered_words.append(word)
    return filtered_words

# --- 1. 数据准备:将文本处理成词语字符串 ---

# 第 8 列是描述
raw_text = getText([8])
# 每个笔记正文都在二维列表中
text = ''.join([s[0] for s in raw_text])

# 1.1 使用 Jieba 进行分词
# 注意:这里我们使用 ' '.join() 将词语用空格连接起来,
# 这是 wordcloud 库读取中文文本的标准格式。
raw_words = jieba.lcut(text, cut_all=False)
# 过滤一些无意义的词
STOP_WORDS = load_stopwords()
filtered_words = filter_words(raw_words, STOP_WORDS)
print(filtered_words)
text_for_wordcloud = " ".join(filtered_words)

# --- 2. 配置参数与字体 ---

# 中文词云图必须指定字体,否则中文会显示为方块
# 请确保您的系统路径下有这个字体文件,否则请替换为其他可用字体
# 常见的 Windows 字体:simhei.ttf (黑体), simfang.ttf (仿宋)
# Mac/Linux 用户可能需要调整路径
font_path = "C:\\Windows\\Fonts\\simhei.ttf"  # 假设使用黑体

wc = WordCloud(
    # 词云图背景颜色
    background_color="white",
    # 最大词语数
    max_words=100,
    # 词云图的宽度和高度
    width=800,
    height=400,
    # 必须指定中文字体路径
    font_path=font_path,
    repeat=False # 禁止重复
)

# --- 3. 生成图像 ---

# 3.1 从处理好的文本生成词云
wordcloud_image = wc.generate(text_for_wordcloud)

# 3.2 显示词云图
plt.figure(figsize=(10, 5))  # 设置图片大小
plt.imshow(wordcloud_image, interpolation='bilinear')
plt.axis("off")  # 不显示坐标轴
plt.show()

# 3.3 (可选)保存词云图到文件
# wordcloud_image.to_file("wordcloud_output.png")

print("词云图生成完毕并显示。")
linearRegression.py
python
import numpy as np
import matplotlib.pyplot as plt
from sklearn.linear_model import LinearRegression
from readExcel import getText

# --- 1. 数据准备 ---
# 点赞数量和收藏数量分别在第 9 和第 10 列
raw_data = getText([9, 10])
data = []
for nums in raw_data:
    # 剔除一些值偏大,但样本较少的点
    if int(nums[0]) <= 250 and int(nums[1]) <= 200:
        data.append([int(nums[0]), int(nums[1])])
print(f"筛选后的样本数是{len(data)}")
# X 必须是二维数组
X = np.array([num[0] for num in data]).reshape(-1, 1)
y = np.array([num[1] for num in data])

# --- 2. 模型训练 (计算方程参数) ---
model = LinearRegression()
model.fit(X, y)

# 获取参数
coefficient = model.coef_[0]
intercept = model.intercept_

# 打印方程(方便查看)
print(f"拟合方程: y = {coefficient:.4f} * x + {intercept:.4f}")

# --- 3. 准备绘图数据 (生成直线上的点) ---
# 获取用于绘制直线的 x 值范围
x_range = np.linspace(X.min(), X.max(), 100).reshape(-1, 1)

# 使用模型预测这些 x 值对应的 y 值,即拟合的直线
y_line = model.predict(x_range)

# --- 4. 绘图 ---
plt.figure(figsize=(8, 6))

# 以 SimHei 字体为例
plt.rcParams['font.sans-serif'] = ['SimHei']

# 解决保存图像时负号 '-' 显示为方块的问题
plt.rcParams['axes.unicode_minus'] = False

# 绘制原始散点图
# X.flatten() 用于将二维数组转换回一维,方便 matplotlib 绘图
plt.scatter(X.flatten(), y, color='blue', label='原始数据点 $(x_i, y_i)$', marker='o', s=100)

# 绘制线性回归直线
plt.plot(x_range.flatten(), y_line, color='red', label='线性回归方程 $y = {%.2f}x + {%.2f}$' % (coefficient, intercept),
         linewidth=2)

# 添加图表标题和标签
plt.title('散点图与线性回归直线拟合')
plt.xlabel('自变量 X')
plt.ylabel('因变量 Y')
plt.legend()
plt.grid(True, linestyle='--', alpha=0.6)

# 显示图表
plt.show()

来源:2025 计算概论 C 钱丽艳班助教团队,作业要求 v1.0(2025-11-27)及随附示例代码。

课程资料由授课教师、助教与同学共同积累。