资讯详情

旅游景点数据分析实战:从脏数据清洗到可视化洞察全链路

📅 2026/11/2 2:16:13 | 华诺云谱 👁 阅读
旅游景点数据分析实战:从脏数据清洗到可视化洞察全链路
简介这份资源面向希望入门数据分析实战的初学者与旅游行业从业者以去哪儿网国庆景点数据为样本完整演示从数据清洗到可视化呈现的分析流程帮助读者掌握用Python处理真实业务数据的基本方法。压缩包共7个文件约79KB包含5个html可视化页面、1个xlsx原始数据表和1个py分析脚本分别对应图表成果展示、结构化数据存储与数据预处理及绘图逻辑结构紧凑、便于对照学习。目前已有1385人学习下载。内容覆盖热力图、柱状图、饼状图等多种图表实现涉及地理信息展示、门票销量对比、景区星级占比等具体场景并串联缺失值与异常值处理、时间序列趋势观察、业务洞察提炼等环节读者可据此理解数据驱动决策的完整链路适合作为课程作业或小型分析项目的参考范例。1. 旅游景点数据分析实战从一堆脏评论到能答辩的洞察报告做旅游景点数据分析最反直觉的一点是真正花时间的不是建模而是把爬下来的评论、评分、门票、客流这些脏数据洗成能算的格式。我见过太多同学一上来就调库跑聚类结果字段类型全是字符串、缺失值占三成模型跑出来自己都不敢信。这套方案要解决的就是这个问题——把「景点数据采集 → 清洗 → 多维分析 → 可视化 → 结论输出」串成一条能复现的链路适合正在做数据分析毕设、课程设计或者要给业务方交一份景点运营分析报告的从业者。下面按需求、设计、实现、避坑、验收的顺序拆开讲每一步都给到能直接改参数复用的程度。2. 需求与技术选型先想清楚分析什么再决定用什么2.1 三类核心分析需求怎么定景点数据分析听起来宽泛落到交付物上通常收敛成三类问题先把这三类定死后面的字段设计和指标才有依据。第一类是景点热度与口碑分析哪些景点讨论量大、评分高、差评集中在哪些维度。这类需求对应的是评论表 评分字段核心指标是评论量、平均分、评分分布、差评关键词。第二类是客流与票价的关联分析票价区间和客流、评分之间有没有关系节假日和工作日的客流差异。这类需求需要门票价格、日期、客流量字段核心是分组对比和相关性。第三类是游客画像与偏好分析评论里反映出的游客关注点交通、排队、卫生、性价比以及不同评分段游客的用词差异。这类需求靠文本处理和词频统计不需要复杂模型。我一般会建议把这三类做成三个分析模块而不是混在一个大脚本里。原因是毕设答辩时评委最爱问「你这个结论是怎么来的」模块化之后每个结论都能追溯到具体的表和具体的计算步骤翻车概率低很多。2.2 技术栈选择与理由这套方案的技术栈不需要多花哨够用、能讲清楚就行。下面是我常用的组合及选择理由环节工具选择理由数据采集requests BeautifulSoup轻量字段可控适合结构化页面数据存储SQLite / MySQL毕设用 SQLite 零配置数据量大再上 MySQL数据清洗pandas缺失值、类型转换、去重一把梭文本分析jieba 词频统计中文分词成熟不需要训练模型可视化matplotlib / pyecharts静态图交报告交互图答辩演示报告输出Jupyter Notebook代码和结论同页答辩直接翻选 SQLite 而不是直接读 CSV 的原因很实际清洗过程要反复查、反复改用 SQL 做聚合比在 pandas 里写一堆 groupby 清晰得多而且答辩时能展示「数据是存在库里的」显得工程完整度高。文本分析不上 BERT 这类模型是因为景点评论的诉求是「看出游客在抱怨什么」词频加人工归类足够上大模型反而增加解释成本评委一问「你这个模型为什么这么判」就答不上来。2.3 数据字段设计字段设计是后面所有分析的地基我一般会先画一张表结构再动手写代码。核心三张表-- 景点基础表一个景点一行 CREATE TABLE spot ( spot_id INTEGER PRIMARY KEY, -- 景点唯一编号 spot_name TEXT NOT NULL, -- 景点名称 city TEXT, -- 所在城市 ticket_price REAL, -- 门票价格单位元 level TEXT -- 景区等级如 5A/4A ); -- 评论表一条评论一行是分析的主力表 CREATE TABLE review ( review_id INTEGER PRIMARY KEY, spot_id INTEGER, -- 关联景点 score REAL, -- 评分 1-5 content TEXT, -- 评论正文 review_date TEXT, -- 评论日期统一存 YYYY-MM-DD FOREIGN KEY (spot_id) REFERENCES spot(spot_id) ); -- 客流表一个景点一天一行 CREATE TABLE visitor_flow ( spot_id INTEGER, flow_date TEXT, -- 日期 visitor_cnt INTEGER, -- 当日客流量 is_holiday INTEGER -- 是否节假日 1/0 );字段设计有两个容易忽略的点一是review_date统一存成YYYY-MM-DD字符串不要存成各种格式混着来否则后面按月份聚合时全是坑二是is_holiday单独建字段而不是从日期推算因为节假日调休规则复杂手工标注一次比写推算逻辑省事。这两点看着小但直接决定后面分析能不能顺利跑通。3. 数据清洗与预处理把脏数据变成能算的表3.1 缺失值与异常值处理真实爬下来的数据缺失和异常是常态。我一般按「先看分布再定策略」的顺序处理而不是无脑 dropna。import pandas as pd import numpy as np # 读取原始评论数据 df pd.read_csv(raw_review.csv) # 第一步看缺失情况决定每列怎么处理 print(df.isnull().sum()) # 评分缺失无法用于评分分析直接删除 df df.dropna(subset[score]) # 评论正文缺失保留记录但标记评分分析仍可用 df[content] df[content].fillna() # 评分异常超出 1-5 范围的视为脏数据剔除 df df[(df[score] 1) (df[score] 5)] # 日期格式统一转成标准日期无法解析的置空后剔除 df[review_date] pd.to_datetime(df[review_date], errorscoerce) df df.dropna(subset[review_date]) df[review_date] df[review_date].dt.strftime(%Y-%m-%d) # 去重同一用户同一景点同一天重复评论只留一条 df df.drop_duplicates(subset[spot_id, content, review_date])这段代码的关键决策在缺失值策略上评分缺失必须删因为评分是核心指标评论正文缺失不能删因为这条记录的评分还有用只是文本分析时跳过。很多同学一刀切dropna()结果样本量掉一半分析结论的置信度直接崩。异常值处理同理评分超出 1-5 的先怀疑是爬虫把「评分人数」错抓成了「评分」剔除前最好抽样看几条原始记录确认原因别把正常数据误杀。3.2 文本清洗与分词评论正文是半结构化文本直接分词会混进大量符号和停用词需要先清洗再分词。import re import jieba # 加载停用词表去掉「的、了、很」这类无信息词 with open(stopwords.txt, encodingutf-8) as f: stopwords set(line.strip() for line in f) def clean_and_cut(text): # 去掉标点、数字、英文字符只保留中文 text re.sub(r[^\u4e00-\u9fa5], , text) # 精确模式分词 words jieba.lcut(text) # 过滤停用词和单字 return [w for w in words if w not in stopwords and len(w) 1] df[words] df[content].apply(clean_and_cut)清洗逻辑里len(w) 1这个过滤条件值得说一下单字词在中文里信息量极低「好」「差」单独出现说明不了什么保留下来只会污染词频统计。停用词表建议自己按景点场景补几条比如「景点」「景区」「感觉」这类词在评论里高频但无区分度加进去之后词频结果会干净很多。分词后不要急着统计先抽十条看看切分效果jieba 对景点名、地名的切分经常出错必要时用jieba.add_word()把专有名词加进词典。3.3 数据入库与一致性校验清洗完的数据写回数据库写之前做一次一致性校验避免脏数据回流。import sqlite3 conn sqlite3.connect(tourism.db) # 写入前校验评论表里的 spot_id 必须都能在景点表找到 valid_ids set(pd.read_sql(SELECT spot_id FROM spot, conn)[spot_id]) df df[df[spot_id].isin(valid_ids)] # 写入评论表 df[[review_id, spot_id, score, content, review_date]].to_sql( review, conn, if_existsappend, indexFalse ) # 校验写入结果 cnt pd.read_sql(SELECT COUNT(*) AS c FROM review, conn)[c][0] print(f评论表当前记录数{cnt})外键校验这一步很多人省掉结果后面按景点聚合时出现一堆spot_id对不上的孤儿记录图表上凭空多出几个「未知景点」。写入后打印记录数是个低成本的好习惯能第一时间发现「怎么比预期少了一半」这类问题。数据入库后后续所有分析都从库里查不再碰原始 CSV保证分析口径统一。4. 多维分析与可视化三个模块出结论4.1 景点热度与口碑分析这个模块回答「哪些景点值得重点关注」。核心是评论量、平均分、评分分布三个指标按景点聚合。# 按景点聚合评论量、平均分、差评率 sql SELECT s.spot_name, COUNT(r.review_id) AS review_cnt, ROUND(AVG(r.score), 2) AS avg_score, ROUND(SUM(CASE WHEN r.score 2 THEN 1 ELSE 0 END) * 1.0 / COUNT(r.review_id), 3) AS bad_rate FROM review r JOIN spot s ON r.spot_id s.spot_id GROUP BY s.spot_id ORDER BY review_cnt DESC hot_df pd.read_sql(sql, conn)差评率用score 2定义这个阈值可以按业务调整但一旦定了就要在报告里写清楚否则评委问「为什么 2 分算差评」会答不上来。热度分析不要只看评论量绝对值评论量高可能是景点本身大要结合差评率一起看——评论量高且差评率也高的景点才是运营真正要介入的对象。可视化上用横向条形图排评论量用散点图把评论量和平均分放一起一眼能看出「高热度低口碑」的异常点。4.2 客流与票价关联分析这个模块回答「票价和客流、口碑有没有关系」。做法是按票价分箱对比各箱的客流均值和评分均值。# 票价分箱按 50 元一档切分 bins [0, 50, 100, 150, 200, 10000] labels [0-50, 50-100, 100-150, 150-200, 200以上] spot_df pd.read_sql(SELECT * FROM spot, conn) spot_df[price_bin] pd.cut(spot_df[ticket_price], binsbins, labelslabels) # 合并客流数据后按票价档聚合 flow_df pd.read_sql(SELECT spot_id, AVG(visitor_cnt) AS avg_flow FROM visitor_flow GROUP BY spot_id, conn) merged spot_df.merge(flow_df, onspot_id) result merged.groupby(price_bin).agg( avg_flow(avg_flow, mean), spot_cnt(spot_id, count) ).reset_index()分箱的边界不是拍脑袋定的要先看票价的分布直方图尽量让每档里的景点数量别差太多否则某一档只有一两个景点均值没有代表性。相关性分析用皮尔逊系数之前先画散点图看是不是线性关系票价和客流很可能是倒 U 型太便宜人挤人、太贵人少直接算相关系数会得出「没关系」的错误结论。这个坑我在模拟项目里踩过后来养成习惯任何相关性结论出来之前先看一眼散点图。4.3 游客关注点词频分析这个模块回答「游客到底在夸什么、骂什么」。做法是把好评和差评分开做词频对比两边的用词差异。from collections import Counter # 按评分分组好评 4-5 分差评 1-2 分 good_words [w for words in df[df[score] 4][words] for w in words] bad_words [w for words in df[df[score] 2][words] for w in words] good_top Counter(good_words).most_common(20) bad_top Counter(bad_words).most_common(20) print(好评高频词, good_top) print(差评高频词, bad_top)词频结果不要直接堆进报告要做人工归类。比如差评里「排队」「人多」「等」可以归到「拥挤」维度「贵」「不值」「性价比」归到「价格」维度「脏」「卫生」「厕所」归到「卫生」维度。归类之后每个维度给个占比报告里就是「差评中拥挤类占 35%、价格类占 28%」这种能直接支撑运营建议的结论。归类这一步没有标准答案但一定要在报告里附上归类规则让结论可追溯。4.4 可视化图表选型图表选型的原则是「一张图只说一件事」。评论量排名用横向条形图评分分布用堆叠柱状图票价与客流关系用散点图加趋势线词频用词云或条形图。pyecharts 适合答辩演示时做交互matplotlib 适合导出静态图进报告。不要在一张图里塞太多维度我见过把评论量、评分、票价画在同一张双轴图里的答辩时自己都讲不清哪根柱子对应哪个轴。5. 避坑与常见问题排查五个最容易翻车的点5.1 爬虫字段错位导致评分全错现象评分分布图显示大量 5 分但差评文本里全是抱怨两者对不上。原因页面结构里评分和评分人数挨着选择器写错抓成了人数或者分页时字段顺序变了没发现。解决爬完先抽 20 条原始记录和页面人工比对确认每个字段对得上评分字段加范围校验超出 1-5 的直接报警而不是静默入库。5.2 日期格式混乱导致月度趋势断裂现象按月聚合的评论量趋势图中间断了一截或者某个月数据异常少。原因原始数据里日期有2024/1/5、2024-01-05、1月5日多种格式to_datetime解析失败被置空剔除。解决入库前统一格式解析失败的记录单独存一张异常表人工处理不要直接丢月度聚合前先GROUP BY substr(review_date, 1, 7)验证每个月都有数据。5.3 停用词表不匹配导致词频全是废话现象词频 Top20 里一半是「景点」「感觉」「真的」这类词看不出游客关注点。原因用了通用停用词表没有针对景点评论场景补充领域停用词。解决先跑一遍词频把高频但无区分度的词手工加进停用词表迭代两三轮同时把景点名、城市名也加进去避免地名霸榜。5.4 相关性结论没有散点图支撑现象报告写「票价与客流显著负相关」答辩时被问「你看过散点图吗」一看是倒 U 型。原因直接算皮尔逊系数没验证线性关系假设。解决任何相关性结论出来前先画散点图非线性关系改用分箱对比或斯皮尔曼等级相关并在报告里说明选择理由。5.5 分析脚本和数据库口径不一致现象Notebook 里算出的评论总数和数据库里查出来的对不上。原因部分分析直接读了原始 CSV部分读了数据库两边清洗规则不同。解决定一条规矩——清洗后的数据只从数据库读原始 CSV 只在清洗阶段碰每个分析模块开头打印一次数据量和预期对不上立刻停下来查。6. 验收与答辩怎么证明这套分析站得住验收这套方案我一般分三层查数据层、分析层、结论层。数据层看三张表的记录数、字段完整性、外键一致性跑一遍SELECT COUNT(*)和空值统计数字对得上才算过。分析层看每个模块的输出能不能复现——换台机器、清空缓存重跑一遍 Notebook结果一致说明没有隐藏的随机性或手工改数。结论层最容易被忽略每条写进报告的结论都要能指回具体的 SQL 或代码单元格评委随机挑一条问「这个数哪来的」你要能三秒内翻到出处。答辩时的技巧我总结成一张对照表评委常问回答要点数据哪来的、多少条说清采集范围、时间跨度、清洗后有效样本量为什么用这个词频方法说清诉求是看关注点分布词频加人工归类足够不上模型是为了可解释差评率阈值为什么是 2 分说清业务定义并说明换阈值结论是否稳健这个结论能指导什么落到具体运营动作比如「拥挤类差评占比高建议限流或分时预约」方案有什么不足主动说样本偏差、单平台数据局限比被问出来强最后一条是血泪经验主动暴露局限比藏着掖着得分高。我见过一个同学被问到数据代表性时支支吾吾其实他只要说「样本集中在节假日工作日数据偏少结论对节假日场景更可靠」就能过关。从那以后我每次带项目都要求学生在报告最后一页专门写「局限与改进方向」答辩时主动讲反而成了加分项。这套从需求到验收的链路你按自己的数据替换字段和参数就能跑起来希望帮到你。本文还有配套的精品资源点击获取
📝

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。

↑