YAOTU INSIGHTS

Python旅游网站数据分析与可视化:从数据清洗到答辩高分指南

Python旅游网站数据分析与可视化:从数据清洗到答辩高分指南
简介一份基于Python的旅游网站数据分析及可视化完整项目以2020年五一旅游数据为案例面向需要完成数据采集、清洗、分析与可视化大作业的高校学生。项目源码评审分超过95分经过严格调试可运行适合数据分析初学者参考。压缩包共74个文件、大小13.62MB包含3个Python脚本、3个ipynb分析笔记、36个html可视化页面、1个csv旅游数据集并附docx说明文档及js/css静态资源覆盖数据处理、图表生成到页面展示的完整流程已有616人学习下载。通过该项目可掌握从数据清洗到可视化展示的完整分析流程项目目录按数据、脚本、模板、静态资源分层便于逐模块学习与二次开发内置词云图、水球图、漏斗图、地图等多种可视化图表其中景点评价率水球图、热门景点词云图、门票销售漏斗图等案例均可迁移至其他城市或领域快速搭建同类旅游数据分析系统无论用于课程设计、期末答辩还是个人作品集都能获得直接可用的源码与参考。1. 期末大作业选题旅游网站数据分析与可视化到底在考什么“基于Python的旅游网站数据分析及可视化”这类期末大作业听起来像是个标准模板——数据好找、工具成熟、可视化库一抓一大把可实际交上去能拿95分的很少。原因在于这门课考核的不是你会不会调plot而是你有没有把一个数据分析项目完整走通从数据获取、清洗、分析到图表呈现最后落成一份能说服人的业务结论。我见过太多人卡在数据阶段就花了三天或者画出一堆图却完全答不上“为什么用折线不用柱状”。这门题适合刚学完 Python 基础、想找一份数据分析项目实践练手、并且期末想冲高分的人。接下来我把完整套路拆给你照着做至少能避开八成翻车点。另外拿到的.zip先别急着解压跑代码看一遍 README 和目录结构能省不少时间。2. 数据准备阶段从抓包到清洗三条路径与一套 Pandas 模板2.1 先看包里有什么README、数据集与代码的目录结构拿到一个.zip第一件事不是双击解压然后试图直接运行而是先看压缩包里的文件清单。期末大作业的压缩包通常会有这几类东西README 说明、数据文件CSV 或 JSON、脚本或 Notebook、报告目录。README 里一般会写清楚数据来源、字段含义、Python 版本要求如果作者没写你就得自己做“考古”——用 Excel 打开数据文件先看列名再找有没有requirements.txt。这一步的意义在于确定后续所有分析都围绕同一套字段展开避免代码写到一半发现列名对不上。我一般会在项目根目录建一个数据说明.txt把字段名、类型、时间格式、哪些列有空值记下来后面写清洗和分析代码时直接对照能减少接近三分之一的无意义返工。如果你发现压缩包里只有代码没有数据别慌看下一节数据来源不只一条路。2.2 数据获取的三条路径公开接口、开源数据集与“结构化模拟”如果 ZIP 里没给现成数据你需要自己解决数据来源。期末大作业场景下三条路我都走过经验如下第一是抓包找公开接口。主流旅游网站的景点页面、点评列表前端几乎都是异步加载 JSON打开浏览器开发者工具切到 Network 面板刷新页面就能看到一批 XHR 请求里面往往直接返回结构化的 JSON 数据。用requests拉下来再用pandas解析就行。但爬虫要考虑请求频率、登录态、反爬策略时间成本高适合数据量小、结构清晰的场景。第二是去 GitHub 找公开旅游数据集。很多开源项目整理了景点评分、游客量、门票价格这类结构化数据字段质量和真实业务几乎一致优势是省时间缺点是需要花一点时间筛选字段口径。第三是老师只给了题目、没给数据的情况。常见做法是按真实业务字段结构生成一份仿真样本数据例如城市、景点、评分、评论数、门票价格、月份、游客量。答辩时明确说明这是按公开统计口径构造的演示数据用于验证分析流程合理性。这不算造假只要诚实标注老师通常能接受。# 路径一请求公开 JSON 接口注意遵守目标站点协议仅限学习使用 import requests import pandas as pd # headers {User-Agent: Mozilla/5.0} # 基本浏览器标识 # resp requests.get(https://api.example.com/scenic/list, headersheaders, timeout10) # data resp.json() # df pd.json_normalize(data[data][list]) # 更稳的路径二直接读本地 CSV df pd.read_csv(tourism_data.csv, encodingutf-8) print(df.head()) print(df.dtypes) # 检查字段类型是清洗的第一步这段代码里requests.get的timeout10很关键没有超时控制遇到慢接口会一直挂住pd.json_normalize能把嵌套 JSON 展平成表格比手动循环json.loads省事得多。实际大作业里我建议优先走 CSV 或 Excel 数据因为评分和游客量字段可以直接用 Pandas 处理不用处理嵌套结构。2.3 数据清洗去重、补缺失、时间格式化与异常值过滤无论数据从哪来清洗都是最耗时、也最能体现基本功的环节。四个动作是必须做的按业务唯一键去重、用中位数或众数填充缺失值、把时间字段统一成datetime类型、过滤明显不合理的异常值。很多人跳过去重直接画图结果趋势线出现双倍峰值答辩时被问住非常尴尬。clean df.copy() # 1. 去重三个字段同时相同才算重复行 clean.drop_duplicates(subset[city, attraction, month], inplaceTrue) # 2. 缺失值评分用中位数填充价格缺省按 0 处理 clean[rating] clean[rating].fillna(clean[rating].median()) clean[price] clean[price].fillna(0) # 3. 时间统一字符串 2024-01 转成月度时间对象 clean[month] pd.to_datetime(clean[month], format%Y-%m) # 4. 异常值过滤游客量为负或单月超千万的删掉 clean clean[(clean[visitors] 0) (clean[visitors] 1000)] # 清洗结果单独存一份后续所有脚本都从这份读入 clean.to_csv(clean_tourism.csv, indexFalse, encodingutf-8-sig) print(clean.info())drop_duplicates里的subset参数指定了“哪几列一致才算重复”不用它的话只要有一列相同就会被删。fillna用中位数而不是均值是因为评分字段往往有偏态分布均值容易被极端值拉偏。to_datetime的format%Y-%m指定了解析格式不写它也能猜但遇到 “2024/01” 这类变体会解析失败。最后utf-8-sig是为了让 Excel 打开 CSV 时不乱码具体原因在避坑章节细说。3. 分析维度拆解游客量、评分与价格的四个核心口径3.1 月度游客量趋势先聚合再画图别拿明细直接上折线拿到clean_tourism.csv之后最容易犯的错是直接拿明细数据去画折线。每一行是一个景点的月度记录几百条线叠在一起画出来的图跟毛线团一样答辩老师一眼就看出你没思路。正确做法是按月度汇总游客量再做趋势分析。trend clean.groupby(clean[month].dt.to_period(M))[visitors].sum().reset_index() trend[month] trend[month].astype(str) # Period 对象转字符串方便画图 print(trend.head())dt.to_period(M)把datetime截断到月份groupby后sum()得到每月总游客量。注意reset_index()不能省否则month会留在索引里后面画图时x轴取不到。这里先不急着画聚合结果还可以继续做环比计算trend[visitors].pct_change()能直接算月度环比增速写报告时一句“暑期环比增长 42%”比放十张图都有说服力。3.2 热门目的地排名评论数、游客量与评分的交叉解读排名是大作业里的必做项但只排一个指标太单薄。常见做法是同时算三份排名按游客量、按评论数、按平均评分然后把它们放在一起交叉解读。你会发现有意思的业务结论游客量最高的城市评分不一定最高评论数很多但评分偏低的景点很可能是服务没跟上人流。top_visitors clean.groupby(city)[visitors].sum().sort_values(ascendingFalse).head(10) top_reviews clean.groupby(city)[reviews].sum().sort_values(ascendingFalse).head(10) top_rating clean.groupby(city)[rating].mean().sort_values(ascendingFalse).head(10) print(top_visitors) print(top_reviews) print(top_rating)这三个groupby的差异点在于聚合函数游客量和评论数用sum评分用mean。评分聚合后要留意样本量某个城市只有一条评论且打了 5 分它就会排到第一这不代表真实水平。稳妥做法是加一个过滤条件只保留评论数超过一定阈值的城市再算均分代码里加一句clean clean[clean[reviews] 50]就行。3.3 门票价格与满意度箱线图与分组均值价格和满意度之间的关系是最容易出彩的分析点。常见做法是把价格切成几个区间对比各区间内的平均评分。这里有一个 Pandas 2.x 用户容易踩的坑pd.cut分箱后groupby默认会忽略空箱需要加observedTrue。bins [0, 50, 100, 200, 500] labels [0-50, 50-100, 100-200, 200以上] clean[price_band] pd.cut(clean[price], binsbins, labelslabels) band_stats clean.groupby(price_band, observedTrue)[rating].mean().reset_index() print(band_stats)pd.cut的bins定义了价格边界labels是每个区间显示的名字。observedTrue在 Pandas 2.0 之后是必须的否则会出现一排 NaN 的警告。分析结论通常是中低价位景区的满意度反而高于高价景区这背后的业务解释可以是“高价景区承载量过大体验下降”。写报告时把这个结论放在图旁边比单纯放一张箱线图更显思考深度。3.4 评论数与游客量的相关性先做正态检验再选相关系数很多大作业喜欢算相关性但直接df.corr()就算完忽略了一个前提条件Pearson 相关系数要求变量近似正态分布。评论数和游客量这类业务数据几乎都是右偏分布用 Pearson 会低估相关强度正确做法是先做正态性检验再决定用什么系数。from scipy import stats # 先检验评分字段是否正态 shapiro stats.shapiro(clean[rating].dropna()) if shapiro.pvalue 0.05: corr clean[[rating, reviews]].corr(methodpearson) else: corr clean[[rating, reviews]].corr(methodspearman) print(corr)stats.shapiro返回(统计量, p值)p 值大于 0.05 认为数据是正态的。业务数据大多落在 else 分支所以methodspearman是更常见的组合。另外注意corr()只对数值列生效如果city列混了进来会报错所以要先取[rating, reviews]两列。报告中写清楚“评分与评论数呈弱正相关Spearman r0.32”其中方法名、系数、样本量三要素齐备评分自然不会低。4. 可视化落地Matplotlib 静态图与 Pyecharts 大屏的搭配方案4.1 Matplotlib 出论文级静态图字体、配色与分辨率期末大作业的推荐策略是静态图放在报告正文里交互图放在附录或演示 HTML 里。Matplotlib 负责前者三个参数是隐形扣分点中文字体、负号显示、保存分辨率。没有配置中文字体所有标签都会变成方块这在后面避坑章节会细讲。import matplotlib.pyplot as plt plt.rcParams[font.sans-serif] [SimHei] # 中文字体 plt.rcParams[axes.unicode_minus] False # 负号正常显示 fig, ax plt.subplots(figsize(10, 5)) ax.plot(trend[month], trend[visitors], markero, linewidth2) ax.set_title(月度游客量趋势2024) ax.set_xlabel(月份) ax.set_ylabel(游客量万人次) ax.tick_params(axisx, rotation45) # 月份过多时旋转标签防重叠 fig.tight_layout() fig.savefig(trend.png, dpi300, bbox_inchestight)dpi300是期刊配图的标准期末报告用 150 也够但 300 会让老师觉得你认真。bbox_inchestight能自动裁掉空白边避免保存出来的图四周一大片白。tick_params(rotation45)解决标签重叠问题别靠plt.xticks(rotation45)那种老写法前者更清晰。如果SimHei在你的机器上不存在换成Microsoft YaHei或Noto Sans CJK SC都行关键是先确认系统里有这个字体文件。4.2 Pyecharts 拼接可视化大屏Page 布局与交互效果Pyecharts 是当前数据分析大作业里做“可视化大屏”的主流选择因为它生成 HTML可以动态交互不用像 Flask 那样自己搭前端。期末场景用Page组件把多张图拼成一个纵向可滚动的页面即可不需要炫技。from pyecharts.charts import Bar, Line, Page from pyecharts import options as opts bar (Bar() .add_xaxis(top_visitors.index.tolist()) .add_yaxis(游客量, top_visitors.values.tolist()) .set_global_opts(title_optsopts.TitleOpts(title热门目的地 TOP10))) line (Line() .add_xaxis(trend[month].tolist()) .add_yaxis(游客量, trend[visitors].tolist(), is_smoothTrue) .set_global_opts(title_optsopts.TitleOpts(title月度趋势))) page Page(layoutPage.SimplePageLayout) page.add(bar, line) page.render(analysis_report.html)Page.SimplePageLayout会让图表纵向排列适合报告展示。add_xaxis需要 Python 列表所以前面分析结果里的index和values都要.tolist()这是最常见的类型报错来源。中间章节里的图表关键还在于Pyecharts 版本不同 API 相差很大0.5.x 和 1.x 的写法完全不同装的时候直接指定pyecharts1.3.1能省下一堆报错。4.3 图表选型表折线、柱状、地图、玫瑰图各用在哪大作业最怕图表“为用而用”。下面这张选型表是我做项目时的标准参考你可以直接抄进报告附录分析目标推荐图表Pyecharts 类型注意点月度趋势折线图Linex 轴必须是排序后的时间序列城市排名柱状图Bar先排序再取前 N地理分布地图Map需要额外地图资源离线易空白价格区间满意度箱线图Boxplot先做异常值过滤评分分布玫瑰图Pierosetyperadius适合展示占比而非精确值评论数与游客量关系散点图Scatter日志换算后再画避免长尾挤压这里重点说MapPyecharts 自带的 1.x 版本地图数据需要在联网环境下载离线跑大概率白屏。期末答辩现场如果网络不稳定地图组件会直接变成空白块。稳妥方案是放弃地图改用柱状图按城市排布或者提前把地图 JS 文件下载到本地在init_opts{js_host: ./js/}中指定本地路径。5. 旅游网站数据分析避坑指南环境、字体与数据不一致的 5 个现场5.1 现象cmd 里输 python 没反应双击脚本直接闪退这是最基础也最影响心态的坑。原因九成是安装时没勾选 “Add Python to PATH”系统根本找不到python.exe。解决方法是重装并勾选或者手动把C:\Python3x\和C:\Python3x\Scripts\加进系统环境变量。装了 Anaconda 的用户优先用 “Anaconda Prompt” 运行脚本里面的环境变量是配好的。血泪经验不要在 Windows 自带 cmd 里试python失败后就放弃去查一下环境变量配置这属于 python 安装教程里反复强调但总有人忽略的一步。5.2 现象图表标题和坐标轴中文全是方块Matplotlib 默认字体不含中文字形。如果你只设了font.sans-serif [SimHei]但系统没有这个字体还是方块。先执行fc-list :langzhLinux/macOS或检查C:\Windows\Fonts确认字体是否存在没有就去下载Noto Sans CJK SC安装。装完还没用则是 Matplotlib 字体缓存的问题删除~/.cache/matplotlib重启 Python 即可。这个坑在 Linux 服务器上跑代码时尤其频繁本地 Windows 一般不缺 SimHei。5.3 现象Pyecharts 渲染出来是空白 HTML控制台报 404两种原因。第一种是 Pyecharts 版本错位用了 0.5.x 的旧代码跑在 1.x 的环境里图表对象直接不是预期结构HTML 里什么都不会显示。解决方法是统一钉版本pip install pyecharts1.3.1。第二种是资源加载问题1.x 默认从 CDN 拉取 echarts 依赖网络受限或离线环境下图表区域空白。解决方法是把echarts.min.js下载到项目js/目录然后生成图表时传入init_opts{js_host: ./js/, theme: white}。5.4 现象CSV 用 Excel 打开中文全乱Notepad 打开正常原因很明确Pandas 默认用utf-8写入 CSV而 Excel 对无 BOM 的 UTF-8 识别不稳定。解决方法是写文件时指定encodingutf-8-sig这是 UTF-8 带 BOM 的别名。前面第 2 章的清洗代码里我已经用了这个参数但如果你使用to_excel则不存在这个问题Excel 原生支持 xlsx 格式的中文。注意尽量不要在清洗阶段用latin1或gbk编码后续重新读入时会遇到一堆编码报错。5.5 现象答辩 PPT 里的数字和数据脚本跑出来的对不上这个问题非常隐蔽。原因通常是数据源不止一个或者脚本每次运行都重新生成了模拟数据。比如你报告里写“全年游客量 3200 万”但答辩前重新运行脚本因为模拟数据的随机种子没固定数字变成了 3100 万当场翻车。解决方法是清洗完成后立刻存一份clean_tourism.csv快照后续分析和画图全部从快照读入如果数据是随机生成的在顶部加一行np.random.seed(42)。另外统计口径也要写清楚比如“游客量”是包含所有月份还是只含旺季避免口径不一致。6. 答辩加分技巧把“能跑”变成“有说服力”的三个习惯6.1 把项目拆成三个脚本答辩时能一键复现不要把所有代码写在一个大 Notebook 里。按阶段拆成data_clean.py、analysis.py、visual.py三个文件每个文件末尾留一个if __name__ __main__:入口。答辩时老师如果问“这个图怎么来的”你可以当场运行python visual.py几秒钟重现全部图表。这个习惯在期末大作业和后续工作中都受用。6.2 用 Page 生成一个总 HTML所有图表放一页与其让老师在多个 Notebook 输出之间来回跳不如用 Pyecharts 的Page把所有图表拼成一个analysis_report.html。打开就是完整可视化报告滚动即可查看全部图表。如果你想更进一步也可以使用Tab组件把图表按“趋势 / 排名 / 价格”分组放在不同标签页交互感更强。6.3 每张图标题直接写业务结论不写“游客量趋势图”图表标题是老师的阅读路径。把“游客量趋势图”改成“暑期游客量为全年峰值、较春季增长 42%”把“热门目的地排名”改成“TOP10 城市游客量占全网 73%头部效应明显”。这一改动不需要额外代码只是把你的分析结论前置效果立竿见影。数据会自己说话是假的需要你替它说。我做这类项目养成的习惯是每次跑完分析先在记事本上写三行“最核心的发现”再回头决定画哪些图。图表永远为结论服务而不是为了凑页数。希望这份思路能帮你把那份.zip变成一份真正能打的大作业也希望你答辩顺利。本文还有配套的精品资源点击获取