简介:这份全国旅游景区数据集收录了截至2022年6月初的约12000条景点记录,覆盖1A至5A各等级,字段包括景点名称、所在城市、地址、等级、经度、纬度等,适合旅游行业分析、地理可视化、行程规划等开发与研究工作,尤其适合GIS开发者、数据分析师及旅游管理研究者快速获取结构化基础数据。资源压缩包共2个文件,总大小1.26MB,其中Excel格式便于直接筛选、透视与人工校对,JSON格式则适合导入数据库、对接后端或用于前端地图打点,两种格式互补,可减少格式转换成本。已有4715人学习或下载,数据输出时间为2022年5月30日,整体时效性较好。利用经纬度与城市字段,可轻松构建景点分布热力图、区域等级对比或推荐系统演示;结合等级字段,还能进一步分析高等级景区的空间集聚特征,为文旅规划、选址调研或教学实验提供参考依据。对于需要做地图可视化或旅游热度分析的研究者,它可直接作为基础数据源,减少自行采集与整理的时间成本。
1. 全国旅游景区数据集:JSON加EXCEL,真正值钱的是清洗后的坐标
全国旅游景区数据集,名字听起来像爬虫站随手打包的资源包,但真正拿它干活的人会很快发现,JSON和EXCEL两份文件只是半成品。2022-06-03这个时间戳决定了数据口径:之后新增的景区、评级调整、行政区划变更,它一概不知道,所有下游统计都只能按这个快照来谈。我身边做文旅数据可视化、GIS底图和景区热力分析的人,几乎都在同一批景区数据上花过两遍时间:一遍清理编码和繁体字,一遍统一坐标口径。
下面按“拆字段→清洗→导入→避坑→进阶核验”的顺序,把这个数据集变成一张能直接进ArcGIS、数据库和报表工具的表。适合要用景区POI做底图或分析的中高级工程师照抄步骤,也适合数据岗新手拿来做第一次全流程清洗练习。值不值得投入?我的判断是:当基础底料用值得,当最终交付物用不值得,清洗这一步省不掉。
2. 拆开JSON和EXCEL两份文件:字段清单与两种格式的适用边界
2.1 JSON是嵌套的,EXCEL是拍平的:先认清字段映射关系
景区数据集最常见的打包方式,是同一份内容给两个视图:JSON保持接口原始结构,Excel把大部分字段拍平成一张宽表。第一次接触这份数据的人容易直接双击Excel开看,觉得“有省份、有坐标、有等级就能用”,然后等脚本对接时报KeyError,才发现JSON里还有嵌套的location对象和type数组。所以我建议动手前先花十分钟,把两份文件的字段映射写下来。
JSON里单个景区的结构通常是这样的:
{ "name": "示例景区", "level": "4A", "province": "浙江省", "city": "杭州市", "district": "西湖区", "location": { "lng": 120.151, "lat": 30.247 }, "type": ["人文景观", "湖泊湿地"], "ticket": { "旺季": 60, "淡季": 40 } }这段JSON里,name、level这种是标量字段,直接取值即可;location是一个对象,展开成Excel里的“经度”“纬度”两列;type是数组,Excel里会被展开成“景区类型”一列或“一级类型”“二级类型”两列;ticket如果是对象,Excel里的常见做法是变成“门票旺季”“门票淡季”两列。这就是“嵌套变拍平”的过程,映射关系是否一致,直接决定后面转换脚本是半小时写完还是写两个小时。
字段映射表我一般会做成下面这样,留一份在项目文档里:
| 业务字段 | JSON路径 | Excel列名 | 类型 | 可空 |
|---|---|---|---|---|
| 景区名称 | name | 景区名称 | 文本 | 否 |
| 景区等级 | level | 等级 | 文本(5A/4A/3A) | 是 |
| 所在省份 | province | 省份 | 文本 | 否 |
| 所在城市 | city | 城市 | 文本 | 是 |
| 所在区县 | district | 区县 | 文本 | 是 |
| 经度 | location.lng | 经度 | 浮点 | 否 |
| 纬度 | location.lat | 纬度 | 浮点 | 否 |
| 景区类型 | type | 景区类型 | 数组/文本 | 是 |
| 门票参考 | ticket.旺季 | 门票旺季 | 浮点 | 是 |
| 更新时间 | update_time | 更新时间 | 日期 | 是 |
这份映射表看着简单,但坑往往出在“可空”这一列。真实数据里,区县级信息缺失非常常见,尤其是跨省交界处的景区;而经纬度一旦缺失,这条记录在GIS里就直接废掉,不能简单填空处理。另一个容易忽略的点是Excel表头命名没有统一标准,有的数据集把type叫“景区类型”,有的叫“类型描述”,写脚本前务必对照真实Excel表头,不能照抄JSON路径。
2.2 用JSON还是用EXCEL:取决于下游是人工判断还是脚本入库
很多新手拿到双格式文件后的第一个问题是:到底用哪个?我的建议很简单,两万行以内、要人工抽检、要给业务同事对口径的,用Excel;要批量入库、要做空间关联、要反复清洗的,用JSON。Excel给人看,JSON给程序看,这个分工可以避免绝大多数“Excel打开后乱码”“json用什么打开都别扭”的问题。
JSON格式的好处是保留了数组和嵌套结构,接口返回是什么样就是什么样,脚本解析逻辑稳定,不会像Excel那样有单元格格式的干扰。Windows上双击.json文件会弹出打开方式选择,常见做法是用VS Code或Notepad++打开;如果只想快速看结构,拖进Chrome地址栏也能以纯文本展示。但这些都只是辅助手段,真正稳定的是脚本解析。Excel格式的好处是所见即所得,坏处是前几列一旦被Excel自动转成日期或科学计数法,原始值就再也找不回来了。所以从接口落盘时就优先保留JSON,Excel只作为派生物。
这里补一句实操经验:如果数据是从接口一段段拉下来的,很多人会用JMeter的JSON提取器把response里的景区名单抽出来,再拼成大文件。这样做的结果往往是字段名大小写不一致,比如lng、Lng、longitude混着来。遇到这种情况,别指望靠眼睛修,直接在脚本里维护一个字段别名映射,把三种写法都归一成lng。
2.3 为什么时间戳比内容更需要先核验
再回到标题里的2022-06-03。这个日期的含义是数据快照时间,而不是“全国旅游景区最新名录”的时间。2022年之后,不少地方经历过度假区评定、等级复核、行政区划调整,这份数据里的level字段、province字段都可能与现状不一致。做年报、做同比分析时,如果拿这份数据和今年的数据直接对比,会得出“景区数量下降了”这种假结论。
我习惯先把update_time字段单独抽出来看分布:是不是所有记录都集中在同一天,有没有某几条明显是补录的。凡是update_time早于快照日期超过一年的记录,在报告里要打上“历史数据”标签。这一步不涉及复杂代码,但在任何清洗流程里都值得最先做,因为统计口径错了,后面清洗得再干净也白搭。行政区划调整是另一个隐蔽问题,数据里的province字段还是旧值,按省级填色时就会出现空白区,处理方式不是改数据,是更新一张新旧区划对照表。
3. 用Python把JSON清洗成Excel表:转换脚本、编码与字段补全
3.1 最小转换脚本:读JSON、抽字段、落XLSX
先把最简单的路径跑通。假设下载下来的JSON文件名是attractions.json,数组的每一项是一个景区对象,用pandas加openpyxl可以直接生成xlsx:
import json import pandas as pd def extract_attractions(json_path: str) -> list[dict]: # 读取景区JSON,文件名为示例,实际使用时换成你下载到的文件名 with open(json_path, "r", encoding="utf-8") as f: data = json.load(f) rows = [] for item in data: loc = item.get("location") or {} rows.append({ "景区名称": item.get("name"), "景区等级": item.get("level"), "省份": item.get("province"), "城市": item.get("city"), "区县": item.get("district"), "经度": loc.get("lng"), "纬度": loc.get("lat"), "景区类型": "、".join(item.get("type", [])), "更新时间": item.get("update_time"), }) return rows if __name__ == "__main__": rows = extract_attractions("attractions.json") df = pd.DataFrame(rows) df.to_excel("attractions_cleaned.xlsx", index=False, engine="openpyxl") print(f"共写入 {len(df)} 条记录,缺失坐标 {df['经度'].isna().sum()} 条")这段脚本的核心逻辑是手动做字段映射,而不是直接pd.read_json一把梭。原因在于嵌套对象location和数组type都必须显式展开,一旦某个字段在数据里叫法变了,脚本会安静地输出None,而read_json会把嵌套结构原样丢进单元格,后面所有依赖坐标的操作都会翻车。如果只是从JSON里抽单个字段,用JsonPath写查询函数更快,但整表清洗还是逐字段取最稳。
参数方面,三个地方需要根据实际数据调整。第一是encoding,绝大多数国内接口或爬虫落盘的JSON是utf-8,但有些老系统会用gb18030,读不出来时先换成gb18030试试;第二是to_excel里的engine,默认是openpyxl,如果环境里没装,pip install openpyxl就好;第三是输出文件名,我习惯在文件名里带上处理日期,方便后面追版本。
3.2 处理JSON数组:把多值字段拆成子表,不要塞进一个格子
上一节的脚本把type数组用顿号连接了,这在只需要一个描述字段时够用,但如果你后面要做“哪些景区是自然景观”这类筛选,或者按类型统计数量,顿号拼接字段会让统计结果虚高:一条“自然+人文”的数据会被两个类型同时命中。这时候的常见做法是生成一张一对多的子表。
# df 是3.1生成的景区主表 # 如果“景区类型”列是顿号连接成的字符串,先按分隔符拆成列表 type_rows = df[["景区名称", "景区等级"]].copy() type_rows["类型"] = df["景区类型"].str.split("、") type_rows = type_rows.explode("类型") type_rows.to_excel("attractions_type_mapping.xlsx", index=False)explode是pandas里处理列表字段最顺手的方法,它会把每个景区按类型拆成多行,主键“景区名称+等级”能保证拆分后还能对齐回主表。使用前要注意先把“景区类型”列里由中文顿号、英文逗号、竖线混在一起的分隔符统一替换成同一个,否则explode拆不干净。如果原始JSON里type本身就是数组,df那列就是list,直接跳过str.split这一步。
拆完子表后,主表里的“景区类型”列是删还是留?我建议主表保留原样,子表单独输出,两张表通过景区名称关联。这样Excel端既能快速浏览,SQL端又能直接做主表join子表的统计,不需要为了满足某一端把数据结构改死。写入Excel时也不要动合并单元格,下游不管是SQL还是ArcGIS,读带合并单元格的表都容易出问题。
3.3 编码与字段类型:乱码和科学计数法的两个前置检查
先说编码。如果用上面的脚本输出的是xlsx,Excel打开不会乱码,因为xlsx内部就是XML加UTF-8存储。真正容易乱码的是你顺手用df.to_csv("attractions.csv")输出的中间文件,Windows下默认编码是GBK,pandas写出去的是UTF-8,Excel双击一打开就是满屏乱码。解决办法是to_csv时指定encoding="utf-8-sig",这个参数会在文件头加BOM,Excel能正确识别UTF-8。为什么不用utf-8而用utf-8-sig,正是BOM的差别,这也是csv跨平台最经典的坑之一。
再说字段类型。景区等级这种纯文本字段还好,容易出问题的是景区编号和联系电话:它们以数字形式存在Excel里时,超过11位就会显示为科学计数法,超过15位末尾数字直接变成0。如果这份数据里恰好有景区编号列,把该列统一转字符串是重中之重:
df["景区编号"] = df["景区编号"].astype(str)这句代码看着不起眼,但能避免两个后续灾难:一是Excel里显示3.20101E+19,二是导入ArcGIS后字段值变浮点导致连接关联失败。如果原始数据已经被Excel“格式化”坏了,唯一靠谱的后悔药是把该列在Excel里重新用“分列→文本格式”导入一次,先转文本再处理,而不是试图用公式还原数字,因为超出15位的精度已经永久丢失了。
4. 把清洗结果导入ArcGIS、数据库和Excel:坐标与字段格式的3个注意
4.1 Excel表格导入ArcGIS:先统一坐标系,再谈叠加
“excel 表格怎么导入arcgis10.8”是这份数据集最常带出的搜索词,也是很多GIS新手第一道坎。ArcGIS里导入Excel的路径很直接:把xlsx直接拖进ArcMap或ArcGIS Pro,表格会显示出来,在表格上右键选Display XY Data,X字段选经度列,Y字段选纬度列,生成事件点图层。ArcGIS Pro里对应操作是右键图层选Create Points From Table,参数逻辑相同。
但这里有个最容易翻车的环节:坐标系设置。国内大部分POI数据来源是高德或腾讯,坐标是GCJ-02(火星坐标),而ArcGIS默认底图和很多公开底图用的是WGS84,两个坐标系差别约几百米。如果直接把数据定义成WGS84再叠加到底图上,所有景区点位会整体偏移,看起来就像“全部点飞到了街道另一边”。我一般先判断坐标来源:如果Excel里有source字段或者README提到高德/腾讯,就先定义GCJ-02;如果来源是公开的地理信息平台,通常是CGCS2000或WGS84。没有来源说明时,抽样取几个点跟天地图对比,偏移几百米的就是GCJ-02,几乎重合的就是WGS84。确认后再在图层属性里重投影到目标坐标系,不要在图面上手动“校正”,那会把所有其他属性一起拉歪。
注意:坐标系判断错误是这份数据导入ArcGIS后最常见的翻车点,比字段缺失更隐蔽。
另一个容易忽略的问题是X、Y字段选反。Excel表格里“经度”和“纬度”如果列顺序是纬度在前经度在后,新手容易按视觉顺序把X选成纬度列,结果是所有点南北换位,一部分落在国境线外。导入后先缩放到全图看一眼大致轮廓,如果点的分布像一面镜子一样横跨太平洋,别怀疑数据坏了,先查X、Y字段有没有选反。
4.2 文本与编号字段:让Excel别再自作主张
Excel对长数字的自动转换,是景区数据集清洗里最令人头疼的行为之一。前面第3章提过用astype(str)预处理,这里再说进入ArcGIS之后的情况:一旦字段在Excel里已经是科学计数法,哪怕导入ArcGIS成功,连表操作也会因为字段类型变成了浮点而匹配失败。所以做导入前的Excel检查时,最好扫一遍每一列的字段类型,把编号、电话这类长数字列全部标为文本。
这里有个判断技巧:景区编号如果带字母,比如320101A001,Excel不会触发科学计数法;但如果编号是纯数字,比如20220603001,很危险。所以数据里看到纯数字ID,直接预警。已损坏的xlsx可以用“分列”批量修复,选中整列,数据→分列→分隔符号→列数据格式选“文本”。这个方法只对还没被四舍五入的数据有效,如果单元格里已经显示成0了,回原始JSON重新抽一遍,别在原地纠结。
4.3 导入数据库:用SQLAlchemy定死字段类型,避免手工导入悬浮
做省级或全国级景区分析时,数据落进PostgreSQL或MySQL比直接拖Excel靠谱得多。手工导入向导看似快,但字段类型猜错、编码错乱这类问题会反复出现,尤其是中文乱码和长文本截断。我一般用Python的SQLAlchemy直接建表写入,字段类型在写入时就定死:
from sqlalchemy import create_engine, String, Float, Date import pandas as pd engine = create_engine("postgresql://your_user:your_password@localhost:5432/tourism") df = pd.read_excel("attractions_cleaned.xlsx") df.to_sql( "scenic_spot", con=engine, if_exists="replace", index=False, dtype={ "景区名称": String(200), "景区等级": String(10), "省份": String(50), "经度": Float, "纬度": Float, "更新时间": Date, }, )to_sql的dtype参数是很多数据岗容易漏掉的,不写的话pandas会把所有字符串都推断成text类型,后面做索引、做join都慢。把景区名称定成String(200),等级定成String(10),字段语义就清晰了。经纬度用Float没问题,但如果用PostgreSQL并打算做空间查询,更好的做法是建表后用Geometry类型把经纬度转成geometry点列,加上空间索引,后面的点面关联会快一个数量级。
如果公司环境只有MySQL,建库时用utf8mb4,否则省份字段的中文排序和筛选会乱。另外,入库之后别急着删中间文件,我习惯保留一份attractions_cleaned.xlsx作为“对账表”,数据库是给程序用的,Excel是给业务同事对口径用的,两边都留一份,后面出现“你的景区数量和我的不一样”的争论时,直接对比源头文件最快。
5. 排查与避坑:景区数据集里最常见的5个脏数据场景
5.1 经纬度填反:所有点都飞到国境线外
现象:把Excel导入ArcGIS后,全图点位的分布看起来像一条横贯南北的镜像带,或有一批点直接飘到海里、国境外。
原因:数据来源方在写导出脚本时,把lng和lat两个字段赋值反了。这在爬虫落盘、人工整理和接口拼接三类数据源里都出现过,尤其当原始字段名是x、y而不是lng、lat时,使用者很容易按自己的习惯把x当经度、y当纬度,结果恰好相反。
解决:先抽样检查。取3到5条知名景区,在在线地图上人工对照,确认经纬度和景区名是否对应。如果全反,直接交换两列:
df[["经度", "纬度"]] = df[["纬度", "经度"]]交换后重新输出,再做一次抽检。另外一个辅助手段是做个中国范围边界检查,经度在73到136、纬度在3到54之外的点直接标记为异常。我把经纬度列名统一改成lng和lat,而不是保留x、y,从命名上杜绝二次踩坑。
5.2 GCJ-02与WGS84混用:点位整体偏移几百米
现象:单个点位看着没问题,但把整个图层叠加到天地图或OSM底图上时,所有点在某个方向上整齐平移了几百米,景区点位落在街边甚至河中央。
原因:数据包含两套坐标系来源。主表坐标来自高德API,是GCJ-02,而补充的几百条景区来自某省级平台,是WGS84,清洗时没有做口径统一,直接在软件里合并了。
解决:合并前给每条记录加source字段,标明坐标来源。高德和腾讯来的统一做GCJ-02转WGS84的换算,注意转换不是简单加一个固定偏移量,不同经纬度偏移量不同,公开发表的转换公式很多,写成一个函数统一调用即可。转换后再和已知WGS84的点做抽检,偏差在两米内才算过。还有一种情况是同一份数据里两条记录坐标一个来自高德一个来自百度,能差出几百米,处理原则一样:先识别再统一。
5.3 同一个景区重复出现:联合键没定好
现象:统计全国景区数量时得出数值偏大,筛某个省的数据发现有的景区出现了两次,名称完全相同,但等级或坐标有一点点差异。
原因:数据是把国家名录、省级名录、市级推荐名单合并出来的,合并时只按名称join,而不同来源对名称的写法不完全一致,比如“西湖”和“杭州西湖风景名胜区”。
解决:去重键不能只用名称,我一般用“标准名称+区县+经纬度四舍五入到小数点后4位”三层组合来判断是否同一个景区。先做名称归一化,去掉“风景名胜区”“景区”这类后缀,再按组合键去重。去重后保留哪条记录也要定策略,我一般保留坐标精度更高且更新时间更新的那条,而不是随机留一条。
5.4 繁体字与全角空格:匹配与筛选全面失效
现象:Excel里筛选“华山”筛选不出来,搜索“崋山”才看到记录;或者某个景区的区县字段带全角空格,导致按“东城区”分组时该景区被单分到一类。
原因:原始网页的编码不规范,简体繁体和全角半角混用,爬虫抓到什么存什么,落到JSON后再转Excel,肉眼很难发现全角空格和普通空格的区别。
解决:清洗阶段加一轮字符归一化,把全角字符转半角,繁体转简体,去掉首尾空白,再把括号统一成半角。写一个normalize函数统一处理所有文本列,处理完后再做去重和join,否则前面的关联操作全是白做。这一步对后续用Excel的VLOOKUP或者数据库的字符串join影响最大。
5.5 时间戳过期:把2022年快照当最新名录用
现象:做景区等级统计分析时,“5A景区数量”比官方最新公布的数值少或多十几家。
原因:这份数据的时间戳是2022-06-03,之后有景区升5A、有景区被除名,数据快照本身和当下统计口径不一致。
解决:在分析代码里把数据集版本号写进参数,比如dataset_version="2022-06-03",所有输出图表标题都带版本标记;如果只需要最新名单,去官方名录接口更新一次,不要在旧数据上打补丁,因为补丁很难补全所有变化。另外,凡是跨年份对比,都用同一数据源同一时间戳的两份快照,不要拿2022年的数据去对2024年的官方口径。版本号也写进文件名,而不是只在报告中标注一行字,避免后续同事误用。
6. 进阶技巧:用坐标列做经纬度核验,把坏点拦在入库之前
6.1 范围加精度双检查:用30行代码过滤无效坐标
进入GIS或数据库之前,先做一步纯Python的坐标核验,能拦截掉上文提到的绝大多数坐标问题。这个检查很简单,但很有效:
def check_lnglat(lng: float, lat: float) -> str: if lng is None or lat is None: return "缺失" if not (73 <= lng <= 136 and 3 <= lat <= 54): return "越界" if round(lng, 4) == 0.0 or round(lat, 4) == 0.0: return "零值" return "正常"这里用到的73到136、3到54是中国大陆及周边海域的大致经纬度范围,不算精准国界,但用来做异常筛子足够。零值检查很关键,很多字段缺值时会被脚本填成0.0,0经度落在几内亚湾,0纬度落在赤道,都比空缺值更容易被下游直接使用。把检查结果加成一列,后续无论入库还是导入ArcGIS都有据可查。
6.2 坐标精度决定热力分辨率:先看小数位再谈网格聚合
最后一个建议和可视化相关。很多人拿这份数据做景区热力图,结果发现热度块又大又糊,原因多半是坐标小数位不足。经度小数点后4位约对应11米,两位小数则降到约1公里,如果数据里坐标只保留两位小数,热力图的网格基本糊成一团。
我习惯在清洗后加一行精度统计,看看经度字段的小数位分布。低于4位的列,回到JSON原文看是否有高精度坐标可补;补不了就降低预期,用市级聚合代替点级热力。坐标精度这件事,数据源头定了就很难靠后处理找回,这也是我反复强调保留JSON原文件的原因。
前年我接一个文旅项目,五十多个景区点导入后一半飞到了海上,查了半天是坐标列填反,从此任何景区数据到手,我都先跑一遍范围加精度检查再谈清洗。这份数据集的定位我一直当成“底料”:能直接看出全国A级景区分布、能做省级对比、能支撑粗糙的密度分析,但别指望它替代实时名录。把它洗干净、把坐标系定准、把口径写清楚,再用时心里就有底了,希望帮到你。
本文还有配套的精品资源,点击获取