
简介全国旅游景区数据集收录了约1.2万个景点涵盖1A至5A全等级解决旅游类项目中基础地理数据缺失的痛点适合文旅行业分析、出行路线规划、数据可视化教学及小程序开发等场景。资源包共2个文件提供Excel表格与JSON两种格式Excel便于快速筛选、排序和透视统计JSON适合程序化解析与对接后端服务压缩包整体仅1.26MB轻量易用。每个景区记录包含景点名称、所在城市、详细地址、景区等级、经度、纬度六项核心字段可直接用于构建景点分布图、分析各等级数量占比或作为算法练习的干净数据集。数据更新至2022年6月已有4710人学习下载下载前还可通过指定小程序预览核验数据是否满足需求。对需要快速获取全国景区结构化数据的数据分析师、旅游产品开发者及高校研究者而言是一份高性价比的参考资料。1. 全国旅游景区数据集一份能直接用起来的 json excel 双格式数据做旅游分析和地图可视化的人都有体会找景点数据不难难的是找一份经纬度干净、等级齐全、省份不丢的景区数据集。这份全国旅游景区数据集涵盖了国内主要景点的名称、行政区划、等级、坐标等关键信息数据快照时间在 2022 年 6 月 3 日同时提供 json 和 excel 两种格式基本覆盖了日常开发里“程序读取”和“人工查看”两种使用方式。你要是正在做智慧文旅项目、景区热度分析、地图打点展示或者课程设计里需要一份真实的景点数据做验证这份数据可以直接落进项目里省掉自己去各个平台抓取、清洗的重复劳动。2. 数据集结构拆解json 和 excel 两种格式分别适合干什么2.1 文件构成与格式定位差异拿到压缩包后第一件事是搞清楚里面文件的组织方式。按这份数据的常规打包习惯压缩包内会包含一个景区数据的 json 文件和一个 excel 文件。json 文件体积相对小一些适合在 Python、Java、Node.js 里通过标准库直接解析excel 文件是把同样的数据摊平成一张二维表适合用 WPS 或 Excel 打开后做筛选、透视、人工核对。这两种格式不是简单复制而是各有侧重。json 里一般是嵌套结构比如地址可能拆成“省份 城市 区县”三层也可能是一整个字符串excel 里则倾向于把每个字段放到独立的列方便用筛选和排序去查数。如果你是要做自动化分析优先用 json如果你只是想快速看一下数据规模、核对某个景区的等级或者把它导进 ArcGIS、QGIS 这类 GIS 工具excel 更顺手。提示解压后先别急着改文件名。json 和 excel 的文件名通常保持一致只是后缀不同建议保留原始命名后续写脚本时不容易对不上。2.2 景区字段清单与数据类型说明从拆包后的实际使用来看这份数据集的字段大致落在以下这些上面。各字段的类型和说明如下表所示具体以包内文件实际表头为准。字段名类型说明景区名称String景点的完整官方名称如“故宫博物院”景区编号String内部索引 ID可用于去重省份String景区所在的省级行政区城市String景区所在的城市区县String景区所在的区县部分景区可能为空景区等级String常见的值为“5A”“4A”“3A”也可能出现“AAAAA”这类写法景区类型String自然景观、人文景观、红色旅游、乡村旅游等详细地址String景区的具体地址描述经度FloatGCJ-02 或 WGS84 坐标系下的经度纬度Float同上简介String景区的简要文字介绍长度不定这里有几个需要留心的点景区等级字段并不统一后面清洗时要归一化经度和纬度在 json 里通常是浮点数在 excel 里则可能被 Excel 自动改成文本或科学计数法格式这个坑后面专门讲景区类型字段有的记录是空的做统计时要注意缺失值占比。2.3 json 数组结构一条景区记录长什么样先直接看 json 文件的顶层结构。用文本编辑器打开后你会看到最外层是一个 json 数组数组里每个元素对应一个景区对象。参考结构如下[ { 景区名称: 故宫博物院, 景区编号: BJ001, 省份: 北京市, 城市: 北京市, 区县: 东城区, 景区等级: 5A, 景区类型: 人文景观, 详细地址: 北京市东城区景山前街4号, 经度: 116.397, 纬度: 39.918, 简介: 明清两代皇宫世界文化遗产。 }, { 景区名称: 西湖风景名胜区, 景区编号: ZJ001, 省份: 浙江省, 城市: 杭州市, 区县: 西湖区, 景区等级: 5A, 景区类型: 自然景观, 详细地址: 杭州市西湖区龙井路1号, 经度: 120.149, 纬度: 30.254, 简介: 以湖光山色和人文古迹闻名的国家级风景名胜区。 } ]顶层是一个 json 数组而不是一个大对象这一点很重要。很多新手习惯用json.load()之后直接按字典取 key结果发现取不到直接报错就是因为最外层是数组必须先遍历或按索引访问。另外中文 key 在 Python 里可以直接使用不需要额外转码但保存文件时要注意编码否则容易出现乱码。json 里字段名都是中文这在实际项目里并不少见。好处是别人拿到这份数据一眼能看懂每个字段的含义不用再去查字典表坏处是如果项目要求字段名用英文你得在读取后做一次 rename比如把“景区名称”改成name、把“景区等级”改成level。3. Python 读取与清洗实战从 json 数组到干净的 DataFrame3.1 用 json 库和 pandas 读取 json 文件读取这份 json 文件常见做法是先用标准库json把数组加载进来再用pandas.json_normalize直接展开成 DataFrame。之所以不直接手动遍历构造列表是因为json_normalize在处理嵌套结构时能少写不少代码还能自动处理缺失字段。import json import pandas as pd # 读取 json 文件注意指定 utf-8 编码 # BOM 头存在时要用 utf-8-sig否则第一个字段名会带 \ufeff with open(全国旅游景区数据集.json, r, encodingutf-8-sig) as f: data json.load(f) # 用 json_normalize 把景区数组展开成 DataFrame df pd.json_normalize(data) print(f共 {len(df)} 条景区记录) print(df.head()) print(df.dtypes)这块代码里有几个关键点。encodingutf-8-sig是看情况加的如果文件是无 BOM 的 UTF-8用utf-8-sig也不会出问题它能自动兼容有 BOM 的情况如果文件带 BOM 却用了utf-8第一个字段名会变成“\ufeff景区名称”之后按字段名取值时对不上。pd.json_normalize会把嵌套的字典自动展开成多列如果原始 json 里“详细地址”是单独的字符串就不会被展开保持不变。提示如果 json 文件很大一次性json.load会占内存。这份景区数据集规模不算夸张直接用json.load能接受但如果后续你换成别的千万级数据建议考虑ijson流式读取。3.2 用 pandas 读取 excel 文件读取 excel 文件pandas 的read_excel是最省事的路径底层依赖openpyxl。如果你的环境里没有这个库会直接报ImportError需要先执行pip install openpyxl。import pandas as pd # 读取 excel 文件 # header0 表示第 0 行是表头sheet_name 指定工作表 df_excel pd.read_excel( 全国旅游景区数据集.xlsx, sheet_name0, header0, dtype{经度: str, 纬度: str} # 先按字符串读入避免精度损失 ) print(df_excel.shape) print(df_excel.columns.tolist())我一般会建议在读 excel 的时候先把经纬度两列按字符串读入而不是默认 float。原因是 Excel 文件里经度纬度经常被显示成 116.397 这种带小数位的形式但底层的单元格格式可能是文本read_excel按 float 解析时会报错或者变成 NaN。先按字符串读入后面再手动astype(float)做类型转换可控性更高。sheet_name0表示读取第一个工作表。你也可以直接用工作表名比如sheet_name景区台账。注意如果 excel 文件里有多个 sheet默认只读第一个需要先确认目标数据到底放在哪个 sheet 里避免读错。3.3 字段清洗去重、等级归一化、坐标转 float原始字段基本能看但要真正拿来统计和画图至少还要做三件事去重、等级归一化、坐标类型修正。# 1. 按景区名称 省份 去重 df[去重键] df[景区名称] _ df[省份] df df.drop_duplicates(subset[去重键], keepfirst).reset_index(dropTrue) # 2. 景区等级归一化把 AAAAA / 5A 类写法统一成 5A def normalize_level(x): if pd.isna(x): return None s str(x).upper().replace(景区, ).replace(级, ) if AAAAA in s or 5A in s: return 5A if AAAA in s or 4A in s: return 4A if AAA in s and AAAA not in s: return 3A return s df[等级标准] df[景区等级].apply(normalize_level) # 3. 经纬度转成 float非法值变成 NaN df[经度] pd.to_numeric(df[经度], errorscoerce) df[纬度] pd.to_numeric(df[纬度], errorscoerce) # 4. 丢掉坐标缺失的记录 df df.dropna(subset[经度, 纬度]) print(df[等级标准].value_counts())这段逻辑不复杂但每一步都有讲究。去重时用“景区名称 省份”组合键比单独用名称更安全因为不同省份可能存在同名景区比如“人民公园”在很多城市都有。等级归一化先统一转大写再按字符匹配规避了“AAAAA”和“5A”两种写法并存的问题。pd.to_numeric配合errorscoerce会把无法转换的坐标值变成 NaN最后统一丢弃避免画图时出现坐标乱飞的脏点。value_counts()输出能直接看到各等级景区的数量分布这是整个分析流程里最有价值的检查步骤之一——如果 5A 景区数量明显少得离谱多半是等级字段里还有没覆盖到的写法比如“国家5A级旅游景区”这类带前后缀的字符串。4. 把景区数据用起来坐标系识别、等级统计与 GeoJSON 导出4.1 坐标系识别GCJ-02 还是 WGS84先用坐标偏移量验证拿到经纬度数据第一件事不是画图而是判断坐标系。国内公开的景区数据最常见的有三种坐标系WGS84GPS 原生坐标、GCJ-02火星坐标高德、腾讯地图在用、BD-09百度坐标。判断方法很简单——拿一个你确定位置的景区用手机 GPS 记录现场坐标再和数据集里的坐标对比差值在几十米到几百米之间基本就是坐标系不同导致的。如果不想跑现场有个土办法找故宫约经度 116.397纬度 39.918 是 WGS84 近似值对比当前数据的坐标。如果数据里故宫坐标大致是 116.39139.915 附近偏差两三百米说明是 GCJ-02。用代码可以粗略判断# 已知故宫 WGS84 坐标 known_point (116.391, 39.907) # 实际 WGS84 约 116.391, 39.907 # 从数据集里找出故宫 palace df[df[景区名称].str.contains(故宫)].iloc[0] data_point (palace[经度], palace[纬度]) # 计算直线距离简化球面距离公式 import math def rough_distance(p1, p2): R 6371000 lat1, lon1 math.radians(p1[1]), math.radians(p1[0]) lat2, lon2 math.radians(p2[1]), math.radians(p2[0]) dlat lat2 - lat1 dlon lon2 - lon1 h math.sin(dlat/2)**2 math.cos(lat1)*math.cos(lat2)*math.sin(dlon/2)**2 return 2 * R * math.asin(math.sqrt(h)) dist rough_distance(known_point, data_point) print(f与 WGS84 坐标的偏差约: {dist:.0f} 米)如果偏差在几百米量级基本可以认定坐标是 GCJ-02如果偏差在几十米以内则是 WGS84。这个脚本是粗糙估算不代替代精密转换但足够帮你判断该用哪种纠偏策略。后续做地图可视化时如果底图是高德的 GCJ-02 坐标系数据本身也是 GCJ-02直接叠加不需要转换如果底图是标准瓦片则需要先把 GCJ-02 转回 WGS84。4.2 景区等级与省份分布统计清洗完数据后最直接的分析是看各省份各等级景区的分布情况。用 pandas 的groupby一行就能算出来。# 各省份各等级景区数量统计 province_level df.groupby([省份, 等级标准]).size().reset_index(name数量) # 只看 5A 景区按数量排序 five_a df[df[等级标准] 5A] province_5a_count five_a.groupby(省份).size().sort_values(ascendingFalse) print(province_5a_count.head(10))这个统计能帮你快速判断数据质量如果某个省份的 5A 景区数量明显异常比如某些大省一个都没有那要先怀疑是等级字段里还有没归一化的变体而不是数据本身缺失。跑统计不只是为了看结果更是为了校验清洗逻辑有没有漏洞。我一般会把 top10 结果和文旅部发布的官方名录做抽样对比抽查几个省份的 5A 景区数量能对得上八九成这份数据就可以放心用了。4.3 导出 GeoJSON一份文件直接拖进 QGIS清洗后的 DataFrame 要转成 GeoJSON 非常直观本质就是按 GeoJSON 规范构造 FeatureCollection。前面也提到了 json 转换在数据交换中的普适性这里你等于做了一次从“表格数据”到“地图数据”的格式转换。import json features [] for _, row in df.iterrows(): feature { type: Feature, geometry: { type: Point, coordinates: [row[经度], row[纬度]] }, properties: { name: row[景区名称], level: row[等级标准], province: row[省份], city: row[城市] } } features.append(feature) geojson { type: FeatureCollection, features: features } with open(景区点.geojson, w, encodingutf-8) as f: json.dump(geojson, f, ensure_asciiFalse, indent2) print(f已导出 {len(features)} 个景观点位)ensure_asciiFalse是为了让中文字符以原始形式写入文件否则 json.dump 会把中文转成 \uXXXX 转义序列虽然不破坏数据但人眼很难读。缩进参数indent2方便调试时检查结构发布到线上可以去掉以减小体积。注意坐标顺序一定要是 [经度, 纬度]GeoJSON 规范里先写 longitude 再写 latitude。这个顺序和平时口头说“纬度、经度”正好相反是最容易翻车的地方。导出的 GeoJSON 可以直接拖进 QGIS 或 geojson.io 预览。如果点位出现在海中央或者整体偏移了几公里大概率是坐标系没对上回到 4.1 的步骤重新确认。5. 常见问题排查编码乱码、字段错位与坐标漂移的五条实战记录做数据工程的人都懂数据本身很少是完美的这一节直接列几条高频踩坑记录全部按“现象 → 原因 → 解决”的格式写。5.1 Excel 打开 csv 文件显示乱码现象把 json 转成 csv 后用 Excel 直接双击打开中文全是乱码。原因脚本保存 csv 时默认用了 UTF-8 编码老版本 Excel 默认按 ANSIGBK解析编码不匹配导致乱码。解决写 csv 时指定utf-8-sig编码Excel 能正确识别 BOM 头。df.to_csv(景区.csv, indexFalse, encodingutf-8-sig)。如果文件已经生成用记事本打开后另存为 ANSI 编码也能临时救回来但不推荐。5.2 json.load 报 JSONDecodeError现象用 Python 读 json 文件时报错JSONDecodeError: Expecting value: line 1 column 1。原因文件不是从第 1 行直接开始就是 json 内容可能在头部带 BOM或者在抓取时头部混入了非 json 字符。解决打开文件时先用utf-8-sig编码读取去掉 BOM如果还有问题用文本编辑器打开看文件头部是否存在 HTML 标签或注释内容。这些残留一般是因为原始数据在抓取后没有清理干净手动删除文件开头非 json 的部分即可。5.3 经纬度在 Excel 里变成科学计数法现象打开 excel 文件经度列显示成1.16397E02这种格式或者单元格左上角有绿色三角提示“文本形式存储的数字”。原因Excel 对超过一定长度的数字列会自动切换成科学计数法显示如果原始数据里经纬度本身被存成了文本读取时解析也会出问题。解决读 excel 时先把经纬度列指定为字符串见 3.2 节的代码。用dtype{经度: str}防止自动解析后面再用pd.to_numeric转换同时把非法值转为 NaN 丢弃。显示上也可以通过设置 excel 的列格式为“数值”修正。5.4 统计的 5A 景区数量明显偏少现象跑完value_counts()发现 5A 景区数量只有几十个比官方公布的两百多个少了一大截。原因等级字段里有“国家5A级景区”“AAAAA”“5A”多种写法只对“5A”做精确匹配会漏掉大量记录。另外可能还有空值记录被dropna顺带丢掉了。解决用归一化函数先把所有等级字段统一映射字符匹配要批量处理“AAAAA”和“A 级”等变体。第 3.3 节的normalize_level就是干这个用的实际项目中可以再扩展关键词列表把“5A级”“五A”之类写法也覆盖进去。5.5 点位整体偏移且相互之间没变形现象把数据画在地图上景点位置整体往东北方向偏了几百米看起来呈现某种系统性偏移。原因坐标系不匹配。数据是 GCJ-02火星坐标或 BD-09而底图用的是 WGS84或者反过来。这是国内地图数据处理里最典型的坑严重的能偏出几百米甚至一公里。解决先按 4.1 的脚本判断坐标系再用对应算法做纠偏。GCJ-02 和 WGS84 有标准转换公式BD-09 和 GCJ-02 之间也有公开算法网上可搜到现成实现。无论是做地图打点还是选址分析视图层的底图和数据的坐标系必须统一。6. 进阶技巧用这份数据建一个本地景区检索工具6.1 把 DataFrame 写入 sqlite实现秒级关键字检索数据清洗完放在 DataFrame 里用着还行但每次想查景区都要重新写 pandas 表达式体验并不好。一个更实用的做法是把 DataFrame 落到 sqlite 内存数据库里然后用 SQL 查询速度更快写法也更接近线上生产逻辑。import sqlite3 import pandas as pd # 已清洗的 df 直接写入内存数据库 conn sqlite3.connect(:memory:) df.to_sql(scenic, conn, indexFalse, if_existsreplace) # 按名称模糊查询 query SELECT 景区名称, 省份, 城市, 等级标准, 经度, 纬度 FROM scenic WHERE 景区名称 LIKE ? result pd.read_sql(query, conn, params(%西湖%,)) print(result)to_sql会把 DataFrame 的字段名直接作为列名中文列名在 SQLite 里可以用但查询时建议用双引号或反引号包住列名避免和关键字冲突。LIKE配合%西湖%适合小型数据集的模糊检索如果将来数据规模变大可以考虑给“景区名称”建索引或者干脆换用FTS5全文搜索组件。6.2 按坐标范围批量筛选相邻景区检索工具除了按名字查还应该支持按位置查。比如你拿到了一个用户当前位置想知道周边 20 公里有哪些景区可以用简化球面距离公式批量计算。import math def haversine_distance(lat1, lon1, lat2, lon2): R 6371000 phi1 math.radians(lat1) phi2 math.radians(lat2) dphi math.radians(lat2 - lat1) dlambda math.radians(lon2 - lon1) a math.sin(dphi / 2) ** 2 math.cos(phi1) * math.cos(phi2) * math.sin(dlambda / 2) ** 2 return 2 * R * math.asin(math.sqrt(a)) # 用户当前位置 cur_lat, cur_lon 30.2741, 120.1551 # 杭州西湖附近 # 计算每个景区到当前位置的距离 df[distance] df.apply( lambda r: haversine_distance(cur_lat, cur_lon, r[纬度], r[经度]), axis1 ) nearby df[df[distance] 20000].sort_values(distance) print(nearby[[景区名称, 省份, 城市, 等级标准, distance]].head(10))这个haversine公式是球面距离计算里最常用的近似算法精度对景区筛选完全够用。运行时要注意df.apply是逐行遍历在几十万条数据量级下稍慢但这几年普遍爱用的 geopy 库封装好了现成的geopy.distance.distance可以直接替代手写公式效果一样且代码更短。把一个静态数据集变成可交互的本地小工具这一套下来从读取、清洗、坐标系识别到可视化、检索、周边筛选整条链路都通了。做地图业务的人常说数据处理没有高深技术全是细节磨出来的。坐标偏移、编码乱码这类问题猛一看都像“玄学”但大多有固定排查路径。从那以后我每次拿到带坐标的公开数据集都会强制走一遍“存字符串读入 → 先查坐标系 → 再算坐标偏差”的流程至少能省掉一半返工时间。这份数据的价值在于它把最烦人的脏活累活省掉了一部分但坑还是要自己踩过才能记得牢希望帮到你。本文还有配套的精品资源点击获取