简介:本资源是一套面向计算机专业学生与初阶开发者的大众点评评论数据采集与分析实战项目,聚焦Python爬虫、数据清洗及消费行为可视化分析全流程,特别适合作为毕业设计、课程设计或数据分析入门实践。压缩包共61个文件,含20个核心Python脚本(覆盖多线程图片爬取、OCR识别、MongoDB存取、SHAP可解释性分析等)、9个JavaScript爬虫逻辑文件(基于Puppeteer实现动态页面抓取)、4份Markdown文档(含项目介绍、配置说明与实战案例详解),以及XML/JSON/YAML等配置与工程文件,整体仅90KB,轻量易部署。已有151人下载学习,资源经多环境实测稳定运行,附完整设计文档与模块化代码结构,支持快速复现、调试与功能扩展;小白用户还可获得远程指导支持,切实降低环境配置与运行门槛。
1. 项目概述:从数据获取到商业洞察的全链路实践
最近在整理过往的数据分析项目时,翻出了一个老项目,一个关于大众点评评论数据的爬虫与分析系统。这个项目麻雀虽小,五脏俱全,完整覆盖了从数据采集、清洗、分析到报告生成的全过程。它不仅仅是一个简单的爬虫脚本,更是一个典型的数据工程与商业分析结合的实战案例。对于想从“会写Python”进阶到“能用Python解决实际问题”的朋友来说,这类项目极具参考价值。它教会你的不是某个库的API调用,而是一整套数据驱动的思维和工作流。
这个项目包的核心价值在于其“端到端”的特性。很多教程只讲爬虫,或者只讲数据分析,但真实世界的问题往往是连贯的。你需要自己想办法拿到“脏”数据,然后把它变“干净”,最后才能从中提炼出有价值的见解。这个过程里踩的坑、做的决策,比如如何应对网站的反爬机制、如何处理千奇百怪的评论文本、如何设计分析维度才能真正反映消费趋势,才是真正宝贵的经验。接下来,我就把这个项目的核心思路、关键技术和实操心得拆解开来,希望能为你构建自己的数据项目提供一个清晰的蓝图。
2. 整体架构与核心思路拆解
这个项目的目标很明确:自动化地获取指定商户或商圈在大众点评上的用户评论,经过清洗和结构化处理,最终生成一份关于消费偏好、评价趋势、竞争对比的分析报告。整个流程可以清晰地划分为三个核心阶段,它们环环相扣,缺一不可。
2.1 第一阶段:数据采集层——稳定高效的爬虫引擎
数据是分析的基石,而爬虫就是我们的“采矿机”。这一阶段的目标不仅是把数据抓下来,更要保证抓取过程的稳定性、效率和道德合规性。我们设计的爬虫需要是一个健壮的“侦察兵”,而非蛮横的“攻城锤”。
核心设计思路:
- 请求模拟与会话管理:大众点评对未登录状态和异常请求有严格检测。我们的爬虫需要模拟真实浏览器的行为,包括管理Cookies、维护会话(Session)、携带合理的请求头(User-Agent, Referer等)。使用
requests.Session()对象是基础,它能自动处理Cookies,保持登录态(如果模拟了登录)。 - 反爬策略应对:这是爬虫项目的重头戏。大众点评可能采用IP频率限制、验证码、动态参数(如
_token)等手段。- IP代理池:这是应对频率限制最有效的手段。我们需要集成一个可靠的代理IP服务,在请求失败或被封时自动切换IP。代码中会有一个代理管理器模块,负责从代理供应商API获取IP,并验证其有效性。
- 请求频率控制:绝对避免暴力请求。必须在请求间加入随机延时(例如
time.sleep(random.uniform(2, 5))),模拟人类阅读速度。更精细的做法是监控服务器响应,如果返回特定错误码,则自动延长休眠时间。 - 动态参数解析:部分数据(尤其是AJAX接口返回的JSON)可能需要从页面HTML中先提取一个加密参数或令牌。这需要分析网页JavaScript逻辑,用Python(如
re、pyquery/BeautifulSoup)解析出来,再拼接到后续请求中。
- 数据解析与容错:页面结构可能变动。解析代码不能写死,要有一定的容错性。使用
BeautifulSoup或lxml进行HTML解析时,要采用相对稳健的选择器,并做好try-except处理,确保某条数据解析失败时,不影响其他数据和整体流程。 - 增量爬取:对于需要长期监控的商户,我们不应每次都全量爬取。爬虫应记录已爬取评论的ID,每次只抓取新出现的评论。这需要在数据库或本地文件中维护一个已爬ID集合。
注意:务必严格遵守网站的
robots.txt协议,并将请求频率控制在极低水平,避免对目标网站服务器造成压力。本项目的代码应仅用于学习和技术交流目的。
2.2 第二阶段:数据清洗与预处理——将“原料”转化为“标准件”
爬取下来的原始数据是“脏”的,包含大量噪声,无法直接用于分析。这一阶段就像食品加工中的“清洗、切配”,目的是生产出干净、结构化的数据。
核心处理流程:
- 缺失值处理:用户可能没有填写“人均消费”或“评分”。对于数值型数据(如人均消费),可以采用中位数或同类商户均值进行填充;对于分类数据(如“推荐菜”),则标记为“未知”或直接留空。
- 异常值检测与处理:比如出现人均消费“0元”或“99999元”,评分“0分”或“6分”。需要通过箱线图(IQR方法)或标准差(Z-score)方法识别这些异常值,并根据业务逻辑决定是修正、填充还是剔除。
- 文本数据清洗:这是评论数据的核心。
- 去噪:去除无关字符(如HTML标签、特殊符号@#¥%、表情符号的乱码)。
- 标准化:全角转半角,英文大小写统一。
- 分词与词性标注:使用
jieba分词库对中文评论进行分词,并过滤掉停用词(如“的”、“了”、“和”等无实义的词)。这一步是为后续的情感分析和主题挖掘做准备。 - 语义归一化:将表达相同意思的不同词语归一化,例如将“好吃”、“美味”、“很棒”统一为“正面口味”。
- 数据格式化与类型转换:将字符串类型的“人均消费”(如“¥150”)转换为整数
150;将“评分”从字符串转换为浮点数;将“评论时间”从“2023年10月1日”的字符串转换为Python的datetime对象。
2.3 第三阶段:数据分析与可视化——挖掘数据背后的故事
拥有干净数据后,我们就可以开始“烹饪”了。分析的目标是回答商业问题,例如:“这家店的优缺点是什么?”、“顾客消费的核心关注点有哪些?”、“和周边竞争对手比,它的竞争力如何?”
核心分析维度:
- 描述性统计分析:
- 评分分布:计算平均分、中位数、众数,绘制评分分布的直方图或箱线图,了解整体口碑水平。
- 人均消费分析:计算消费均值、区间分布(例如0-50, 50-100, 100-200, 200+),了解店铺的价格定位。
- 评论时间趋势:按日、周、月聚合评论数量,绘制折线图,观察口碑变化趋势,是否与促销活动、节假日相关。
- 文本挖掘与情感分析:
- 词频分析与词云:对清洗后的评论分词进行词频统计,生成词云图,直观展示顾客讨论最多的关键词(如“服务”、“环境”、“口味”、“价格”)。
- 情感倾向判断:使用预训练的情感词典(如知网Hownet、BosonNLP情感词典),为每条评论计算情感得分(正面、负面、中性)。可以统计情感分布,并观察情感分与评分的关系。
- 主题模型(LDA):如果评论量巨大,可以使用无监督学习的LDA模型,自动发现评论中隐含的主题簇,例如“等位问题”、“菜品推荐”、“环境吐槽”等,这比单纯看词频更能深入理解顾客反馈的结构。
- 竞争对比分析(如果爬取了多家商户):
- 横向对比多家商户在平均评分、人均消费、服务/口味/环境细分评分、评论情感倾向等关键指标上的差异。
- 使用雷达图或多指标条形图进行可视化,竞争力强弱一目了然。
报告生成:最后,使用Jupyter Notebook、Python-docx或Jinja2模板+WeasyPrint等方式,将上述分析图表和结论整合成一份结构化的PDF或HTML报告,实现从数据到见解的自动化交付。
3. 核心模块技术实现详解
3.1 爬虫引擎的实现细节与避坑指南
爬虫模块是整个项目的基础,其健壮性直接决定了后续数据的质量和数量。这里以爬取单个商户评论列表页为例,拆解关键代码和思路。
1. 请求头与会话初始化
import requests import time import random from fake_useragent import UserAgent class DazhongDianpingSpider: def __init__(self): self.session = requests.Session() # 使用动态生成的User-Agent self.ua = UserAgent() # 基础请求头,尽可能模拟浏览器 self.headers = { 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8', 'Accept-Language': 'zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2', 'Connection': 'keep-alive', 'Upgrade-Insecure-Requests': '1', } # 代理IP池(示例,实际需从服务商API获取) self.proxy_pool = ['http://ip1:port', 'http://ip2:port'] self.current_proxy_index = 0 def get_random_headers(self): """生成随机的请求头""" headers = self.headers.copy() headers['User-Agent'] = self.ua.random return headers def get_proxy(self): """获取一个代理IP,实现简单轮询""" proxy = self.proxy_pool[self.current_proxy_index] self.current_proxy_index = (self.current_proxy_index + 1) % len(self.proxy_pool) return {'http': proxy, 'https': proxy}要点:User-Agent一定要随机化并常用更新。Accept-Language等字段能增加请求的真实性。
2. 页面请求与解析函数
from bs4 import BeautifulSoup import re def fetch_page(self, url, max_retries=3): """带重试和代理切换的页面请求函数""" for attempt in range(max_retries): try: headers = self.get_random_headers() proxies = self.get_proxy() if self.proxy_pool else None # 关键:添加随机延时,建议在2-5秒以上 time.sleep(random.uniform(3, 6)) resp = self.session.get(url, headers=headers, proxies=proxies, timeout=10) resp.raise_for_status() # 检查HTTP状态码 # 检查返回内容是否包含反爬提示(如验证码、安全拦截) if "验证码" in resp.text or "security" in resp.text.lower(): print(f"触发反爬,当前代理:{proxies}, 暂停一段时间") time.sleep(60) # 长时间休眠 continue # 换代理重试 return resp.text except (requests.exceptions.RequestException, requests.exceptions.ProxyError) as e: print(f"请求失败 (尝试 {attempt+1}/{max_retries}): {e}") if attempt < max_retries - 1: time.sleep(5 ** attempt) # 指数退避策略 else: print(f"URL {url} 抓取彻底失败") return None def parse_comments_from_html(self, html): """从HTML中解析评论列表""" if not html: return [] soup = BeautifulSoup(html, 'lxml') comment_list = [] # 根据实际页面结构查找评论项,这里的选择器是示例 comment_items = soup.select('div.review-words') # 需替换为实际选择器 for item in comment_items: try: comment = {} # 用户名 user_elem = item.select_one('a.name') comment['user_name'] = user_elem.get_text(strip=True) if user_elem else '' # 评分(通常用class表示,如“sml-rank-stars star50”表示5星) star_class = item.select_one('.sml-rank-stars')['class'][-1] if item.select_one('.sml-rank-stars') else '' comment['rating'] = self._star_class_to_score(star_class) # 自定义转换函数 # 评论正文 content_elem = item.select_one('.review-words') # 处理“展开评论”的情况 full_text = content_elem.get_text(separator=' ', strip=True) if content_elem else '' comment['content'] = re.sub(r'收起评论|展开评论', '', full_text) # 人均消费(可能不存在) cost_elem = item.select_one('.per-capita .num') comment['avg_cost'] = int(cost_elem.text) if cost_elem else None # 评论时间 time_elem = item.select_one('.time') comment['comment_time'] = time_elem.get('title') if time_elem else '' # 有时时间在title属性里 comment_list.append(comment) except Exception as e: print(f"解析单条评论时出错: {e}") continue # 跳过这条,继续解析下一条 return comment_list @staticmethod def _star_class_to_score(star_class): """将星星的CSS类名转换为分数(1-5)""" mapping = {'star50':5, 'star45':4.5, 'star40':4, 'star35':3.5, 'star30':3, 'star25':2.5, 'star20':2, 'star15':1.5, 'star10':1} return mapping.get(star_class, None)避坑指南:
- 选择器脆弱性:网站前端改版是常态。不要用过于复杂或绝对的位置选择器。优先使用具有明确语义的
class或id,并定期检查爬虫是否还能正常工作。 - 数据不全:评论可能被折叠。上述代码只获取了第一屏的文本。有些网站会通过AJAX加载完整评论,这就需要分析其网络请求,找到加载完整内容的API接口,直接请求JSON数据,这比解析HTML更稳定。
- 编码问题:确保请求和解析时使用正确的字符编码(通常是UTF-8),
resp.encoding = 'utf-8'或让BeautifulSoup自动检测。
3.2 数据清洗的Pandas实战技巧
清洗阶段我们主要依赖pandas。假设我们已经将爬取的原始数据保存为CSV文件raw_comments.csv。
import pandas as pd import numpy as np import jieba import jieba.analyse from datetime import datetime # 1. 加载数据 df = pd.read_csv('raw_comments.csv') # 2. 查看基本信息与缺失值 print(df.info()) print(df.isnull().sum()) # 3. 处理缺失值 # 人均消费用中位数填充(假设缺失是随机的) if 'avg_cost' in df.columns: df['avg_cost'].fillna(df['avg_cost'].median(), inplace=True) # 评分缺失极少,若有则直接删除该行 df.dropna(subset=['rating'], inplace=True) # 4. 处理异常值 # 假设人均消费大于1000或小于5为异常 if 'avg_cost' in df.columns: q1 = df['avg_cost'].quantile(0.25) q3 = df['avg_cost'].quantile(0.75) iqr = q3 - q1 lower_bound = q1 - 1.5 * iqr upper_bound = q3 + 1.5 * iqr # 将异常值替换为上下边界值(或删除) df['avg_cost'] = df['avg_cost'].clip(lower_bound, upper_bound) # 5. 文本清洗函数 def clean_text(text): if not isinstance(text, str): return '' # 去除HTML标签(如果爬虫没处理干净) text = re.sub(r'<[^>]+>', '', text) # 去除网址、@提及等 text = re.sub(r'http\S+|@\w+', '', text) # 去除特殊符号和数字(根据分析目标决定是否保留) text = re.sub(r'[^\w\u4e00-\u9fff,。!?、]+', ' ', text) # 保留中文、常见标点 # 去除多余空白 text = re.sub(r'\s+', ' ', text).strip() return text df['content_cleaned'] = df['content'].apply(clean_text) # 6. 分词与关键词提取(为后续分析准备) def extract_keywords(text, topK=10): if not text: return [] # 基于TF-IDF提取关键词 keywords = jieba.analyse.extract_tags(text, topK=topK, withWeight=False) return keywords # 可以新增一列存储每条评论的关键词 df['keywords'] = df['content_cleaned'].apply(lambda x: extract_keywords(x, topK=5)) # 7. 时间字段格式化 if 'comment_time' in df.columns: # 尝试解析多种时间格式 df['comment_time_parsed'] = pd.to_datetime(df['comment_time'], errors='coerce') # 删除无法解析的时间行 df.dropna(subset=['comment_time_parsed'], inplace=True) # 8. 保存清洗后的数据 df.to_csv('cleaned_comments.csv', index=False, encoding='utf-8-sig')实操心得:
clip()函数是处理数值型异常值的好帮手,比直接删除更能保留数据规模。- 文本清洗的正则表达式需要根据数据实际情况反复调整。可以先抽样查看脏数据的样子,再针对性编写规则。
pd.to_datetime的errors='coerce'参数非常有用,它会把解析失败的时间变成NaT(Not a Time),方便后续统一处理。- 清洗步骤最好写成函数或管道(Pipeline),方便复用和测试。
3.3 消费分析与报告生成的核心代码
清洗完成后,我们进入分析阶段。这里使用pandas、matplotlib和seaborn进行数据分析和可视化。
import matplotlib.pyplot as plt import seaborn as sns plt.rcParams['font.sans-serif'] = ['SimHei'] # 用来正常显示中文标签 plt.rcParams['axes.unicode_minus'] = False # 用来正常显示负号 sns.set_style("whitegrid") # 加载清洗后的数据 df = pd.read_csv('cleaned_comments.csv', parse_dates=['comment_time_parsed']) # 1. 描述性统计 print("=== 基本统计 ===") print(f"总评论数: {len(df)}") print(f"平均评分: {df['rating'].mean():.2f}") print(f"评分中位数: {df['rating'].median():.2f}") if 'avg_cost' in df.columns: print(f"人均消费中位数: {df['avg_cost'].median():.0f} 元") # 2. 评分分布可视化 plt.figure(figsize=(10, 6)) # 使用Seaborn的countplot,按0.5分间隔 rating_order = sorted(df['rating'].unique()) sns.countplot(data=df, x='rating', order=rating_order, palette='viridis') plt.title('用户评分分布') plt.xlabel('评分') plt.ylabel('评论数量') plt.tight_layout() plt.savefig('rating_distribution.png', dpi=300) plt.show() # 3. 人均消费区间分析 if 'avg_cost' in df.columns: df['cost_bin'] = pd.cut(df['avg_cost'], bins=[0, 50, 100, 150, 200, df['avg_cost'].max()+1], labels=['0-50', '50-100', '100-150', '150-200', '200+']) cost_dist = df['cost_bin'].value_counts().sort_index() plt.figure(figsize=(8, 8)) plt.pie(cost_dist.values, labels=cost_dist.index, autopct='%1.1f%%', startangle=90) plt.title('人均消费区间占比') plt.savefig('cost_distribution_pie.png', dpi=300) plt.show() # 4. 评论时间趋势(按月) df['year_month'] = df['comment_time_parsed'].dt.to_period('M') monthly_trend = df.groupby('year_month').size() monthly_trend.index = monthly_trend.index.astype(str) # 转换为字符串便于绘图 plt.figure(figsize=(12, 5)) monthly_trend.plot(kind='line', marker='o') plt.title('月度评论数量趋势') plt.xlabel('年月') plt.ylabel('评论数') plt.xticks(rotation=45) plt.grid(True, linestyle='--', alpha=0.7) plt.tight_layout() plt.savefig('monthly_trend.png', dpi=300) plt.show() # 5. 文本分析 - 词云生成 from wordcloud import WordCloud from collections import Counter # 将所有清洗后的评论合并成一个长文本 all_text = ' '.join(df['content_cleaned'].dropna().astype(str)) # 分词并统计词频 words = jieba.lcut(all_text) # 加载停用词表 stopwords = set([line.strip() for line in open('stopwords.txt', 'r', encoding='utf-8')]) filtered_words = [w for w in words if w not in stopwords and len(w) > 1] word_freq = Counter(filtered_words) # 生成词云 wc = WordCloud(font_path='simhei.ttf', width=800, height=400, background_color='white', max_words=100) wc.generate_from_frequencies(word_freq) plt.figure(figsize=(12, 6)) plt.imshow(wc, interpolation='bilinear') plt.axis('off') plt.title('评论高频词词云') plt.tight_layout() plt.savefig('wordcloud.png', dpi=300) plt.show() # 6. 简单情感分析(基于词典的简单示例) # 这里需要准备正面和负面情感词典 positive_words = set(['好', '不错', '喜欢', '满意', '赞', '推荐', ...]) negative_words = set(['差', '不好', '失望', '不行', '贵', '慢', ...]) def simple_sentiment(text): pos_count = sum(1 for word in jieba.lcut(text) if word in positive_words) neg_count = sum(1 for word in jieba.lcut(text) if word in negative_words) if pos_count > neg_count: return '正面' elif neg_count > pos_count: return '负面' else: return '中性' df['sentiment'] = df['content_cleaned'].apply(simple_sentiment) sentiment_dist = df['sentiment'].value_counts() print("\n=== 情感分布 ===") print(sentiment_dist)报告生成:可以将上述所有图表和关键统计量,用Jinja2模板引擎渲染到一个HTML报告中,再转换为PDF。
from jinja2 import Environment, FileSystemLoader import pdfkit # 需要安装wkhtmltopdf # 准备模板数据 report_data = { 'shop_name': '示例餐厅', 'total_comments': len(df), 'avg_rating': df['rating'].mean(), 'median_cost': df['avg_cost'].median() if 'avg_cost' in df.columns else None, 'rating_chart': 'rating_distribution.png', 'cost_chart': 'cost_distribution_pie.png', 'trend_chart': 'monthly_trend.png', 'wordcloud_img': 'wordcloud.png', 'sentiment_data': sentiment_dist.to_dict(), 'top_keywords': word_freq.most_common(20) } # 渲染HTML env = Environment(loader=FileSystemLoader('.')) template = env.get_template('report_template.html') html_out = template.render(**report_data) with open('analysis_report.html', 'w', encoding='utf-8') as f: f.write(html_out) # 转换为PDF (可选,需配置wkhtmltopdf路径) # config = pdfkit.configuration(wkhtmltopdf=r'C:\Program Files\wkhtmltopdf\bin\wkhtmltopdf.exe') # pdfkit.from_file('analysis_report.html', 'analysis_report.pdf', configuration=config)4. 实战案例:分析某商圈火锅店竞争力
为了让你更有体感,我分享一个真实的简化版案例:分析某城市一个热门商圈内5家头部火锅店的线上口碑。
目标:找出各家优劣势,为消费者选择和新店定位提供数据参考。
步骤与发现:
- 数据获取:使用上述爬虫,分别爬取5家店最近1000条有效评论。关键字段:评分、人均消费、评论内容、时间。
- 统一清洗:对5份数据应用相同的清洗流程,确保可比性。
- 对比分析:
- 评分与价格:制作散点图,X轴为人均消费,Y轴为平均评分,气泡大小代表评论数。一眼就能看出谁是“性价比之王”(高分低价),谁是“品质标杆”(高分高价),谁是“需要避坑”(低分高价)。
- 口碑词云对比:将5家店的词云并列。A店词云中心是“服务”、“贴心”、“等位”,说明服务是其核心优势;B店则是“麻辣”、“过瘾”、“新鲜”,强调口味;C店出现了“贵”、“不值”,提示可能存在价格争议。
- 细分评分趋势:如果数据中有“口味”、“环境”、“服务”的细分评分,可以绘制分组柱状图。发现D店口味分很高,但服务分拖了后腿,改进服务可能是其提升排名的捷径。
- 时间序列洞察:观察各家店月度评分变化。发现E店在推出新菜品套餐的当月,评分有明显提升,但次月回落,说明营销活动短期有效,但产品力可能未完全留住顾客。
最终产出:一份约10页的PDF报告,包含上述所有图表和简明结论。例如:“A店以服务见长,适合家庭聚餐;B店是重口味爱好者的首选,但价格不菲;C店需审视其定价策略...”
这个案例的价值在于,它将零散的评论数据,转化为了结构化的、可行动的商业洞察。这套方法不仅适用于餐饮,稍加调整,便可应用于酒店、旅游景点、教育培训等任何依赖线上口碑的行业。
5. 常见问题与排查技巧实录
在开发和运行这个项目的过程中,我遇到了不少典型问题。这里列出一个速查表,希望能帮你节省时间。
| 问题现象 | 可能原因 | 排查与解决思路 |
|---|---|---|
| 爬虫返回空数据或403错误 | 1. 请求头被识别为爬虫。 2. IP被限制或封禁。 3. 目标页面是JavaScript动态渲染。 | 1. 检查并完善请求头,特别是User-Agent、Referer。2. 立即启用代理IP池,并降低请求频率。 3. 使用浏览器开发者工具(F12)的“网络”选项卡,查找数据真正的AJAX API接口,直接请求JSON数据。 |
解析时BeautifulSoup找不到元素 | 1. 网页结构已更新。 2. 选择器写错或过于具体。 3. 数据在 iframe或Shadow DOM内。 | 1. 重新检查页面HTML结构,更新CSS选择器或XPath。 2. 使用更通用、层级更高的选择器,或结合多个属性定位。 3. 对于动态加载内容,需用 Selenium或Playwright等浏览器自动化工具。 |
| 数据中有大量乱码或问号“?” | 字符编码不一致。 | 1. 在请求后设置resp.encoding = 'utf-8'(或gbk等)。2. 用 chardet库检测编码:resp.encoding = chardet.detect(resp.content)['encoding']。 |
pandas读取CSV时中文乱码 | CSV文件保存的编码与读取时指定编码不一致。 | 写入时用df.to_csv('file.csv', index=False, encoding='utf-8-sig'),读取时用pd.read_csv('file.csv', encoding='utf-8-sig')。utf-8-sig能更好处理Excel打开时的BOM头。 |
| 情感分析结果不准 | 使用的词典过于简单或领域不匹配。 | 1. 尝试使用更成熟的情感分析库,如SnowNLP(针对中文)、TextBlob(英文)。2. 对于垂直领域(如餐饮),可以手动构建或扩充领域情感词典,例如加入“鲜嫩”、“爽滑”为正面,“油腻”、“齁咸”为负面。 |
| 生成图表时中文显示为方框 | matplotlib默认字体不包含中文。 | 在绘图前添加以下代码:plt.rcParams['font.sans-serif'] = ['SimHei', 'Microsoft YaHei']plt.rcParams['axes.unicode_minus'] = False |
| 爬虫运行一段时间后速度变慢甚至卡死 | 1. 代理IP质量下降。 2. 未及时释放连接,内存泄漏。 3. 网站反爬升级。 | 1. 集成代理IP有效性验证模块,定期剔除失效IP。 2. 使用 with requests.Session() as s:上下文管理器,或定期重置Session。3. 增加更复杂的反反爬策略,如模拟鼠标移动轨迹(需用 Selenium)。 |
最后的建议:这类数据项目,最难的不是写代码,而是应对变化。网站会改版,反爬策略会升级。因此,代码的模块化和日志记录至关重要。将爬虫、清洗、分析各自独立成模块,并记录详细的运行日志(用了哪个IP、爬了哪一页、遇到了什么错误),这样当问题出现时,你才能快速定位和修复。把这个项目当作一个不断需要维护的系统,而不是一劳永逸的脚本,你的工程能力会在这个过程中得到真正的锻炼。
本文还有配套的精品资源,点击获取