外观
小红书数据分析
原文: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 打开就能使用,具体使用方法如下:
- 配置项目环境在VSCode 或PyCharm 的终端中,依次执行如下两行命令,用来分别配置Python 和JavaScript的环境:
text
pip install -r requirements.txt
npm install- 获取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 所示:

图12: 找到开发者工具

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

图14: 获取cookie
如果小红书网页没有关闭,也没有退出个人账号,则再次使用爬虫爬取网页内容时,无需再次获取cookies,否则需要按上述方法重新获取并修改main.py 文件中的cookies_str 字符串。
修改main.py 文件参数对于第130-141 行的参数,可按自己的喜好修改,其中query 字符串是要搜索的关键词,query_num 指的是将搜索结果中前多少条笔记爬取。如图14 所示。
运行并查看结果运行爬虫项目,会在项目文件夹中生成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 所示。

图15: 分享数量条形统计图
D.4.2 绘制两个变量的散点图
对于一个笔记的点赞数量和收藏数量,可以认为这两个值都表现了对这个笔记的积极观点。为了直观地感受这两个变量之间的关系,我们可以画出这两个变量的散点图。代码文件是scatterPlot.py。绘图结果如图16 所示(本次绘图剔除了值较大但数量较少的点)。
D.5 探索性任务(选做)
D.5.1 词频分析与大模型筛选
为了统计哪北大的哪些食物在小红书中的频率更高,我们可以使用Jieba 库来进行中文分词。食物名称是名词,因此我们可以先筛选出所有名词,再交给大模型进一步筛选哪些是食物

图16: 点赞数量与收藏数量散点图
名称。代码文件是wordsFrequency.py。筛选出的名词保存在“词频统计.txt”文件中。本次任务我们使用的大模型是Gemini 2.5,使用的提示词是:
这里有词频统计前100 名的数据,请返回这些词中属于食物的词,返回前10 名即可。要求{word}:{count} 格式不要变。
话题:1282食堂:509...(一共100 个词语及频率)
最终大模型返回的结果是:
牛肉:78
面:58
鸡:55
汤:51
肉:48
鸡肉:43
菜:40
砂锅:36
肥牛:35
鸡蛋: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 所示。

图17: 点赞数量与收藏数量散点图
D.5.3 探究两个变量之间的线性关系
对于在D.4.2 中提到的两个变量,我们可以进一步探究其关系,例如用线性回归方程来拟合两个变量。这里我们可以用机器学习中常用的sklearn 库来计算回归方程,而无需手动计算。代码文件是linearRegression.py。绘图结果如图18 所示(本次绘图剔除了值较大但数量较少的点)。
这个探索性任务是对机器学习的抛砖引玉,本任务使用的线性回归可能各位同学在高中阶段就学习过,是一个固定的计算方式,没有优化参数等步骤,相对而言比较容易理解。有兴趣的同学可以继续探索机器学习的更深奥的理论。
D.6 总结(必做)
总结一下自己在分析收集到的数据这一过程中的收获,可以是学会了某个Python 包,也可以是学会了如何处理大量数据,或者是对北大的美食有了更进一步的了解,言之有理即可。

图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 NonebarChart.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)及随附示例代码。