Python旅游景点信息可视化系统实战:Django+数据分析+大模型
Python旅游景点信息可视化系统理解起来并不复杂用 Django 做 Web 框架把景点基础信息、游客量、评分、评论等数据管理起来再通过 pandas 做数据分析最后用 ECharts 把结果展示在页面上。如果还想让使用者用自然语言直接问数据可以在这个基础上接一个大模型 Agent例如 DeepSeek把“哪个城市的综合评分最高”这类问题转成可执行的查询和解释。这个方向比较适合正在学 Django 和数据分析的人也适合做课程设计、毕设或者是景区、旅游平台的内部看板。相比那种只做一个“静态大屏”的演示项目这套系统的核心价值在于数据从哪里来、怎么清洗、如何统计、如何呈现、如何被检索追问是一条完整的链路。大模型只是其中一层“问答入口”不是必需品。下面按我实际开发时会走的顺序拆开讲。先定边界再搭骨架然后处理数据最后做可视化和大模型问答。1. 先定边界数据链路、展示对象和大模型 Agent 的定位1.1 系统不是“爬虫加页面”重点在数据能不能流转起来只看“旅游景点信息可视化”这几个字很容易把重点放在页面效果上。实际开发时数据链路才是最需要先想清楚的部分。数据从哪里来是整个系统的起点。常见的来源包括景区公开统计数据接口、旅游平台的开放数据、地方文旅部门发布的数据文件以及企业内部上报的 Excel、CSV。如果是自己抓取网页数据要提前确认数据来源是否允许采集只取公开且合规的数据不要触碰用户隐私和平台限制。这个边界一开始就要写进项目说明里避免后面做偏。数据拿到之后要经过这么几步才能到页面数据采集脚本或管理命令定时拉取、导入。数据清洗空值、重复值、格式统一、异常数值处理。数据建表按业务拆分成基础信息表、统计表、评分评论表。数据分析聚合、趋势、对比、分类。数据可视化接口返回 JSON前端图库渲染。数据问答大模型 Agent 基于结构化结果回答自然语言问题。这六步里前三步最琐碎也最容易被忽略。很多人一上来就写页面结果图表数据是写死的导致系统换个数据源就崩。这是这类项目最常见的失败原因。1.2 先做一个最小可用闭环再谈大模型我建议第一个版本不要做大而全先做“单数据源、单页面、单图表”的闭环。比如导入一份包含景点、省份、评分、游客量的 CSV在页面展示一张省份统计柱状图再做一个筛选下拉框。这个闭环跑通后再逐步加地图、趋势图、导出功能和评论分析。这样做的好处是你可以在每个环节验证结果。CSV 导入了多少条记录统计结果和 Excel 透视表是否一致接口返回的 JSON 是否完整如果一上来就同时做十个图表和 Agent出了问题很难定位是数据问题、统计问题还是渲染问题。大模型 Agent 在这个系统里的定位应该是“查询助手”不是“数据唯一出口”。用户可以直接看图表也可以像聊天一样问“上季度游客量下降最多的省份是哪个”。Agent 需要先把问题翻译成数据查询条件或者先从已统计好的结果里检索再由模型组织自然语言答案。它不要自己去编数字也不要绕过数据管道直接回答。这个定位决定了后续的接口设计和提示词写法。2. 环境准备和项目骨架先把能跑起来的环境搭好2.1 Python 版本、虚拟环境和依赖清单开发这套系统不需要很高的机器配置。普通办公电脑8G 内存CPU 即可。大模型部分走 API 调用不需要本地 GPU。Python 版本建议 3.9 或更高安装时勾选“Add Python to PATH”。Django 版本以你实际安装的稳定版为准下面的命令在 4.x 下都能用。创建虚拟环境避免多个项目的依赖互相污染python -m venv venv # Windows venv\Scripts\activate # Linux / macOS source venv/bin/activate安装基础依赖pip install django pandas numpy requests openpyxlpandas 用来做数据清洗和聚合numpy 在很多统计计算里会用到requests 用来请求外部接口和大模型 APIopenpyxl 用来读取 Excel 文件。如果后面要做聚类分析再加 scikit-learn。如果要做 Redis 缓存再加 redis。这套依赖足够完成主体开发。不要一上来就安装 Spark、Hadoop 之类的大数据组件旅游景点项目在绝大多数情况下用 pandas 就能处理。2.2 创建 Django 项目和 App目录按职责划分在虚拟环境激活后执行django-admin startproject tourism_visual cd tourism_visual python manage.py startapp stats python manage.py startapp agent_chat我习惯把目录拆成两个 Appstats 负责景点数据模型、分析服务和可视化接口agent_chat 负责大模型问答逻辑。如果评论功能很重也可以单独拆一个 reviews App。职责划分清楚之后后面排查问题会省很多时间。创建完项目后先修改 settings.py把stats、agent_chat加入INSTALLED_APPS。设置LANGUAGE_CODE zh-hansTIME_ZONE Asia/Shanghai。在项目根目录建一个static目录并配置STATICFILES_DIRS用来放本地化的 ECharts 文件。如果要用环境变量管理 API Key可以安装python-dotenv或直接读取环境变量。然后执行python manage.py migrate python manage.py createsuperuser数据库默认用 SQLite开发阶段足够了。等数据量变大再换 MySQL 或 PostgreSQL迁移成本主要是在连接配置和字符集上模型代码可以保持不变。2.3 数据库选型先用 SQLite 起步遇到并发再切换很多人会纠结数据库选型。我的建议很明确开发阶段直接 SQLite零配置、文件方式备份方便。如果多人同时写、数据量大到 SQLite 明显卡顿再迁移到 PostgreSQL。切换数据库时注意两件事一是字符集MySQL 要确保使用 utf8mb4否则中文或 emoji 评论会乱码二是settings.py的数据库连接改为环境变量不要把密码写死在代码里。Django 的 ORM 在切换数据库之后大部分查询代码不需要改但涉及日期函数和聚合函数的 SQL 方言可能会有差异需要回归测试一遍。3. 数据建模和数据导入表结构决定分析上限3.1 核心表和字段设计旅游景点信息不能只建一张表。建议至少拆成三张ScenicSpot景点基础信息表景点名称、省份、城市、等级、经纬度、类型、创建时间。SpotDailyStat每日游客统计表景点、日期、游客量、门票价格、收入。SpotReview评分评论表景点、用户标识、评分、评论内容、评论日期。基础信息表和数据统计表分开核心原因是同一个景点会有多天的游客量如果塞在同一张表里会导致字段大量重复统计接口写起来也痛苦。下面是一个简化的模型示例from django.db import models class ScenicSpot(models.Model): name models.CharField(max_length128, verbose_name景点名称) province models.CharField(max_length64, verbose_name省份) city models.CharField(max_length64, verbose_name城市, blankTrue) level models.CharField(max_length32, verbose_name等级, blankTrue) category models.CharField(max_length64, verbose_name景点类型, blankTrue) longitude models.DecimalField(max_digits10, decimal_places6, nullTrue, blankTrue) latitude models.DecimalField(max_digits10, decimal_places6, nullTrue, blankTrue) class Meta: db_table scenic_spot class SpotDailyStat(models.Model): spot models.ForeignKey(ScenicSpot, on_deletemodels.CASCADE, related_namedaily_stats) stat_date models.DateField(verbose_name统计日期) visitor_count models.IntegerField(default0, verbose_name游客量) ticket_price models.DecimalField(max_digits8, decimal_places2, default0, verbose_name门票价格) revenue models.DecimalField(max_digits12, decimal_places2, default0, verbose_name收入) class Meta: db_table spot_daily_stat unique_together (spot, stat_date) class SpotReview(models.Model): spot models.ForeignKey(ScenicSpot, on_deletemodels.CASCADE, related_namereviews) rating models.IntegerField(verbose_name评分) # 假设1-5分 comment models.TextField(blankTrue, verbose_name评论内容) review_date models.DateField(verbose_name评论日期) class Meta: db_table spot_review字段类型里金额和经纬度用DecimalField而不是FloatField避免浮点误差。统计表的unique_together约束可以防止同一天重复导入同一景点的数据。3.2 批量导入数据用 Django management command数据导入不要写在 views.py 里也不要放在每次请求的视图函数里。正确做法是写成 Django management command这样既能手动执行也能用定时任务跑。以一个 CSV 导入命令为例# stats/management/commands/import_spot_data.py import csv from django.core.management.base import BaseCommand from stats.models import ScenicSpot class Command(BaseCommand): help 导入景点基础信息CSV def add_arguments(self, parser): parser.add_argument(csv_path, typestr) def handle(self, *args, **options): path options[csv_path] with open(path, encodingutf-8-sig) as f: reader csv.DictReader(f) ok 0 for row in reader: _, created ScenicSpot.objects.update_or_create( namerow[name], defaults{ province: row.get(province, ), city: row.get(city, ), level: row.get(level, ), category: row.get(category, ), }, ) ok 1 self.stdout.write(self.style.SUCCESS(f导入完成{ok} 条))使用update_or_create而不是get_or_create的好处是同一景点再次导入时会更新已有记录不会产生重复数据。执行方式python manage.py import_spot_data data/spot.csv数据文件建议统一放在data/目录并按照导入日期命名例如spot_20250101.csv方便追溯。3.3 数据清洗一定不要跳过这一步导入数据时最容易出现的几类问题CSV 文件用 Excel 编辑后带有 UTF-8 BOM读取时第一列字段名带\ufeff。解决办法是读取文件时使用utf-8-sig。省份和城市名称不统一比如“广西壮族自治区”和“广西”混用。游客量字段里出现--、逗号、空格导致int()失败。评分字段缺失统计平均值时需要用dropna()或填充默认值。清洗逻辑可以抽成一个函数放在stats/services.py里。导入命令调用它后面如果是接口采集同样调用它。这样能保证不同来源的数据进入数据库前口径一致。4. 数据分析层不要把统计逻辑堆在视图函数里4.1 用 service 层隔离分析逻辑Django 的 views.py 适合做 HTTP 请求处理不适合写复杂统计。我会在stats/services.py里集中写数据查询和聚合逻辑views 只负责调用 service 并返回 JSON。这样做有几个直接好处同一个统计结果可以被页面、API、Agent 三种方式复用。单独跑脚本测试统计逻辑时不需要起 HTTP 服务。后续加缓存时只需要在 service 外部包一层。一个统计接口的典型流向页面
上一篇/下一篇内容由系统自动关联
返回资讯列表 →