ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

全国旅游景点POI数据处理实战:从7z解压到清洗可视化

全国旅游景点POI数据处理实战:从7z解压到清洗可视化 简介全国旅游景点SQL数据包面向旅游数据分析、GIS可视化、地图产品开发以及相关学术研究场景收录了约32万条全国景点记录可直接导入MySQL查询使用。资源以7z压缩后仅11.01MB包内包含1个SQL文件表结构涉及id、title、tel、add、type、areaid、poiid、gcjx、gcjy、gpsx、gpsy等字段完整覆盖景点名称、联系电话、地址、类型归属、行政区划标识及经纬度坐标等关键信息。数据总量达324498项并附带北京动物园等样例记录便于理解字段的实际取值与存储方式。读者拿到后即可快速建表导入用于构建景点检索系统、旅游APP后端数据、区域热力分析或各类数据可视化项目。已有4653人学习下载适合需要结构化地理数据的开发者和数据分析师直接取用。1. 32万条全国旅游景点数据.7z一份还没打开就让人头疼的压缩包第一次拿到「32万条全国旅游景点数据.7z」这个文件时大多数人的第一反应是先解压再说结果要么卡在 7z 命令不存在要么解压出来一堆乱码甚至打开 CSV 才发现经纬度整列都是反的。这个压缩包的本质是一份覆盖全国的大规模 POI 数据解压后通常是一张带景点名称、省份、城市、经纬度、景区等级和简介的结构化表适合做旅游客流分析、景区分布可视化、门票定价研究或地理信息系统的基础数据源。但真正值钱的不是文件本身而是你拿到手之后能否把它清洗成一张能直接进 pandas 的表。这篇笔记就是围绕这个目标写的从 7z 解压命令、数据生成格式到清洗和可视化一条链路走完。2. 这份景点数据里有什么字段结构、压缩比与先别急着解压的理由2.1 字段结构的合理预期旅游景点数据看起来是「一张表」但真实落地时字段千差万别。常见做法是核心字段至少包含景点名称、所在省份、城市和区县这是地理聚合分析的基本维度。其次是坐标字段一般是「经度,纬度」两个独立列或者兑成一个字符串如「116.397,39.909」稿子里写「纬度,经度」顺序的坑在数据源里非常常见清洗时一定要留个心眼。再往下是描述性字段包括景区等级5A、4A 这类评定信息、门票参考价、开放时间、景点简介以及分类标签。有些数据源会把「A 级景区」单独拆一个表用景区编号关联。拿到手先不要假设它一定是 CSV 或 JSON很多数据包为了压缩体积会直接用 SQLite 数据库文件配合 7z 压缩能达到非常夸张的压缩比解压后你面对的甚至不是一个文件而是一个目录树。2.2 数据量与压缩比7z 压缩包的玄学先给新手打个预防针32 万条听起来很多但纯文本 CSV 里的每条记录如果带上简介和详细地址行均长度很容易超过 1KB。也就是说解压后的文件体积可能在几百 MB 到 1GB 之间浮动7z 压缩包则通常在几十 MB 到一百多 MB 的水平。7z 格式在 LZMA2 算法下对这类重复度高的文本数据压缩率极高这也是为什么「32万条全国旅游景点数据.7z」会用 7z 而不是 zip 发布。这就带来一个很实际的问题解压后的临时文件直接把磁盘剩余空间吃剩一个零头。个人经验里最典型的翻车现场是先把压缩包解压到 C 盘默认目录结果 C 盘爆红整个系统卡到鼠标都挪不动。处理大压缩包的玄学是先看压缩包内部文件列表再预估解压后体积最后决定解压到哪块盘。2.3 三种常见的数据内容形态解压后有三种主流形态处理方式完全不同CSV 最通用但编码不确定。都是 CSV有的用 UTF-8 带 BOM有的用 GBK还有的是 UTF-8 无 BOM。直接用 pandas 读大概率会有一场乱码血泪战。JSON 文件适合嵌套结构比如每个景点带多张图片链接、多条交通信息。这类数据更「现代」但一行 JSON 可能就几 KB解析时内存压力更大。SQLite 数据库则是一步到位的玩法直接用 sqlite3 查询不需要一次性 load 进内存非常适合「32 万条全量分析」这种场景。你可以用一条命令先探明压缩包内部结构不需要解压任何东西# 列出压缩包内所有文件带大小信息 7z l 32万条全国旅游景点数据.7z这命令的输出会显示文件数、原始大小和压缩后大小。重点看 Original Size 那一列它就是解压后需要的磁盘空间。如果这个数字是几个 GB我建议直接改解压路径别犹豫。2.4 拿到数据后先做的一件事很多人的习惯是拿到压缩包直接右键解压然后打开文件看数据。经验丰富一点的工程师会反过来先补一条数据体检命令把编码格式和字段首行抓出来因为解压本身不费事费事的是解压后发现格式不对再重新处理。# -so 把内部文件内容输出到标准输出结合 head 只取前 1KB 做探测 7z e -so 32万条全国旅游景点数据.7z 景点数据.csv | head -c 1000注意这里的「景点数据.csv」需要替换成 2.1 节那条 7z l 命令看到的内部文件名。如果文件路径带中文目录在部分 Linux 终端下会出现转义问题保险做法是给文件名加引号。看到的前几行会直接告诉你三件事这是不是 CSV表头长什么样以及用的是 UTF-8 还是 GBK。这一步做完后面清洗才有方向。3. 7z 解压实操Linux、Windows 与 PyCharm 三环境跑通3.1 Linux 下解压 7z 文件安装 p7zip 与命令参数说明「linux解压7z文件」是这块最常见的搜索需求因为 Linux 默认不带 7z 支持报错信息往往是「7z: command not found」。你需要先装 p7zip 工具集# Debian/Ubuntu 系 sudo apt update sudo apt install p7zip-full # CentOS/RHEL 系需要先启用 EPEL 仓库 sudo yum install epel-release sudo yum install p7zip装完以后最核心的一条命令是# 解压到当前目录保留压缩包内目录结构 7z x 32万条全国旅游景点数据.7z # 指定解压目录例如 /data/tourism 7z x 32万条全国旅游景点数据.7z -o/data/tourism参数说明x是「解压并保留目录结构」这是处理多文件压缩包的推荐参数-o指定输出目录注意-o后面不能有空格写-o/data/tourism而不是-o /data/tourism。如果压缩包有密码追加-p你的密码比如7z x file.7z -p123456但这样会在 shell 历史里留下密码建议解压后再清理。还有一个容易被坑的点7z e这个命令会把所有文件解压到同一层目录不保留压缩包内的子目录。如果内部正好有两个不同目录下有同名文件7z e会直接覆盖或报错。判断自己该用x还是e的唯一标准就是看7z l列出来的路径是否带多层目录。3.2 Windows 上的图形界面与命令行解压Windows 用户通常直接装 7-Zip右键就能看到「解压到当前文件夹」和「解压到 32万条全国旅游景点数据\」两个选项。但这套图形界面操作在批处理或定时任务里没法用所以我一般会用命令行方式假设 7-Zip 装在默认路径# 在 cmd 或 PowerShell 中调用 7z.exe注意路径带引号 C:\Program Files\7-Zip\7z.exe x D:\data\32万条全国旅游景点数据.7z -oD:\data\tourism这里有个 Windows 特有的坑压缩包里如果是中文文件名在 cmd 默认的 GBK 代码页下看起来可能是乱码。解压前先执行chcp 65001切换到 UTF-8 代码页再跑 7z 命令文件名就能正常显示。另外 PowerShell 里调用 7z 时-o参数的路径如果带空格必须用双引号把整个-o参数包起来否则会解析为两个参数。3.3 PyCharm 里处理 7z用 py7zr 库把解压纳入项目流程「pycharm添加7z」这个搜索词其实是个概念混淆PyCharm 本身不解压压缩包真正要做的是给项目解释器安装一个能处理 7z 的 Python 库让解压、读取、清洗在同一条脚本链路里完成不需要来回切命令行。我常用的是 py7zr它把 7z 的解压能力直接以 API 形式暴露给 Python。# 在 PyCharm 的 Terminal 面板执行一次即可 # pip install py7zr然后解压到项目目录import py7zr # moder 表示只读打开extractall 解压到指定目录 with py7zr.SevenZipFile(32万条全国旅游景点数据.7z, moder) as z: z.extractall(path./tmp_tourism) # getnames() 返回压缩包内所有文件名 print(z.getnames()[:10])参数说明SevenZipFile的第一个参数是压缩包路径moder是读取模式如果你想创建压缩包则用modewextractall(path...)控制解压目标目录。py7zr 的历史版本中extract方法需要额外传path参数新版本里统一使用extractall如果你在旧代码里看到z.extract(path...)也能跑但建议按当前 API 写。解压完立刻交给 pandas 是这条链路最畅快的部分import pandas as pd import glob # 解压后再用 glob 找 CSV 文件避免硬编码文件名 csv_files glob.glob(./tmp_tourism/**/*.csv, recursiveTrue) df pd.read_csv(csv_files[0], encodingutf-8, nrows1000) print(df.shape, df.columns.tolist())参数说明glob.glob的recursiveTrue允许**匹配子目录这样不管解压出来是平铺文件还是多层目录都能找到。pd.read_csv里nrows1000是快速预览手段拿到文件先只看前 1000 行别上来就读全量这是防内存崩溃的常识。3.4 解压后的文件校验行数与哈希解压完成先别急着进分析花一分钟做三个验证。第一步是数行数# wc -l 统计行数CSV 带表头时要减 1 才是数据条数 wc -l tmp_tourism/*.csv第二步是核对文件大小确认和7z l输出的 Original Size 大致一致。第三步是比对哈希值如果数据源官方给了 MD5 或 SHA256用下面对比# 计算解压文件哈希 sha256sum tmp_tourism/*.csv这一步的作用不是形式主义而是确认压缩包在下载或传输过程中没有损坏。哈希不匹配基本意味着解压出来的文件有某处数据损坏这种隐患在 32 万条数据里极难定位返工成本极高。保留原始 7z 压缩包就是给自己留一颗后悔药重置数据时不需要重新下载。4. 从原始数据到分析表编码修复、去重与坐标规整4.1 编码问题UTF-8、GBK 与 BOM 的一次性排查解压只是热身真正的第一道坎是编码。读取时常见三种结果正常显示中文、中文变成「锟斤拷」、首列多一个看不见的\ufeff字符。前一种是编码用对了后两种分别对应 UTF-8 解码 GBK 数据、以及未处理 BOM 头。一个简单的排查办法是先用二进制模式读文件头判断编码再选择读取参数with open(csv_path, rb) as f: head_bytes f.read(100) # UTF-8 的 BOM 是 \xef\xbb\xbfGBK 没有 BOM 头 if head_bytes.startswith(b\xef\xbb\xbf): encoding utf-8-sig else: # 尝试用 GBK 解读如果抛异常就回到 UTF-8 try: head_bytes.decode(utf-8) encoding utf-8 except UnicodeDecodeError: encoding gbk这里的逻辑是有 BOM 就直接用utf-8-sig它能自动剥离 BOM没有 BOM 时先尝试 UTF-8 严格解码失败说明大概率是 GBK。实际项目中还有一种情况文件整体是 UTF-8但个别景区简介里混入了 GBK 编码的错误字节。这种情况更隐蔽读取时要用errorsreplace把坏字节替换成占位符避免整个文件读取中断。# 用 errorsreplace 兜底坏字节变成 而不是把整个 DataFrame 搞炸 df pd.read_csv(csv_path, encodingencoding, errorsreplace)参数说明errorsreplace是在解码遇到非法字节时用 UFFFD 替换而不是抛出异常。宁可个别字符坏掉也不能让 32 万条数据卡在读取这关。4.2 去重32 万条里混了多少重复数据旅游景点数据的重复率通常高得吓人。同一个景区可能被以「西湖景区」「杭州西湖」「西湖风景名胜区」三种名称收录还可能有同一名称在不同区县重复出现。直接按「景点名称」去重会误删按「名称城市坐标」去重才靠谱。# 先按文本去重再按坐标容差去重 df_dedup df.drop_duplicates(subset[景点名称, 城市], keepfirst) # 坐标去重保留每个景区第一条记录 df_dedup df_dedup.drop_duplicates(subset[经度, 纬度], keepfirst)注意这样写的前提是经纬度已经是数值类型。如果原始数据里经度是字符串「116.397,39.909」这种合并写法要先拆列再排序否则两个字段永远不会重复。还有一个容易误杀的场景同一名称在不同省份存在比如「人民公园」几乎每座城市都有。因此去重时subset必须把「省份」或「城市」放进去不然会把不同城市的同名景区删掉。4.3 坐标解析从字符串到可用于地图的数值数据里常见的坐标格式有三种经度,纬度逗号分隔、116.397 39.909空格分隔、以及116.39739.909这种带符号格式。更极端的是度分秒格式比如116°2349。写一个统一解析函数是最省心的做法import re def parse_lng_lat(coord_str): if not isinstance(coord_str, str): return None, None # 匹配数字和可选的小数点 nums re.findall(r[-]?\d\.?\d*, coord_str) if len(nums) 2: return None, None # 度分秒格式: 116°2349 - 116 23/60 49/3600 deg_match re.search(r(\d)°(\d)\(\d), coord_str) if deg_match: d, m, s map(int, deg_match.groups()) val d m / 60 s / 3600 # 原字符串带 - 号则取负数 return (-val if - in coord_str else val), None # 普通格式取前两个数值按「经度,纬度」的常规顺序返回 return float(nums[0]), float(nums[1])这段代码的关键在于它先分出度分秒和普通格式两条路径再对度分秒做换算。注意它默认返回顺序是「经度,纬度」如果你的数据源顺序相反可以在调用时把返回值对调。接着用中国地理范围做一次合法性过滤这是把坐标漂移问题挡在门口最粗暴也最有效的手段# 中国大致经纬度范围超出即判定为脏数据 df_valid df[(df[经度].between(73, 135)) (df[纬度].between(18, 54))]4.4 输出干净的表CSV 给人看Parquet 给程序用清洗完的数据最终要落盘。常见做法是同时输出两份一份给同事用 SQL 或 Excel 打开一份给你自己的后续分析流程用# utf-8-sig 保证 Excel 打开不乱码 df_clean.to_csv(tourism_clean.csv, indexFalse, encodingutf-8-sig) # parquet 是二进制列式格式读取快、体积小 df_clean.to_parquet(tourism_clean.parquet, indexFalse)这里有个细节to_csv用utf-8-sig而不是utf-8因为 Excel 在读取带 BOM 的 UTF-8 文件时才能正确识别中文。而 Parquet 自带 schema 信息列类型会原样保留下次pd.read_parquet直接得到一个干净的 DataFrame连dtype都不用重新指定。如果你用的是 pandas 2.xto_parquet需要先确认环境里有 pyarrow 或 fastparquet 库。缺库时运行会直接报ImportError这时执行pip install pyarrow5. 常见翻车点排查从 CRC 错误到经纬度漂移5.1 解压到一半报 CRC 错误进度条卡住不动现象7z x解压到 80% 左右弹出CRC Failed文件停留在目录里但无法确认是否完整。原因压缩包在下载过程中被截断或者存储介质的某个扇区损坏。CRC循环冗余校验是压缩包自带的完整性校验机制文件稍有损坏就会在解压时暴露。解决先别急着删除用7z t测试压缩包完整性确认损坏范围。如果哈希对不上重新下载源头文件如果是网络传输导致换下载方式并对比官方提供的哈希值。对于已经解压出来的半成品文件建议直接清掉因为损坏位置不确定后续读出来的数据可能在某一行悄悄出错。5.2 pandas 读取时内存爆炸8G 机器直接卡死现象pd.read_csv跑完一行代码内存占用从 1G 飙到 8G系统开始疯狂卡顿。翻车现场通常发生在读取全量文件时尤其是字段多、单行内容长的 CSV。原因pandas 默认按 Python 对象读入所有列字符串列的内存开销远大于文件本身。32 万条数据、每条含几百字简介时内存放大系数达到 510 倍很正常。解决读取时指定dtype压缩存储粒度用usecols只留你真正需要的字段必要时用chunksize分块读取。# 只读必要的列经度纬度降为 float32 cols [景点名称, 省份, 城市, 经度, 纬度, 景区等级, 简介] df pd.read_csv( tourism_clean.csv, usecolscols, dtype{经度: float32, 纬度: float32}, chunksize50000 )参数说明chunksize50000让read_csv变成一个可遍历的对象每次只处理 5 万行而不是一把梭全部读进内存。配合usecols去掉不需要的大字段8G 内存可以安稳跑完 32 万条。5.3 经纬度列顺序反了地图上的点全部漂移现象把数据画到地图上四川省的景区全部落在云南北京的景点漂到河北。看起来是地理分布错了实际是数据源的坐标顺序根本不是「经度,纬度」。原因不少采集工具从 GPS 模块拿到的是「纬度,经度」顺序写入 CSV 时却也直接把字段命名为「经度,纬度」字段名和实际内容对不上。解决抽样标定。取几个你确定的坐标北京故宫约 (116.397, 39.909)杭州西湖约 (120.130, 30.259)。如果数据里的「经度」列读数在 30 附近而不是 116 附近说明两列顺序反了用下面的命令对调# 发现经度列实际存了纬度直接交换两列 df[[经度, 纬度]] df[[纬度, 经度]].values注意这里用.values取数组再赋值避免 pandas 在列名替换时的索引对齐问题。5.4 打开 CSV 中文乱码出现「锟斤拷」和「口口口」现象用 pandas 或 Excel 打开解压后的 CSV景点名称变成一坨「锟斤拷」或者方块字符。原因文件本身是 GBK/GB2312 编码读入时用了 UTF-8。解码错了字节序列就映射成汉字「锟斤拷」这是 UTF-8 解码 GBK 字节流最典型的产物。解决按第 4.1 节的流程做编码探测。如果已经知道是 GBK在 Windows 下要把 CSV 转成 UTF-8 再给 Excel 用# Linux 下用 iconv 把 GBK 转成 UTF-8 iconv -f GBK -t UTF-8 原始文件.csv 转换后文件.csv历史经验是数据发布方如果是从旧版 Windows 系统导出优先怀疑 GBK如果发布方是后端程序生成的文件优先怀疑 UTF-8。两种都可以用 4.1 节的二进制探测代码区分。5.5 统计各省 5A 景区数量结果多出一个数量级现象按省份统计景区数量发现某些省份的 5A 景区数量比官方公布值高出一倍同一个「故宫」出现在好几个城市的记录里。原因数据源把同一景区的多平台收录条目都算进来了名称完全相同但来源不同、或者同一个景区被重复抓取多次。解决做多级去重第一级按名称、省份、城市、经纬度四个字段联合去重第二级对经纬度做空间容差合并。# 核心去重四字段联合确认唯一性 df_uniq df.drop_duplicates( subset[景点名称, 省份, 城市, 经度, 纬度], keepfirst ) # 再按坐标格网去重放大 100 倍后取整坐标差 0.01 度以内的视为同一景点 df_uniq[grid] (df_uniq[经度] * 100).round() * 100000 (df_uniq[纬度] * 100).round() df_uniq df_uniq.drop_duplicates(subset[grid], keepfirst)参数说明(经度 * 100).round()的意思是保留小数点后两位的粗粒度两个坐标相差在 0.01 度以内时会被归到同一个格网。这里的 100 就是容差系数数值越大容差越小、去重越严格需要根据你实际数据的精度调整。6. 进阶玩法把 32 万条数据渲染成全国分布图数据清洗到这个阶段可以做一个非常有说服力的验证把 32 万条景点全部画到一张全国地图上。这一步既检验坐标清洗成果也直接把你之前所有处理的结果可视化。建议先用sample抽样比如抽 5 万条渲染因为浏览器和前端图表库一次性处理 32 万个散点会明显掉帧。import plotly.express as px # 随机抽样并指定随机种子保证结果可复现 df_sample df_clean.sample(n50000, random_state42) # scatter_geo 不需要地图服务 key开箱可用 fig px.scatter_geo( df_sample, lat纬度, lon经度, color省份, scopechina, title全国旅游景点分布抽样 ) fig.show()参数说明random_state42让后续每次运行抽到相同样本方便对比验证scopechina把地图视野限制在中国区域内同时会隐藏经纬度越界的数据点。渲染完成后把图放大到四川、广东、黑龙江几个省份凭直觉核对一下局部密度再抽查几个已知景点坐标验证就基本结束了。除了散点图这 32 万条数据还可以做语义搜索。用str.contains搜索简介里的关键词比如「避暑」「古镇」「国家级自然保护区」把符合条件的子集提出来看分布这就是一份免费的旅游主题数据集。更进一步的做法是把每行简介用 TF-IDF 向量化然后用余弦相似度做推荐系统想打满效果的上限可以再跑一个本地 Embedding 模型做语义检索但这已经超出一次笔记的范畴了。我现在的习惯是所有地理数据压缩包在解压前先写一个十行的验证脚本把编码、行数、坐标范围三个指标一次性打出来确认后再进行主流程。这套流程始于「32万条全国旅游景点数据.7z」这个文件但它真正有用的部分是那个可复用的检查脚本和数据清洗框架。下次你拿到任何别的 ZIP、7z 或 TGZ 数据包这套流程还能原样用上希望帮到你。本文还有配套的精品资源点击获取
返回列表