全国景区数据集JSON与Excel转换清洗实战指南
发布时间:2026/10/2 9:17:35来源:尧图网络
简介这份全国旅游景区数据集收录约12000条景点记录覆盖1A至5A各等级景区适合旅游数据分析、地图可视化、行程规划、景区管理及旅游类应用开发等场景字段包含景点名称、所在城市、地址、等级、经度和纬度结构规范清晰方便直接进行筛选、统计与地理标记。压缩包内共2个文件分别是xlsx和json格式体积约1.26MBxlsx版本适合用Excel做透视表与图表分析json版本便于在Python、JavaScript等环境中解析可快速接入网站或小程序后端。该数据集目前已吸引4710人学习下载配套小程序可下载前预览校验数据拿到后既能绘制全国景区分布热力图也能按城市、等级做专题分析免去自行采集和清洗的繁琐工作是旅游类项目起步阶段的高性价比基础数据。1. 全国旅游景区数据集一张能直接喂给GIS和报表的底表拿到这份“全国旅游景区数据集含json、excel格式文件2022-06-03”时我第一反应不是急着看有多少条数据而是先确认两件事JSON和Excel两边字段对不对得上以及数据截止日期2022-06-03在目录里有没有体现。做景区POI和目的地分析的人应该能理解这个数据集的价值在于它把景区名称、等级、省市县、经纬度这些字段做成了一份开箱即用的底表不用再去地图厂商或各地文旅局官网逐个抓取清洗。适合旅游数据分析、GIS可视化、景区评级分布统计的同学直接拿来当分析起点尤其是那些不想从爬虫起步、只想快速验证分析流程的从业者。这篇笔记会从双格式差异、清洗流程、两种格式互相转换的实操一直写到底层踩坑和落地验证让你拿到数据后当天就能跑出第一版结果。2. 拆解JSON与Excel双格式先想清楚谁当主表我经手的几个景区数据集版本里JSON和Excel大多数不是同一份内容的两份拷贝而是同一数据的两种序列化形态Excel更贴近人眼审阅和报表统计JSON更贴近接口联调和GIS工具导入。如果一上来就拿Excel当标准答案后面做空间分析时很容易漏掉JSON里那些嵌套字段反过来只盯着JSON又会发现它在人工核对时难用至极。所以第一步不是急着转换而是先拆开两边看结构定下哪份当主表。2.1 JSON的嵌套结构为什么直接读Excel会丢层级先解决一个最实际的问题JSON文件用什么打开我一般不用系统自带的记事本除非文件只有几百KB否则一打开就是一行几十万字符眼睛直接花掉。常见做法是用VS Code、Notepad这类带语法高亮的编辑器或者直接在Python里用json.load()看一眼顶层结构这一步能省很多后面判断字段的力气。拿多数景区数据集来说JSON顶层是一个数组也就是常说的json数组每个数组元素对应一个景区的完整描述。我见过两种常见形态第一种是扁平结构每个元素长这样{ name: 故宫博物院, level: 5A, province: 北京市, city: 北京市, district: 东城区, longitude: 116.397, latitude: 39.918, address: 北京市东城区景山前街4号, intro: 明清两代皇宫…… }字段全部平铺Excel表格里有什么JSON里就有什么这种最省事。第二种是嵌套结构数据按省份分组外层是省份对象内层才是景区列表再往里还可能嵌着geo、tags这类子对象。这种结构如果直接用Excel打开嵌套层级会丢得一干二净。比如我见过某份数据的geo字段长这样geo: { lng: 116.397, lat: 39.918, source: gcj02 }Excel里如果只导出lng和lat两列source这个坐标系信息就丢了后面做地图投影时坐标偏移几百米你还以为是数据错了。所以拿到JSON先花十分钟把顶层结构和嵌套层级摸清楚比什么都重要。2.2 Excel侧的数据治理空值、重复和编码再来看Excel侧。我用pandas直接read_excel读这份数据集时遇到的第一个问题往往是编码和表头解析。CSV时代大家还会记得utf-8和gbk的恩怨Excel文件本身是二进制格式编码问题少一些但表头不干净的情况很常见——比如第一行是数据说明第二行才是字段名或者某些列名带着空格和换行符。处理这种数据集的稳妥姿势是先读两行看结构再决定跳行import pandas as pd df_raw pd.read_excel(全国旅游景区数据集.xlsx, headerNone, nrows5) print(df_raw.head(5))headerNone表示先不把任何一行当表头nrows5只取前5行这样一眼就能看出数据从第几行开始、字段名在第几行。不要直接header0去读遇到前两行是说明文字的情况字段名全会被顶错位后面所有清洗工作全白做。确定表头行之后再正式读取df pd.read_excel(全国旅游景区数据集.xlsx, header2) df.columns [str(col).strip() for col in df.columns] df df.dropna(howall)header2表示第三行才是真正的字段名行str(col).strip()去掉列名两边的空格和换行符dropna(howall)把整行都为空值的数据删掉。这么处理后再检查重复值和空值率print(df.duplicated(subset[name, province]).sum()) print(df.isna().sum())全国景区名录里同名景区在不同区县的情况非常常见所以我用name province两个字段联合判断重复而不是只看景区名。空值率如果超过5%说明这份数据的采集质量一般后面做统计时要特别标注。2.3 双格式校验用行数和主键对齐两份底表既然同一份数据集给了两种格式一个很自然的疑问是两个文件内容一样吗答案是大概率不完全一样。我遇到过JSON比Excel多几百条、或者Excel里修改过某几个景区等级的情况所以拿到数据的第一天就要做一次双格式校验确定以哪份为准。我的习惯是用景区名加省份作为联合主键把两边对一遍import pandas as pd import json df_excel pd.read_excel(全国旅游景区数据集.xlsx) with open(全国旅游景区数据集.json, r, encodingutf-8) as f: data_json json.load(f) df_json pd.json_normalize(data_json) df_excel[key] df_excel[name].astype(str) | df_excel[province].astype(str) df_json[key] df_json[name].astype(str) | df_json[province].astype(str) keys_excel set(df_excel[key]) keys_json set(df_json[key]) print(仅Excel有:, len(keys_excel - keys_json)) print(仅JSON有:, len(keys_json - keys_excel)) print(两边一致:, len(keys_excel keys_json))pd.json_normalize是pandas自带的JSON解析函数能把嵌套JSON自动展开成扁平DataFrame这是JSON转Excel时最常用的一个方法。set的差集和交集操作能快速找出两边不一致的数据量。如果差异超过1%我建议以JSON为准——因为JSON往往保留了更完整的原始字段Excel可能是供人翻阅的精简版。确定主表之后后续所有清洗和入库都围绕主表进行避免两边交替修改导致数据越弄越乱。3. JSON转Excel批量落库用Python脚本吃下整个数据集明确了主表之后最常见的落地需求就是把JSON那份转成Excel方便给业务同事审阅或者直接导入数据库。这里说的“转”不是另存为那么简单JSON里的嵌套结构、列表字段、缺失值都要在转换过程中处理干净否则转出来的Excel跟原JSON对不上反而增加沟通成本。3.1 开工前先做目录体检先把数据集目录里的文件列出来看清楚别急着写转换脚本。我见过有人直接把两个文件都读进内存才发现JSON文件有几个GB大小机器直接卡死。所以脚本第一步应该先看文件大小和行数ls -lh 全国旅游景区数据集/ du -sh 全国旅游景区数据集/*.json wc -l 全国旅游景区数据集/*.jsondu -sh看单个文件占多少磁盘空间wc -l看JSON文件有多少行。这里有个坑需要注意格式化过的JSON每个景区占多行wc -l统计的是总行数而不是景区条数。想看真实条数得用Python数数组长度import json with open(全国旅游景区数据集.json, r, encodingutf-8) as f: data json.load(f) print(景区总数:, len(data))如果文件过大导致json.load内存爆掉就得改用ijson这个流式解析库一行一行地读。目前绝大多数场景下景区数据集规模在万级到十几万级之间普通json.load足够应对但目录体检这一步能帮你提前判断数据规模避免后面转换时才发现内存不够。3.2 json_normalize拆平所有嵌套字段按数据集的实际结构来处理如果顶层是数组、每个元素是扁平的景区对象直接json_normalize就够了。但更常见的情况是里面有嵌套对象比如地址拆成了province、city、district三个字段或者geo对象里包含经纬度。这种情况下json_normalize的sep参数就派上用场了import pandas as pd import json with open(全国旅游景区数据集.json, r, encodingutf-8) as f: data json.load(f) df pd.json_normalize( data, sep_, max_level2 ) print(df.columns.tolist()) print(df.shape)sep_表示嵌套字段用下划线连接比如geo_lng、geo_lat、geo_source这样列名里不会出现点号避免后续导入数据库时列名被识别成特殊字符。max_level2控制只展开两层嵌套防止嵌套过深把列数撑到几十列。展开之后先看一眼df.columns确认字段名是否符合预期再决定是否需要进一步处理。需要注意json_normalize对缺失的嵌套字段会生成NaN而不是直接报错这是它的优点。比如某个景区没有tags字段展开后它就是空值不会导致整个转换中断。3.3 写出Excel并控制精度转成DataFrame之后最后一步是写出Excel。这里有两个容易翻车的地方一是经纬度精度二是长文本字段。float_cols [geo_lng, geo_lat] for col in float_cols: if col in df.columns: df[col] pd.to_numeric(df[col], errorscoerce).round(6) text_cols [name, intro, address, tags] for col in text_cols: if col in df.columns: df[col] df[col].astype(str) df.to_excel(景区数据_清洗版.xlsx, indexFalse, sheet_name景区名录)round(6)把经纬度保留到小数点后6位大约0.1米的精度足够日常分析使用。如果保留到小数点后9位Excel里会显示成一长串数字而且浮点误差会带来无意义的尾数。文本列统一转成str有两个作用一是防止intro这种长文本被pandas识别成浮点数而报错二是避免空值在Excel里显示成nan字符串。提示errorscoerce的意思是把无法转换成数字的值强制变成NaN不会因为某一行经纬度格式异常就让整个脚本崩溃。写完之后打开Excel抽查几行重点看经纬度、景区等级、省份这三列是否和JSON一致。我这边的经验是抽查比例不必太高但一定要包含等级为5A的景区——这类重点景区数据通常是人工核对过的准确率更高如果它们都错了说明源头数据老早就出了问题。4. 从Excel反推JSON把景区名录变成接口可用的数据JSON转Excel是给人和数据库用的反过来把Excel转成JSON则是给接口和前端用的。景区数据集最终常常要投到一个后台管理系统里或者对接给小程序、大屏展示。这种场景下前端要的不是一份宽表而是一个结构干净的JSON数组字段命名风格要统一嵌套层级要合理。4.1 openpyxl读Excel的稳定姿势读取Excel用pandas当然可以但pandas读出来是DataFrame再做逐行转JSON的映射时反而绕了一层。我更喜欢直接用openpyxl按行读这样字段的顺序、类型都由自己掌控from openpyxl import load_workbook wb load_workbook(景区数据_清洗版.xlsx, read_onlyTrue, data_onlyTrue) ws wb[景区名录] header_row None for row in ws.iter_rows(values_onlyTrue): if row[0] and row[0] ! 景区名称: break if row[0] 景区名称: header_row row break print(header_row)read_onlyTrue是只读模式加载大文件时内存占用很低data_onlyTrue表示读取公式计算后的结果而不是公式本身避免拿到一串SUM(...)字符串。iter_rows(values_onlyTrue)逐行生成元组配合一个循环就能精确定位表头所在行比pandas更直接。判断表头行的逻辑是如果当前行第一个单元格等于“景区名称”就认定这是表头行。这份数据集字段名里必然包含“景区名称”四个字所以用这个值做锚点最可靠——比数第几行更稳因为不同发布渠道的Excel表头位置可能不同。4.2 组装JSON数组的字段映射读到了表头接下来就是逐行读数据、组装成JSON数组。这一步的关键是字段映射——Excel里的中文列名要映射成接口约定的英文或拼音字段名同时把空值处理规则定好import json from openpyxl import load_workbook wb load_workbook(景区数据_清洗版.xlsx, read_onlyTrue, data_onlyTrue) ws wb[景区名录] headers [cell.value for cell in next(ws.iter_rows(min_row1, max_row1))] field_map { 景区名称: name, 等级: level, 省份: province, 城市: city, 区县: district, 经度: longitude, 纬度: latitude, 地址: address, 简介: intro, } result [] for row in ws.iter_rows(min_row2, values_onlyTrue): record {} for i, header in enumerate(headers): if header in field_map: value row[i] if value is None: continue record[field_map[header]] value if name in record and province in record: result.append(record) with open(景区数据_to_api.json, w, encodingutf-8) as f: json.dump(result, f, ensure_asciiFalse, indent2) print(导出条数:, len(result))field_map里的键是Excel列名值是接口字段名。循环里先把该行每个单元格值取出来如果值为空就直接跳过这个字段——接口场景里空字段和不存在的字段是等价的不需要写入null。最后的if name in record and province in record是兜底判断避免出现既没有名称也没有省份的脏数据混进接口返回。json.dump的三个参数各自有讲究ensure_asciiFalse保证中文原样输出而不是转成\uXXXXindent2让生成的JSON可读方便开发和联调时排查encodingutf-8保证文件编码正确。默认的ensure_asciiTrue会把所有中文转义文件打开后满屏\u开头的东西调试体验极差——这是新手最容易忽略的一个细节。4.3 导出的三条验证规则Excel转JSON的脚本跑完之后别急着提交给前端先做三条验证能挡掉八成的联调返工import json with open(景区数据_to_api.json, r, encodingutf-8) as f: data json.load(f) # 1. 条数对齐 print(JSON条数:, len(data)) # 2. 必填字段非空 empty_name [x for x in data if not x.get(name)] print(缺少name的记录:, len(empty_name)) # 3. 经纬度范围校验 bad_lng [x for x in data if x.get(longitude) and not (-180 x[longitude] 180)] bad_lat [x for x in data if x.get(latitude) and not (-90 x[latitude] 90)] print(经度越界:, len(bad_lng), 纬度越界:, len(bad_lat))这三条规则对应三个层面条数对齐是保证转换过程没有丢数据name非空保证记录的完整性经纬度范围保证空间数据可用。经度范围是-180到180纬度范围是-90到90如果出现经度390这种值说明源数据里混入了非经纬度字段或者坐标单位搞错了。第4条规则是我后来加的习惯——检查坐标系来源。数据集里如果标了gcj02高德、腾讯系坐标系直接拿去做WGS84的地图底图叠加位置会偏几百米。转换的时候如果发现JSON里有坐标系标识字段一定要原样保留不要只导出经纬度数字。5. 景区数据集落地的常见坑现象、原因与解法这份数据集的“坑”说多不多说少不少。下面这五条是我在多个版本的数据集上真实踩过的每一条都按现象到原因到解法的顺序写透希望能帮你少走弯路。5.1 Excel打开报“文件格式与扩展名不匹配”现象双击数据集里的Excel文件弹窗警告“文件格式与扩展名不匹配”点“是”之后文件能打开但某些工作表显示乱码或者部分列丢失。原因这个警告通常说明文件实际格式不是标准xlsx。有些渠道为了减小文件体积把xlsx改成zip再压缩或者直接把CSV内容换了个xlsx后缀。还有种情况是文件本身是xls2003格式后缀却是xlsx。解决先看文件头确认真实格式再决定用哪个引擎读取。检查方法是用file命令或Python的openpyxl试错file 全国旅游景区数据集.xlsx输出里会显示Microsoft Excel 2007或者Composite Document File。如果是后者说明实际是xls老格式用pandas.read_excel时要指定enginexlrd读取。如果输出直接告诉你这是CSV文本文件那就用pd.read_csv打开而不是硬改用Excel引擎。注意不要在这种文件上直接右键另存为xlsx骗自己底层格式没变该出的问题还得出。先把真实格式搞清楚再决定转换方案。5.2 景区等级字段显示成了数字现象Excel里“等级”这一列显示的是3、4、5这样的数字而不是“3A”“4A”“5A”。原因数据采集时把“5A”识别成了“5”后面的字母A被当成单位丢了。这种情况在复制网页表格时很常见有些采集工具把“5A级”拆分成了“5”和“A级”最终只保留了数字部分。还有一种可能是Excel的自动类型转换把字符串“5A”里的数字部分抽取出来整列转成了数值型。解决反向验证加正则修复。先用value_counts查看等级列的唯一值确认数字和A级两种形态同时存在import pandas as pd df pd.read_excel(全国旅游景区数据集.xlsx) print(df[等级].value_counts(dropnaFalse))确认问题后写个映射函数把数字转回A级格式。数字“5”对应“5A”“4”对应“4A”但要小心“5A级景区”和“5A”这两种原始值不要重复映射def fix_level(x): if str(x).isdigit(): return str(x) A return str(x) df[等级] df[等级].map(fix_level) print(df[等级].value_counts(dropnaFalse))修复后复查唯一值的数量5A、4A、3A、2A、1A应该都在且没有重复或空值。如果发现有“5AA”这种双字母拼接说明转换前有人手动改过数据需要再用正则re.sub(r(\d)A, r\1A, x)收一遍尾巴。5.3 经纬度精度莫名丢失现象JSON里的经纬度是116.397515这种6位小数转成Excel后变成了116.3975坐标精度从0.1米级退化到10米级有的更夸张直接变成116.4景区位置漂到隔壁街道去了。原因Excel默认的单元格格式是“常规”对于超过一定位数的小数会自动四舍五入显示真实值并没有丢但用户看表、复制数据时会复制到显示值。另外如果原始JSON里的经纬度是字符串类型pandas读进来后转成float时也可能被截断。解决转换时显式把经纬度列转成浮点数并设置单元格格式。用round(6)控制精度同时用openpyxl的单元格格式把小数位数固定下来from openpyxl.styles import numbers ws wb[景区名录] for row in ws.iter_rows(min_row2, min_col1, max_colws.max_column): for cell in row: if cell.column_letter in [G, H]: # 经纬度所在列 cell.number_format 0.000000number_format 0.000000强制单元格显示6位小数不管Excel的默认设置怎么变精度不会再被吞。这里还需要注意如果源数据里经纬度是gcj02坐标系做完转换后保留一个坐标系列名或备注别让下游使用者把GCJ-02的数据当WGS84直接用。5.4 景区等级值不统一现象同一份数据集里“5A”“5A级”“AAAAA”“5A景区”四种写法并存。原因数据来自多个渠道拼接。不同省份文旅局官网对景区等级的表述不同有的写“5A”有的写“AAAAA”有的在等级后面带了“级”字或“景区”二字。采集脚本没有做标准化直接原样入库。解决统一映射表把各种写法收敛成一种标准格式level_map { 5A: 5A, 5A级: 5A, 5A景区: 5A, AAAAA: 5A, 4A: 4A, 4A级: 4A, AAAA: 4A, # 类推 } df[等级] df[等级].map(level_map).fillna(df[等级])fillna(df[等级])的作用是把映射表里没覆盖到的值保留原样防止漏网之鱼被替换成空值。映射后再用一个去重统计确认df[等级].unique()应该只剩下标准化的A级串。做景区等级统计口径时这一条尤其关键——我就是因为没做标准化第一次输出5A景区数量时少算了几十家。5.5 从JSON解析时间字段时区被改现象数据里有update_time字段JSON里的原始值是2022-06-03T00:00:0008:00翻成DataFrame后变成了2022-06-02 16:00:00差8个小时。原因json_normalize解析ISO 8601时间字符串时自动把带时区的08:00转换成了UTC时间再存成datetime类型导致东八区的时间被回拨了8小时。pandas的datetime解析对带时区的字符串默认转UTC这是最容易被忽视的一个隐形坑。解决读取JSON前先关闭pandas的自动时间解析或者手动固定时区。我通常在json_normalize里把时间字段指定为字符串不交给pandas自动转df pd.json_normalize( data, sep_, max_level2 ) for col in df.columns: if time in col or date in col: df[col] df[col].astype(str).str.replace(08:00, , regexFalse) df[col] pd.to_datetime(df[col], format%Y-%m-%dT%H:%M:%S, errorscoerce)str.replace(08:00, , regexFalse)先去掉时区后缀再用format参数明确指定格式这样解析出来的时间就是原始的北京时间不再被转换成UTC。errorscoerce保证解析不了的值变成NaT而不是报错中断。如果你后续要把数据放进PostgreSQL的timestamptz字段保留时区后缀也行但Excel报表场景下统一用无时区的本地时间更省心。6. 进阶把数据集接到热力图与报表的最终验证数据清洗完、格式转换完最后还是得有个东西能证明这份数据真的好用。我的做法是拿这份景区数据集做一张景区分布热力图和一张等级统计报表这两个产物能同时验证空间字段和属性字段的质量比空口说“数据没问题”有说服力得多。先做热力图。常见做法是把处理后的数据转成GeoJSON或者直接加载到支持经纬度的GIS工具里。如果你不想走重工具直接用Python出图也行这里的关键是验证散点分布是否符合常识——5A景区应该集中在东部和中部西部稀疏import matplotlib.pyplot as plt df[longitude] pd.to_numeric(df[longitude], errorscoerce) df[latitude] pd.to_numeric(df[latitude], errorscoerce) fig, ax plt.subplots(figsize(12, 8)) ax.scatter(df[longitude], df[latitude], s1, alpha0.5) ax.set_xlabel(经度) ax.set_ylabel(纬度) plt.savefig(景区分布图.png, dpi150)dpi150保证出图清晰度够用。跑完看一眼图如果出现了经纬度为0的点跑到地图左下角或者明显的经纬度重复聚类说明数据清洗还没到位回头查第5章的几个坑。再做等级统计报表。这条能验证属性字段的标准化是不是真的做彻底了level_stats df[等级].value_counts().reindex([5A, 4A, 3A, 2A, 1A]) print(level_stats)reindex强制按5A到1A的顺序排列如果某个等级是缺失的会显示NaN而不是报错方便你判断数据覆盖情况。正常情况下5A最少逐级递增如果出现3A数量异常低于4A的情况大概率是等级标准化没做全。最后说一个小习惯处理这种双格式数据集时我坚持“同一份数据只维护一个主表版本”无论JSON还是Excel后续所有更新都先改主表再重新生成另一份。原因很简单——双格式的初衷是方便不同角色使用不是创造两个数据源。如果两边各自修改最终你会发现JSON和Excel对同一个景区的等级描述已经对不上了这时候再来做数据对齐成本比重新抓一遍数据还高。希望这个习惯对你也有参考价值帮你在拿到类似数据集时少走几步弯路。本文还有配套的精品资源点击获取
网站建设高端定制企业官网