Python爬虫实战:大众点评数据采集、清洗与消费分析全流程解析 简介本资源是一套面向高校计算机专业学生与初阶Python开发者的大众点评评论数据采集与分析实战项目聚焦毕业设计、课程设计及爬虫技术入门实践。项目完整覆盖从网页评论抓取、多线程图片下载、OCR文本识别、MongoDB数据存储到清洗去重、情感分析、SHAP可解释性建模及消费行为可视化报告的全流程兼具工程规范性与教学实用性。压缩包共61个文件含20个核心Python脚本如scrape_comment.js、data_preprocess.py、customer_shap.py、9个JS爬虫逻辑文件、4个Markdown文档含项目介绍与配置说明、4个JSON/YAML配置文件及配套bat/sh启动脚本总大小仅90KB轻量易部署。已有151人学习下载提供完整可运行代码、结构化目录设计、全局变量与日志管理模块并支持在本地快速复现数据获取→清洗→分析→报告生成全链路特别适合毕设选题参考与爬虫进阶实践。1. 项目缘起从数据焦虑到实战洞察最近在复盘一个本地生活服务相关的项目时我遇到了一个典型的数据困境手头有一些零散的消费记录但想了解某个商圈或品类的整体消费趋势、用户评价偏好时却缺乏系统性的数据支撑。市面上的行业报告要么太宏观要么收费昂贵且不一定能精准匹配我的分析需求。这时一个直接的想法就是为什么不自己动手从大众点评这样的生活服务平台上获取第一手数据呢这个想法催生了今天要分享的这个实战项目。它不仅仅是一个简单的“Python爬虫”而是一个从数据采集、清洗到分析、可视化的完整数据流水线。项目最终打包成了一个名为“Python大众点评评论爬虫源码数据清洗消费分析报告实战案例.zip”的压缩包里面包含了可运行的代码、处理好的示例数据、详细的分析报告以及一个完整的实战案例。无论你是想学习爬虫技术、掌握数据清洗的“脏活累活”还是希望获得一份能直接用于商业分析或学术研究的消费洞察报告这个项目都能提供一个清晰的路径和可复现的模板。大众点评作为本地生活领域的头部平台积累了海量的商户信息和用户评价这些数据是理解消费市场、分析用户行为、评估商户竞争力的宝贵资源。通过Python技术栈我们可以自动化地获取这些数据并从中提炼出有价值的信息。接下来我将拆解这个项目的核心模块分享其中的技术选型、实战步骤以及我踩过的一些坑希望能帮你绕过弯路直接上手。2. 技术栈选型与核心原理拆解在开始动手之前选择合适的工具至关重要。这个项目涉及数据采集、处理、存储和分析多个环节我选择的是一套成熟、高效且社区活跃的技术组合。2.1 爬虫框架Requests 自定义会话管理对于大众点评这类反爬机制较为复杂的网站直接使用Scrapy这样重量级的框架有时反而会因特征明显而被快速封禁。我选择了更轻量、更灵活的Requests库作为核心配合Session对象来维持会话状态。大众点评的页面加载和内容呈现严重依赖JavaScript尤其是商户列表和评论数据很多都是通过XHRAjax请求动态加载的。这里的关键在于模拟浏览器行为并正确解析这些动态请求。通过浏览器开发者工具的“网络”Network面板我们可以捕获到获取评论数据的真实API接口。通常这些接口会返回JSON格式的数据里面包含了评论内容、用户评分、人均消费、推荐菜等结构化信息。使用Requests发起对这类接口的请求比解析完整的HTML页面要高效和稳定得多。注意直接请求API接口虽然高效但接口参数往往包含加密的令牌token或签名sign这些需要从首页或列表页的JavaScript代码或网络请求中逆向分析得出。这是本项目的一个技术难点也是反爬对抗的核心。2.2 数据解析与提取JsonPath 与 正则表达式由于目标数据是JSON格式使用JsonPath来提取数据比传统的XPath或BeautifulSoup更为直观和方便。例如要提取所有评论的用户昵称JsonPath表达式可能类似于$.data.reviews[*].userName。对于少量嵌套在HTML片段中的信息如评论正文中可能包含的标签则辅以正则表达式进行清洗。2.3 数据清洗与处理Pandas 为核心这是将“原始数据”转化为“可用数据”的关键一步。Pandas是Python数据分析的事实标准其DataFrame结构非常适合处理表格型数据。在本项目中数据清洗主要包括以下几个方面缺失值处理用户可能未填写“人均消费”或者“推荐菜”信息为空。需要根据分析目标决定是填充如用均值、插值还是删除。格式标准化从网页爬取的“人均消费”可能是字符串“150”需要提取数字并转换为整型“评分”可能是字符串“4.5”需转为浮点型。文本清洗评论正文可能包含无关的HTML标签、特殊字符、表情符号Emoji、多余的空格和换行符。需要使用字符串方法和正则表达式进行清理。数据去重由于网络重试或分页逻辑可能导致的重复记录需要根据唯一标识如评论ID进行去重。异常值检测与处理例如出现人均消费为0元或99999元的明显异常数据需要结合业务逻辑进行筛选或修正。2.4 数据存储CSV 与 SQLite对于中小规模的数据分析和项目演示CSV文件是最简单通用的交换格式。使用Pandas的to_csv()方法可以轻松将DataFrame保存为CSV文件。如果数据量较大或需要更复杂的查询我会将数据存入SQLite数据库。SQLite是一个轻量级的文件数据库无需安装服务器通过Python标准库sqlite3即可操作非常适合作为桌面级数据分析项目的存储后端。2.5 分析报告与可视化Matplotlib/Seaborn 与 Jupyter Notebook数据分析的结果需要直观地呈现。Matplotlib是基础的绘图库Seaborn基于其上提供了更美观的统计图形和更简洁的API。两者结合可以制作出专业的图表如消费价格分布直方图、评分趋势折线图、菜系占比饼图等。整个分析过程我强烈推荐在Jupyter Notebook中完成。Notebook支持交互式编程能将代码、运行结果、图表和富文本注释Markdown整合在一个文档中是生成可复现分析报告的绝佳工具。最终的分析报告可以导出为HTML或PDF与源码和数据一并打包。3. 爬虫实战突破反爬与稳健数据采集有了技术栈的蓝图我们来深入爬虫部分的具体实现。爬取大众点评的核心挑战在于其多层次的反爬策略我们的代码必须足够“友好”以模拟真人行为同时又要足够“健壮”以应对各种异常。3.1 请求头Headers的精细化伪装这是最基础也是最重要的一步。你的请求头需要看起来完全像一个真实的浏览器。这不仅仅是添加一个User-Agent那么简单。我通常会从浏览器中复制一次完整请求的所有Headers包括User-Agent: 标识浏览器和操作系统。Accept/Accept-Language/Accept-Encoding: 声明客户端接受的内容类型。Connection: 保持连接。Host: 目标主机。特别重要的Cookie: 大众点评的登录状态和风控标识大多存在于Cookie中。首次访问可能需要处理验证码或扫码登录来获取初始Cookie之后在Session中维护。对于公开数据采集有时也可以使用未登录状态但可能会遇到频次限制。Referer: 表明请求来源对于按顺序翻页的请求正确设置Referer至关重要。X-Requested-With: 如果是Ajax请求可能需要设置为XMLHttpRequest。一个配置得当的Headers字典能大幅降低被直接拒绝访问的概率。3.2 会话Session管理与Cookie持久化使用requests.Session()对象它可以自动处理Cookies使得多次请求之间保持状态就像浏览器一样。在爬虫启动时可以先访问一次大众点评首页让Session获取必要的初始Cookie。之后的所有请求都通过这个Session发出。为了应对可能的中断和重启我们需要实现Cookie的持久化。可以将Session的cookies通过pickle库保存到本地文件下次启动时再加载进来从而延续之前的会话状态避免重复登录或从头开始爬取。3.3 核心API接口的逆向与参数构造如前所述直接抓取动态加载的数据是关键。以获取某商户的评论列表为例打开目标商户页面在开发者工具Network面板中筛选XHR请求。滚动页面触发评论加载找到返回评论数据的请求通常包含“review”或“comment”字样。分析这个请求的URL、方法GET/POST以及参数Query String或Form Data。你会发现关键参数如shopId商户ID、page页码、pageSize每页数量以及一个可能随时间或会话变化的_token或sign参数。sign参数通常是其他参数加上一个密钥salt通过某种加密算法如MD5, SHA1生成的用于防止请求被篡改。你需要找到生成这个sign的JavaScript代码并用Python实现相同的算法或者更简单地直接复用浏览器运行时生成的值这要求爬虫与浏览器环境联动复杂度较高。对于学习和小规模采集有时可以观察到短时间内token的变化规律进行模拟。3.4 频率控制与异常处理毫无节制地高速请求是导致IP被封的最快途径。必须在请求间加入随机延时。import time import random def safe_request(session, url, **kwargs): time.sleep(random.uniform(1, 3)) # 随机等待1-3秒 try: resp session.get(url, **kwargs) resp.raise_for_status() # 检查HTTP状态码是否为200 return resp except requests.exceptions.RequestException as e: print(f请求失败: {url}, 错误: {e}) # 这里可以加入重试逻辑 return None此外代码必须包含完善的异常处理try...except对网络超时、连接错误、HTTP错误状态码如403禁止访问、429请求过多等情况进行捕获和记录并根据策略决定是重试、跳过还是终止爬取。3.5 数据解析与存储成功获取到JSON响应后使用json.loads()解析然后用JsonPath或直接通过字典键值对提取所需字段。将每一页的每条评论数据整理成一个字典添加到列表中。每爬完一定数量比如一页或每隔一段时间将数据列表通过Pandas转换为DataFrame并追加写入到CSV文件或SQLite数据库中实现增量存储防止程序意外崩溃导致数据全部丢失。4. 数据清洗实战从杂乱到规整爬取下来的原始数据就像刚从矿场挖出的原石充满了杂质。数据清洗的目的就是将其打磨成可供分析的璞玉。这个过程通常在Jupyter Notebook中进行便于一步步观察数据变化。4.1 加载与初步观察首先用Pandas加载爬取到的CSV文件。import pandas as pd df pd.read_csv(dianping_reviews_raw.csv) print(df.head()) # 查看前几行 print(df.info()) # 查看列信息和缺失值 print(df.describe()) # 查看数值型列的统计摘要4.2 处理缺失值使用df.isnull().sum()统计各列缺失值数量。对于“人均消费”这样的数值列如果缺失比例不高可以考虑用中位数进行填充因为中位数对异常值不敏感。if df[avg_cost].isnull().sum() 0: median_cost df[avg_cost].median() df[avg_cost].fillna(median_cost, inplaceTrue)对于“推荐菜”这类文本列缺失可以直接填充为空字符串。4.3 格式转换与标准化价格处理df[avg_cost]可能是字符串“128”。我们需要提取数字。df[avg_cost_num] df[avg_cost].str.extract(r(\d)).astype(float) # 或者更稳健的方式先替换掉非数字字符 df[avg_cost_num] df[avg_cost].replace(r[^\d.], , regexTrue).astype(float)评分处理df[taste_score]等评分可能是字符串“4.5”直接转换。score_columns [taste_score, env_score, service_score] for col in score_columns: df[col] pd.to_numeric(df[col], errorscoerce) # 转换失败设为NaN时间处理评论时间review_time可能是“2023-08-15”或“昨天 19:30”等格式。需要统一解析为datetime类型。对于相对时间如“昨天”需要编写函数进行转换或者如果分析不依赖精确日期可以暂时忽略这部分复杂处理。4.4 文本评论深度清洗评论正文content的清洗最为繁琐也最影响后续的文本分析如情感分析、关键词提取。import re def clean_text(text): if not isinstance(text, str): return # 1. 移除HTML标签 text re.sub(r[^], , text) # 2. 移除URL链接 text re.sub(rhttps?://\S, , text) # 3. 移除表情符号简单版复杂表情需更全面的范围 text re.sub(r[\U00010000-\U0010ffff], , text, flagsre.UNICODE) # 4. 移除多余空白字符包括全角空格 text re.sub(r\s, , text) text re.sub(r , , text) # 全角空格 # 5. 移除首尾空格 text text.strip() return text df[content_cleaned] df[content].apply(clean_text)4.5 去重与异常值处理去重根据评论唯一ID去重。df.drop_duplicates(subset[review_id], inplaceTrue, keepfirst)异常值对于人均消费可以设定一个合理的范围如10元到2000元超出范围的数据视为异常可以剔除或标记。reasonable_min, reasonable_max 10, 2000 df df[(df[avg_cost_num] reasonable_min) (df[avg_cost_num] reasonable_max)]清洗完成后将干净的数据保存到新的文件如dianping_reviews_cleaned.csv供后续分析使用。5. 消费分析报告生成从数据到洞察数据清洗完毕就进入了最有意思的部分——探索数据背后的故事。我们围绕“消费分析”这个主题可以从多个维度展开。5.1 基础统计分析首先对整体情况有一个把握。# 基本统计量 print(f共分析 {len(df)} 条有效评论。) print(f人均消费平均值为{df[avg_cost_num].mean():.1f} 元) print(f人均消费中位数为{df[avg_cost_num].median():.1f} 元) print(f口味评分均分{df[taste_score].mean():.2f}) print(f环境评分均分{df[env_score].mean():.2f}) print(f服务评分均分{df[service_score].mean():.2f}) # 评分分布 import matplotlib.pyplot as plt import seaborn as sns plt.figure(figsize(15, 4)) for i, col in enumerate([taste_score, env_score, service_score], 1): plt.subplot(1, 3, i) sns.histplot(df[col], bins10, kdeTrue) plt.title(f{col} Distribution) plt.tight_layout() plt.show()通过直方图我们可以直观看到评分是呈正态分布还是严重偏态例如用户倾向于打高分或低分。5.2 价格区间与消费水平分析将人均消费划分为几个区间观察不同价位段的商户数量、平均评分等。# 定义消费区间 bins [0, 50, 100, 150, 200, 300, 500, 1000, df[avg_cost_num].max()] labels [50, 50-100, 100-150, 150-200, 200-300, 300-500, 500-1000, 1000] df[cost_bin] pd.cut(df[avg_cost_num], binsbins, labelslabels, rightFalse) # 统计各区间商户数量及平均评分 cost_analysis df.groupby(cost_bin).agg( review_count(review_id, count), avg_taste_score(taste_score, mean), avg_cost(avg_cost_num, mean) ).round(2).reset_index() print(cost_analysis)这个分析可以揭示“性价比”区间。例如可能发现人均100-150元区间的商户不仅数量多而且平均口味评分最高。5.3 评分与消费的关联性分析人们通常认为“贵的就是好的”那么数据是否支持这个观点我们可以计算人均消费与各项评分的相关系数并绘制散点图观察趋势。# 计算相关系数 correlation df[[avg_cost_num, taste_score, env_score, service_score]].corr() print(correlation[avg_cost_num]) # 查看消费与各评分的相关性 # 绘制消费 vs 口味评分散点图 plt.figure(figsize(10, 6)) sns.scatterplot(datadf, xavg_cost_num, ytaste_score, alpha0.5) sns.regplot(datadf, xavg_cost_num, ytaste_score, scatterFalse, colorred) # 添加回归线 plt.title(Relationship between Average Cost and Taste Score) plt.xlabel(Average Cost (CNY)) plt.ylabel(Taste Score) plt.show()如果回归线呈轻微上升趋势说明有一定正相关但通常这个关系不会很强因为影响评分的因素很多。5.4 文本评论的情感与主题挖掘对于清洗后的评论内容content_cleaned可以进行简单的词频统计找出高频词汇。from collections import Counter import jieba # 中文分词库 # 将所有评论拼接并分词 all_text .join(df[content_cleaned].dropna().tolist()) words jieba.lcut(all_text) # 过滤掉停用词如“的”、“了”、“和”等和短词 stopwords set([的, 了, 在, 是, 我, 有, 和, 就, ...]) # 需要自定义停用词表 filtered_words [w for w in words if len(w) 1 and w not in stopwords] word_freq Counter(filtered_words).most_common(20) # 取前20个高频词 print(word_freq)高频词往往能反映用户关注的焦点如“味道”、“服务”、“环境”、“价格”、“不错”、“一般”等。更进一步可以使用snownlp或paddlehub等库进行情感分析给每条评论打上情感倾向分正面/负面然后分析不同消费区间、不同评分段的情感分布差异。5.5 时间趋势分析如果数据中包含准确的评论日期可以分析消费趋势、评分趋势随时间如月份、季度的变化。例如是否在节假日前后人均消费更高夏季对冷饮店的评分是否有提升6. 实战案例分析某商圈火锅店消费特征为了将上述流程串起来我选取了“XX市YY商圈”的所有火锅店作为分析案例。这个案例完整地走了一遍从目标定义到报告呈现的全过程。6.1 目标定义与数据采集目标分析该商圈内火锅店的整体竞争格局、价格分布、口碑差异并找出潜在的高性价比或特色店铺。采集使用爬虫以商圈名为关键词搜索筛选品类为“火锅”的商户共获取了45家有效商户。然后针对每家商户爬取其最近300条评论如果不足则全部爬取最终获得约1.2万条评论数据。6.2 数据清洗重点针对火锅店场景清洗时特别关注了“推荐菜”字段。原始数据中推荐菜可能是一个用逗号或顿号分隔的字符串。我们将其拆分为列表便于后续分析哪些菜品最受欢迎。df[recommended_dishes] df[recommended_dishes].fillna().apply(lambda x: [dish.strip() for dish in re.split(r[,、], x) if dish.strip()]) # 将所有推荐菜展开统计词频 from itertools import chain all_dishes list(chain.from_iterable(df[recommended_dishes])) dish_freq Counter(all_dishes).most_common(15)6.3 分析发现与可视化价格带分析发现该商圈火锅店人均消费主要集中在80-150元区间属于大众消费水平。其中100-120元区间的店铺数量最多且综合评分均值最高可视为“黄金价格带”。口碑两极分化有3家店口味评分平均在4.8分以上满分5分但人均消费也高于180元另有5家店评分低于3.8分且多集中在低价位80元。中间档位的店铺评分差异不大。菜品关联通过分析推荐菜词频发现“毛肚”、“虾滑”、“牛肉”、“鸭血”是提及率最高的菜品。进一步交叉分析发现高评分店铺的推荐菜中“鲜切牛肉”、“特级毛肚”等高品质食材关键词出现频率显著更高。服务与环境的影响通过相关性分析发现对于火锅店“服务评分”与“口味评分”的相关系数最高0.65远高于“环境评分”与“口味评分”的相关系数0.3。这说明在消费者感知中火锅店的服务质量如加汤及时性、响应速度极大地影响了他们对整体口味体验的评价。6.4 报告输出与建议基于以上分析在Jupyter Notebook中制作了包含图表和文字说明的完整报告并导出为HTML。报告的核心结论和建议包括对消费者建议在100-120元价位选择性价比最高关注推荐菜中包含“鲜切”、“特级”等关键词的店铺口味更有保障特别留意服务评价。对商户竞品分析低价并非竞争优势低于80元价位的店铺口碑普遍较差。建议中等价位店铺在保证核心菜品质量的同时重点提升服务水平这是拉升口碑的关键杠杆。高价位店铺需明确其高端食材或独特体验的价值主张以支撑其溢价。这个案例的完整代码、清洗后的数据和分析报告都包含在项目压缩包中。通过这个端到端的项目你不仅能学到技术更能掌握一套从数据获取到商业洞察的完整方法论。数据爬取只是起点如何清洗、如何提问、如何从数据中找到故事的线索才是更有价值的技能。本文还有配套的精品资源点击获取