简介面向交通数据分析、城市规划与GIS应用开发者这份高德交通态势爬取资源使用脚本调用高德地图接口获取实时路况、拥堵指数等态势信息并兼顾请求限速与反爬策略可直接用于后续交通流分析及ArcGIS空间数据预处理。资源包共2个文件包含一个Python脚本与一个ZIP压缩包整体大小仅2KB非常轻量。目前已有1867人学习下载。借助该资源读者可掌握高德接口的参数构造与返回数据解析方法理解交通数据清洗、格式转换与异常处理等关键环节同时脚本还提供了导入ArcGIS前处理的使用思路并涉及数据存储、更新频率及合规性等实用建议能够帮助开发者快速搭建一套可运行的交通态势抓取流程。该资源适合交通、GIS及相关专业的学生和研究者快速上手也方便有经验的开发者直接改造复用。1. 高德交通态势爬取先想清楚你要的是路况数据还是道路级事件把高德交通态势爬取做成一个能每天稳定出数的任务我前后翻过三次车一次是接口只返回视口内的路况全城数据永远缺一半一次是速度字段单位没换算存下来的数值直接没法用于统计还有一次是坐标串没做首尾闭合去重道路长度被算了两遍。这篇笔记就把这套爬取方案怎么设计、关键参数怎么调、以及上述坑的排查过程写清楚。目标很具体让没跑过交通数据的新手也能在半天内把单城市的实时交通态势落成 CSV 文件然后决定要不要继续做全城网格覆盖和历史趋势分析。读完这个标题所代表的方案能不能解决你手头的问题你应该能自己判断。2. 看懂交通态势接口的返回结构再决定按城市还是按网格采集2.1 交通态势字段拆解道路分段、速度、拥堵等级与坐标串交通态势数据不是完整的道路网而是把路网切成若干段通常叫 link 或道路分段每段给出一份实时快照。我第一次拿到的返回里每条记录包含四类关键字段道路分段标识、当前速度、拥堵等级、坐标串。理解这四个字段是后面所有统计的前提。分段标识经常让人误以为是道路名实际大多数情况下它是内部编号道路名称字段经常为空所以后续做区域分析时我习惯把坐标串作为关联空间属性主键而不是靠路名。速度字段有两种常见单位米/秒或千米/小时。同一家接口在不同版本里都可能切换单位我踩过的坑是某个版本改了单位而爬虫没做换算导致整批存档的速度值跑偏。拥堵等级多用 0~4 或 0~9 表示常见映射是 0 未知、1 畅通、2 缓慢、3 拥堵、4 严重拥堵。有的接口用 0~9 时5 以下算畅通6 以上算拥堵归一化时需要自己定阈值。坐标串通常是分号分隔的经纬度对序列例如116.3,39.9;116.31,39.91。它表示该道路分段的折线路径坐标精确到小数点后多位采集后应原样保存不要在解析阶段就做四舍五入否则后面做长度计算和空间关联时偏差放大。除了这四个字段返回里可能还带事件列表比如事故、施工、管制。这类数据属于交通事件而不是交通态势统计拥堵指数时不要混入否则会重复计算。2.2 正式配额接口与网页端接口key、限流和版权边界怎么选高德交通态势爬取最常见的两条路一条是地图厂商开放平台正式开通的交通态势类接口需要申请 key 并按配额调用另一条是网页端地图在页面渲染时自发请求的接口抓包拿到地址就能调用。对比项正式配额接口网页端抓包接口获取门槛注册 key部分能力要求企业认证打开网页开发者工具即可定位稳定性字段与配额相对稳定版本迭代随时可能失效限流方式按配额与并发数控制按 IP 和请求频率控制数据范围随开通的产品线不同跟随视口或城市范围适用场景内部系统、数据产品个人研究、原型验证、小规模采集我一般的做法是先抓包确认数据结构用它把流程跑通确认数据对本项目有价值之后再去申请正式配额避免一开始就被配额卡住开发进度。注意网页端接口的响应内容可能被压缩或混淆抓包时优先看预解析后的 JSON 预览如果看不到就找源文件里的明文 JSON 片段。从稳定性看正式配额接口的字段不一定不变但变化频率远低于网页端。如果你的目标是做季度级历史趋势两者都要在解析层加字段漂移保护解析时先检查必需字段是否存在不存在则告警而不要静默写空。商用场景的授权边界需要你自己确认但我给团队的建议很直接凡是出现在对外产品里的数据一律走正式配额网页端接口只用于内部原型和算法验证。2.3 采集范围取舍按城市编码一次拉取还是按网格逐块补齐很多城市的交通态势接口你只传一个城市编码它返回的其实只是主城区或默认视口范围不是整个行政区。这个现象我在多个城市复现过郊区快速路和乡镇道路基本拿不到。页面地图能看到什么范围请求往往就返回什么范围这是个普遍规律。解决办法是网格化采集把城市划成若干矩形格子对每个格子分别请求然后合并去重。网格宽度我常用 0.05 度约 5~6 公里一个中型城市大约 30~60 个格子。格子太大边缘道路切在视口外会被接口裁掉格子太小请求次数翻倍限流风险上升。网格顺序也有讲究。从城市中心往外围逐圈请求比随机乱序更不容易触发频率限制。每两个请求之间至少加 200~500 毫秒延时不要用固定 200 毫秒加一点随机抖动更像真实用户操作。网格重叠问题容易被忽略。相邻格子的道路分段在边界处可能重复返回合并时以道路分段 ID 加坐标串首尾点作为主键去重。网格采集在数据完整性和请求次数上都要权衡一次城市级全量网格快照请求数多频率要相应调低。3. 用 Python 实现高德交通态势爬取从单次请求到增量落盘3.1 先定位接口浏览器抓包三步找出交通态势请求拿到真实请求地址是整个流程的第一步。打开地图网页按 F12 进入开发者工具切到 Network 面板在过滤器里选择 Fetch/XHR然后拖动地图让视口完成一次城市中心到边缘的来回移动。此时面板里会出现大批请求交通态势相关的请求名通常带有 traffic、road、status 这类词。选中请求后看右侧的请求网址和 Query String Parameters重点关注这几个参数城市编码、级别、视口边界或中心点、时间戳。确认是目标接口后把请求网址、完整查询参数、请求头里的 User-Agent、Referer、Accept 逐项记录下来。请求头里的 Referer 很关键不少接口用它做来源校验不带或带错会导致返回空数据。定位完成后可以先在浏览器地址栏重放一次请求或复制为 cURL 后在命令行执行。能拿到 JSON 就说明这个接口可用。如果返回密文或压缩乱码检查请求头里是否缺少 Accept-Encoding 或 gzip这不是接口问题是请求头不完整。3.2 单城市交通态势请求函数与关键参数说明下面这段是我常用的请求骨架URL 部分需要替换成你抓包得到的真实地址import requests import time CITY_ADCODE 110000 # 城市行政区划编码替换为目标城市 URL https://map.example.com/traffic/status # 替换为抓包得到的真实接口 def fetch_traffic(adcode: str, view: str None) - dict: 拉取指定城市的交通态势快照。 view 传 左下经度,左下纬度;右上经度,右上纬度None 表示默认城市范围。 params { city: adcode, # 城市编码 level: 6, # 6道路级5区域级 view: view, # 可选网格化时传入 timestamp: int(time.time() * 1000), # 动态参数防缓存 } headers { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64), # 与抓包一致 Referer: https://map.example.com/, # 来源校验必须与抓包一致 } resp requests.get(URL, paramsparams, headersheaders, timeout10) resp.raise_for_status() data resp.json() if str(data.get(status)) ! 1: # 各接口成功值不同以抓包为准 raise RuntimeError(f接口返回异常: {data.get(info)}) return data这段代码的逻辑很直白构造参数、带请求头发送、校验业务状态码。参数说明如下city是行政区划编码一般在接口返回里能看到对应字段level决定粒度道路级数据量大但精度高做区域拥堵指数用道路级view是网格化采集的关键不传时很多接口默认返回城市中心范围timestamp是常见的防缓存参数不加可能导致返回旧数据。User-Agent和Referer必须与初次抓包完全一致不要用库默认值否则容易被识别为脚本。timeout10表示单次请求超时 10 秒局域网或弱网环境要调到 15~20 秒。返回内容的status字段含义因接口而异我见过返回 1 表示成功也见过返回字符串 true 的所以统一转字符串比较稳妥。3.3 解析返回 JSON提取道路分段、速度与拥堵等级拿到 JSON 后难点在解析嵌套结构和清洗字段。不同接口字段名不同下面的解析函数以我遇到过的普遍结构为例import math import json def parse_traffic(data: dict) - list[dict]: 把接口返回的 trafficinfo 数组转成标准行记录。 rows [] for item in data.get(trafficinfo, []): coords_raw item.get(polyline, ) # lon,lat;lon,lat level normalize_level(str(item.get(level))) # 统一成 0~4 speed speed_to_kmh(item.get(speed, 0)) # 统一成 km/h rows.append({ road_id: item.get(road_id, ), speed_kmh: speed, level: level, polyline: coords_raw, length_km: calc_length(coords_raw), }) return rows def normalize_level(v: str) - int: 拥堵等级归一化0 未知、1 畅通、2 缓慢、3 拥堵、4 严重拥堵。 mapping {0: 0, 1: 1, 2: 2, 3: 3, 4: 4, 5: 1, 6: 2, 7: 3, 8: 4, 9: 4} return mapping.get(v, 0) def speed_to_kmh(v) - float: 速度归一化为 km/h。接口可能是 m/s也可能是 km/h按实测校准。 f float(v) if f is None or f 0: return 0.0 # 如果抓包发现字段是 m/s 且数值普遍较小就按 m/s 换算 return round(f * 3.6, 2) if f 50 else round(f, 2) def calc_length(coords_raw: str) - float: 把折线坐标串按 haversine 公式累加长度单位 km。 coords [] for pair in coords_raw.split(;): lon, lat map(float, pair.split(,)) coords.append((lon, lat)) if len(coords) 2: return 0.0 # 去掉首尾重复点避免闭环闭合点导致长度翻倍 if coords[0] coords[-1]: coords coords[:-1] total 0.0 for i in range(len(coords) - 1): total haversine(coords[i], coords[i 1]) return round(total, 3) def haversine(p1: tuple, p2: tuple) - float: 两点球面距离单位 km。 lon1, lat1 p1 lon2, lat2 p2 lat1, lat2 math.radians(lat1), math.radians(lat2) dlat lat2 - lat1 dlon math.radians(lon2 - lon1) a math.sin(dlat / 2) ** 2 math.cos(lat1) * math.cos(lat2) * math.sin(dlon / 2) ** 2 return 6371.0 * 2 * math.asin(math.sqrt(a))解析逻辑的关键是按分号和逗号拆分坐标串再按 haversine 公式计算分段里程。normalize_level把不同量纲的等级统一成 0~4方便跨时段比较。speed_to_kmh里做了单位自适应判断数值普遍小于 50 时按米/秒换算否则按千米/小时保留这个判断需要你用抓包数据校准。提示polyline拆分时注意尾巴上的空字符串split(;)得到的结果末尾可能有空元素计算前先过滤否则 float() 会报错。计算长度前先判断首尾是否相同并去掉重复点这个操作叫首尾闭环去重。很多接口返回的折线是闭合环不去重的话整条路段的长度会被多算约等于一个来回统计区域拥堵里程时误差很大。3.4 定时采集与增量存储把脚本挂成常驻任务单次快照只是开始交通态势的价值来自持续累计。我通常用 cron 控制频率每个快照独立保存为一个文件文件名带时间戳天然支持增量存储。采集频率看业务需求做早晚高峰对比的就每天抓两次做全天分析的最多每 5 分钟一次再频繁会触发限流。import json import time def save_snapshot(data: dict, save_dir: str) - str: 把原始返回按时间戳落盘返回文件路径。 ts time.strftime(%Y%m%d_%H%M%S) path f{save_dir}/{ts}.json with open(path, w, encodingutf-8) as f: json.dump(data, f, ensure_asciiFalse) return path对应到系统层面的定时任务我习惯用 crontab 而不是 Python 内部循环避免脚本因异常退出后线程死掉# 每天 07:30 和 17:30 各拉一次快照日志追加 30 7,17 * * * cd /path/to/project python3 fetch_traffic.py --city 110000 --save_dir data/ logs/fetch.log 21增量存储的关键是文件命名规则日期时间戳精确到秒同一秒内的快照会互相覆盖如果频率高于每分钟一次就要在命名里增加毫秒字段。落盘建议保留原始 JSON 而不是只存解析后的 CSV因为字段漂移后原始数据可以重新解析补救CSV 则已定型无法还原。解析后的标准化数据单独存一份 CSV主要给后续分析用。解析脚本和落盘脚本分开写采集失败时只丢原始文件已解析的历史数据不受影响。每次采集完成后立刻做数据量校验比如当天快照的记录数比昨天同时段骤减 50%大概率是接口返回异常可以直接告警而不是等到入库后才发现。4. 高德交通态势爬取的避坑记录五个翻车点与排查思路4.1 只返回城中部分、郊区全空视口参数与服务范围边界现象第一次跑全城采集结果 CSV 里的道路集中在市中心郊区快速路一条都没有同一天的官网地图上却能看见郊区路况。原因大部分接口并不按行政区划返回全量数据而是以请求参数里的视口边界或默认视口为准我当时只传了城市编码接口按默认视口兜底返回了主城区。解决抓包时观察地图页面每次拖动视口发出去的边界参数把它们补齐到请求里之后手动遍历网格逐块请求。全城网格覆盖后数据量立刻正常郊区道路也能拿到只是网格边界存在轻微重叠要靠首尾点去重处理。4.2 速度字段全是 0字段取错与单位换算错误现象某一天的存档里所有道路速度整整齐齐全是 0但拥堵等级正常地图页面显示的路况也是正常状态。原因接口升级后多了一个预测速度字段我解析里取的字段名指向了新的空值字段真正速度字段换了名。解决把原始 JSON 和页面显示的路况逐条对比确认哪个字段是速度哪个是预测值同时检查单位。速度字段单位还有一种坑上海某段路返回的数值是 12看起来像 m/s 实际是 km/h直接算拥堵指数会离谱。每换一次接口版本都抽样对比 20 条记录再定单位。4.3 请求不带 Referer 被限流来源校验与频率控制现象连续请求 30 次后接口开始返回空数组再过一会直接报频繁错误进程崩掉。原因请求次数超过接口阈值且我用的是 Python 默认请求头没有带浏览器环境标识正好触发来源校验。解决把网页抓包里的完整请求头照搬过来重点检查 User-Agent、Referer 和 Accept三个值缺一不可。同时给每次请求之间加 0.3~1.0 秒随机延时不要用固定间隔。之后连续跑了几千次请求没有再限流说明频率控制和来源伪装是限流的主因。4.4 坐标串在底图上偏移坐标格式与经纬度顺序搞反现象把解析出的折线画到一份 WGS-84 底图上道路位置整体偏移了几百米和卫星影像对不上。原因接口返回的坐标是 GCJ-02 加密坐标底图是 GPS 原始坐标两者存在系统性偏差另一个常见问题是经纬度顺序接口返回的是 纬度,经度 而不是直觉的 经度,纬度。解决确认坐标顺序后再写解析函数顺序错了每一条记录都会画偏坐标系不一致的先按公开的坐标转换算法统一转换后再存入空间数据库。画图验证时交叉比对几条熟悉的路段偏移正常后再批量入库。4.5 道路长度重复统计首尾闭环点没去重现象统计出的全城拥堵道路总里程比实际城区道路总长还高明显不合常理。原因接口里一条道路分段的折线收尾是同一个点形成闭合环我在长度计算里没有去掉闭环终点相当于每段道路都多算了一遍。解决解析坐标串后先判断首尾点是否相同相同则去掉最后一点再算长度。修复后全城总里程立刻降到了合理区间与官方路网规模基本吻合。这个 bug 不报错只影响统计值是所有静默错误里最难发现的一类建议在脚本里加一条不变量检查全城总里程必须在合理范围内超出则告警。5. 爬下来的交通态势还能做什么从道路级数据到区域拥堵热力原始快照存得多了可以做的事就多起来。我最常用的进阶用法是构建区域拥堵指数代码思路是把每个网格或行政区内的道路分段按拥堵等级分组用拥堵里程除以总里程得到该区域的拥堵里程占比再叠加速度中位数做二次修正。from collections import defaultdict def region_level_index(rows: list[dict]) - dict: 按道路分段归属计算区域拥堵指数。rows 是解析后的快照行。 agg defaultdict(lambda: {len: 0.0, congested: 0.0}) for r in rows: # 实际项目中 region 字段由道路中心点落入哪个网格决定这里以 road_id 前几位为示例 region r[road_id][:6] agg[region][len] r[length_km] if r[level] 3: # 3 拥堵, 4 严重拥堵 agg[region][congested] r[length_km] return {k: round(v[congested] / v[len], 4) for k, v in agg.items()}把每天的指数按小时聚合能得到区域的早晚高峰曲线与假期日历对比能看出节假日拥堵波峰整体后移。如果再做网格热力用每个格子的拥堵里程占比作为填充色可以直接输出城市拥堵热力图这比直接画单条道路的等级更有决策价值。这一层还有一个被低估的用法数据质量回溯。网格采集的关键问题是有没有覆盖全、有没有漏格子通过对比连续快照里每个网格的记录数稳定性KPI 异常就能倒推到采集层而不是等分析结果出来才发现数少了。最后一个建议把这一步写成一个独立的校验脚本每次采集完自动执行检查全城总里程、记录数和网格覆盖数量不达标就重试一次还不行就告警。这套交通态势爬取方案值不值得投入不是看单次抓得多快而是看跑一个月之后数据还能不能信、还能不能补。希望帮到你。本文还有配套的精品资源点击获取