简介:针对高德交通态势数据的获取需求,这套Python爬虫方案面向开发者、交通数据分析人员及地理信息系统应用学习者,可调用高德地图API采集实时交通流量与拥堵指数,解决路况数据获取与初步处理的难题。方案中包含请求间隔设置、User-Agent伪装等反爬策略,以规避接口限制、提升抓取稳定性,并给出数据清洗与格式转换的预处理思路。抓取结果支持存储为CSV、JSON或数据库,可导入ArcGIS进行空间可视化与拥堵规律分析,也能定期运行实现监测更新,支撑交通管理、出行规划等场景。下载压缩包共包含2个文件,以Python脚本为主,辅以一个zip文件,整体仅约2KB,代码紧凑便于直接阅读和复用。目前已有1867人学习下载,适合希望快速搭建轻量级交通态势采集与整理流程的参考者。
1. 高德交通态势爬取:一个脚本,把实时路况变成 ArcGIS 能用的数据
做交通数据分析的人,大概率经历过这一步:接到全城路况需求,发现高德有现成的交通态势接口,兴冲冲申请 key、调通接口,结果返回的是一堆 JSON,每条道路是一串 polyline 坐标加一个拥堵等级。丢给 ArcGIS 直接画图?画不了。这份「高德交通态势爬取」资源做的,就是把“调接口拿 JSON”和“进 ArcGIS 做空间分析”之间的空档填上——高德交通态势爬取.py负责爬取、解析、分区落盘,配套的“导入 ArcGIS 前处理”说明负责把数据转成能用的线要素。适合想快速拿到全城实时路况、又不想从零折腾坐标和格式的交通规划、城市研究相关从业者。
2. 交通态势接口选型:矩形查询怎么调、返回字段怎么读、配额为什么是瓶颈
做爬取之前,先得搞清楚高德交通态势接口的几个硬约束。这一章把接口选型、参数含义和配额机制讲透,后面用脚本时才不会翻车。
2.1 矩形查询和圆形查询怎么选
高德的交通态势接口有两类查询方式,一类是矩形区域查询,一类是圆形区域查询。矩形查询要求你传入一个矩形的左下角和右上角经纬度,接口返回该矩形范围内所有有路况回报的道路;圆形查询则传入中心点和半径。做全城路况分析,矩形查询是唯一合理的选择——你能用几个矩形拼出一整个行政区,而圆形查询在城区边界会有大量重叠,数据去重很麻烦。
接口地址是https://restapi.amap.com/v3/traffic/status/rectangle,注意这是 v3 版本的 Web 服务接口。高德后来出过更新版本的交通态势服务,但那个接口需要额外传diu参数,普通个人开发者的 Web 服务 key 基本拿不到权限,老项目走 v3 是最稳妥的。这个判断很重要——如果你按新接口文档去调,第一步就会卡在参数上。
先看一个最简单的请求长什么样:
curl "https://restapi.amap.com/v3/traffic/status/rectangle?key=YOUR_KEY&rectangle=116.30,39.80,116.45,39.95&extensions=all&level=6"这里的四个参数是硬性配置:key是你在高德开放平台申请的 Web 服务 key,类型必须选“Web服务”,不能选 Android/iOS 的 key;rectangle是左下角经度、左下角纬度、右上角经度、右上角纬度,四个值用英文逗号分隔,顺序固定;extensions填all才能拿到速度和方向字段,填base只有道路名和状态;level是道路等级,6 代表所有等级,如果要单独分析高速可以填 1,国道填 2,省道填 3,县道填 4。
2.2 返回结构里真正要读的五个字段
请求成功之后,返回的是标准 JSON,外层有status、info这些通用字段,真正的数据在trafficinfo.roads数组里。每条 road 代表一段有独立路况回报的道路片段,不是一整条路,这点要心里有数。下面是一个典型返回的简化结构:
{ "status": "1", "info": "OK", "trafficinfo": { "roads": [ { "name": "北四环中路", "status": "3", "direction": "东", "speed": 28, "polyline": "116.32,39.91;116.33,39.91;116.34,39.91" } ] } }polyline是道路中心线的坐标串,分号分隔坐标点、逗号分隔经纬度,这是导入 ArcGIS 的核心素材。真正要读的字段整理如下:
| 字段 | 类型 | 含义与判断要点 |
|---|---|---|
name | string | 道路名称,可能有同名的不同路段,不能当主键 |
status | string | 拥堵状态:0 未知、1 畅通、2 缓行、3 拥堵、4 严重拥堵(新版有 4,老版只到 3) |
speed | number | 平均速度,单位 km/h,可能为 0,不一定是数据异常 |
direction | string | 方向描述,如“东”“西”,部分路段为空 |
polyline | string | 中心线坐标串,导入 ArcGIS 时逐点解析成线 |
注意status是字符串不是数字,写代码时别直接拿int()去转,有的响应里会带空字符串。
2.3 配额是按公里算的,不是按次数算的
交通态势接口和其他高德 Web 服务最大的区别是配额机制。普通接口按调用次数扣配额,交通态势接口按“道路里程”扣——你请求一次覆盖 10 公里的矩形,就扣 10 公里的配额。个人开发者的默认配额非常少,少到什么程度?一次覆盖全市范围的请求,可能直接把当天的配额烧掉大半。这是这个项目里最需要提前规划的点,也是很多人在跑数据时中途夭折的原因。
解法是在脚本里做两件事:第一,把大矩形拆成小矩形,失败时能单块重试;第二,把每次请求的原始 JSON 落盘缓存,后续分析全部从缓存里读,绝不重复请求。下一章写爬取脚本时会把这套机制完整实现。
3. 爬取脚本落地:从 key 配置到 CSV 分区抓取的完整链路
高德交通态势爬取.py的核心思路不复杂:读配置、按矩形循环请求、解析 JSON、追加写入 CSV。但细节决定成败,这一章给出可复现的完整链路,并解释每个参数为什么这么设。
3.1 读懂主流程:请求函数、重试机制、解析落盘
先看请求部分。常见做法是写一个带指数退避重试的请求函数,因为交通态势接口对 QPS 很敏感,偶尔超时是常态,不要一失败就崩溃:
import requests import time import random KEY = "你的高德Web服务Key" RECT = "116.30,39.80,116.45,39.95" # 左下经度,左下纬度,右上经度,右上纬度 url = "https://restapi.amap.com/v3/traffic/status/rectangle" params = { "key": KEY, "rectangle": RECT, "extensions": "all", "level": 6 } def fetch_traffic(rect): params["rectangle"] = rect for attempt in range(3): try: resp = requests.get(url, params=params, timeout=10) data = resp.json() if data.get("status") == "1": return data else: print(f"接口返回异常: {data.get('info')}") except Exception as e: print(f"第{attempt + 1}次请求异常: {e}") time.sleep(2 ** attempt + random.random()) return None逻辑说明:params["rectangle"]在每次调用时动态替换,方便循环遍历多个矩形;重试采用 2 秒、4 秒、8 秒的指数退避,最后加一个随机数是为了避免多个矩形同时失败后同步重试、再次打满 QPS。timeout=10必须写,不写的话接口假死会卡住整个循环。
参数说明:RECT这个变量现在只是默认值,真正干活时会被rect参数覆盖;level=6表示取全等级道路,如果只分析快速路就改成 1,能显著减少数据量。
解析函数负责把trafficinfo.roads展平成一行行记录。常见做法是把polyline原样保留,再额外算一个中心点,方便后面在 ArcGIS 里做点图层:
def flatten_roads(data): rows = [] roads = data.get("trafficinfo", {}).get("roads", []) or [] for road in roads: polyline = road.get("polyline", "") points = polyline.split(";") if polyline else [] center = points[len(points) // 2] if points else "" rows.append({ "name": road.get("name", ""), "status": road.get("status", "0"), "speed": road.get("speed", ""), "direction": road.get("direction", ""), "polyline": polyline, "center_lng_lat": center }) return rows逻辑说明:center_lng_lat取坐标串中间那个点的经纬度,作为路段的近似中心。取中点而不是取第一个点,是为了避免 polyline 首尾在路口附近时中心点偏移到马路外面。这个字段不进 ArcGIS 做线,而是用来快速预览数据范围和检查矩形是否错位。
3.2 参数不是越小越好:矩形大小、请求间隔和失败重跑
矩形拆多大是一个需要平衡的问题。拆太小,请求次数大量增加,当天总配额消耗反而更快(每次请求有固定开销);拆太大,单次返回数据量爆炸,容易触发接口截断或超时。我一般按经纬度 0.1 度到 0.2 度见方来切,也就是大约 10 到 18 公里见方的一个格子。以北京市为例,用五个矩形就能覆盖主要城区,每个矩形的数据量在一两兆左右,解析不会太慢。
请求间隔同样要设置。交通态势接口对 QPS 的限制很严格,连续快速请求很容易被限流。常见做法是每次循环后sleep(0.5)到sleep(1.5)随机抖动,这个区间实测在大多数情况下能稳定跑完一轮:
rects = [ "116.20,39.75,116.35,39.85", "116.35,39.75,116.50,39.85", "116.20,39.85,116.35,39.95" ] csv_file = open("traffic_rows.csv", "w", encoding="utf-8-sig") fieldnames = ["name", "status", "speed", "direction", "polyline", "center_lng_lat"] writer = csv.DictWriter(csv_file, fieldnames=fieldnames) writer.writeheader() for rect in rects: data = fetch_traffic(rect) if data: rows = flatten_roads(data) for row in rows: writer.writerow(row) print(f"{rect} 抓取完成,道路数: {len(rows)}") else: print(f"{rect} 失败,需手工重跑") time.sleep(random.uniform(0.5, 1.5)) csv_file.close()逻辑说明:每个矩形独立抓取、独立打印结果,失败的矩形会被记录下来,而不是让整个流程中断。encoding="utf-8-sig"是给 Excel 和 ArcGIS 准备的,带 BOM 的中文 CSV 在 Windows 下打开不容易乱码。
参数说明:rects这个列表需要你按照目标城市的地理范围自己定义。怎么确定边界?一个偷懒但有效的方法是打开高德地图网页版,用坐标拾取器找到城市左下角和右上角的经纬度,按 0.1 度步长切成网格,再把网格的边界填进列表。
3.3 为 ArcGIS 前处理准备的 CSV 字段设计
CSV 的字段设计直接决定后面导入 ArcGIS 的工作量。除了上面代码里的六个原始字段,我建议你再加两个派生字段:fetch_time和rect_id。fetch_time记录抓取时刻,因为交通态势是实时数据,多轮抓取合并分析时必须能按时间筛选;rect_id标记这条数据来自哪个矩形,排查问题时能快速定位是哪个区域的抓取逻辑出了问题。
写入时在循环内补上这两个字段:
from datetime import datetime fetch_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S") row["fetch_time"] = fetch_time row["rect_id"] = rects.index(rect)逻辑说明:rect_id直接用列表下标虽然简单,但如果后续调整矩形顺序,历史数据的 ID 会错乱,更稳妥的做法是给每个矩形起一个可读的名字,比如"chaoyang_west",这样 CSV 里看名字就知道是哪个片区。fetch_time建议精确到秒,同一个矩形在一小时内多次抓取的数据可以用这个字段区分。
到这一步,你已经有了一个字段完整、格式规整的 CSV。接下来最大的坑才开始出现——坐标偏移、空字段、配额超限,这些都在下一章的避坑手册里集中处理。
4. 避坑手册:五个必踩的坑和现场排查记录
这一章写的是我在实际跑高德交通态势数据时踩过的坑,每一条都是“现象→原因→解决”的结构。你大概率会遇到其中至少三条。
4.1 配额耗尽:一天的量一次跑光
现象:脚本刚开始跑得挺顺利,几分钟后接口开始返回类似配额超限的提示,info字段不再是OK,而是提示当天配额已用完,后续所有请求全部失败。
原因:交通态势接口按道路里程扣配额,矩形越大、覆盖的道路越多,单次扣得越狠。个人开发者默认配额很低,一次大范围的请求就烧掉大半,再跑几轮彻底归零。
解决:第一,把矩形控制在 0.1 度到 0.2 度见方,别贪大;第二,原始 JSON 一定要缓存,分析数据时不要去重新请求前一天已经抓过的内容;第三,如果配额确实不够用,把请求时间错开,每天零点重置后再跑,不要在同一天内反复重试。
4.2 rectangle 参数写错:一个符号毁掉一次全城拉取
现象:请求返回status=1、info=OK,但trafficinfo.roads是空的,或者返回的道路数量少得离谱,明显和矩形范围不匹配。
原因:rectangle参数要求的顺序是“左下经度、左下纬度、右上经度、右上纬度”,很多人习惯性写成左上和右下,或者把经度纬度顺序搞反。还有一种情况是用了中文逗号,高德解析失败后静默返回空结果,不报错。
解决:写一个自检函数,每次请求后检查roads的长度,如果请求成功但道路数为 0,先打印出当前的rectangle并人工核对范围。另外,先用一个小矩形测试,比如经纬度跨度只有 0.02 度、确定有城市道路的区域,能返回几条 road 再铺开全城网格。
4.3 坐标系偏移:GCJ-02 和 WGS84 的几百米差距
现象:把 CSV 导入 ArcGIS 后,路况线和底图道路明显错位,严重的时候偏离几百米甚至一公里,看起来像数据抓错了。
原因:高德返回的polyline坐标用的是 GCJ-02(火星坐标系),而 ArcGIS 默认在线底图通常是 WGS84 坐标系,两者之间大约有几百米的系统性偏移,纬度越高偏移越明显。
解决:最简单的方法是不做坐标转换,直接在 ArcGIS 或 QGIS 里叠加高德瓦片底图,让数据和底图保持同一个坐标体系。如果你必须用 WGS84 底图,就得先对polyline做 GCJ-02 到 WGS84 的近似纠偏。网上有很多开源实现,但别指望它有厘米级精度,肉眼对齐底图是没问题的。
4.4 speed 为 0 与 status 对不上:缺失值不能乱删
现象:同一段路status=1(畅通)但speed=0,或者status=3(拥堵)但speed=80,看起来数据自相矛盾。
原因:speed字段在部分路况响应里本身就是缺失值,高德用 0 表示“没有速度回报”,并不代表车辆真的静止;status和speed的更新频率也可能不同,一个是路况计算值,一个是实时速度采样值,时间上略有错位。
解决:判读时以status为主、speed为辅。speed为 0 时不要直接删行,也不要填成 0 参与计算,最好原样保留空值,在 ArcGIS 做分级渲染时用status字段,不要用speed直接做颜色梯度,否则会出现大量灰点。
4.5 空 roads 和偶发超时:怎么判断是没路还是被卡了
现象:某个矩形请求返回status=1但roads为空,或者请求偶发超时、返回异常状态码,重试几次又好了。
原因:两种情况本质不同。空roads可能是矩形范围确实不在路网覆盖区,比如画在了水域、郊区空旷地带;偶发超时则多半是对端限流或网络抖动,重试能解决。
解决:按住 4.2 的自检思路,空roads时打印矩形范围并人工确认是不是郊区;偶发超时用第 3 章的重试函数处理,3 次指数退避足够。跑完一轮后把失败的矩形记录到一个retry_list.txt,单独重跑,不要重新拉全量。
5. 导入 ArcGIS 的前处理:polyline 转线、GCJ-02 纠偏与瓦片底图叠加
zip 包的备注写着“导入 ArcGIS 前处理使用”,这步就是把上一步的 CSV 变成 ArcGIS 能识别的线要素。这里给出我常用的处理流程。
5.1 把 polyline 解析成线要素
在 ArcGIS 里用 arcpy 把 CSV 转成线要素。如果环境里没有 arcpy,用 QGIS 的“从分隔文本添加图层”也能做,但处理大量 polyline 时 arcpy 更可控:
import arcpy import csv arcpy.env.workspace = r"C:\traffic" fc = arcpy.CreateFeatureclass_management( "C:/traffic/traffic.gdb", "traffic_lines", "POLYLINE" ) with open("traffic_rows.csv", "r", encoding="utf-8-sig") as f: reader = csv.DictReader(f) with arcpy.da.InsertCursor(fc, ["SHAPE@", "name", "status", "speed"]) as cursor: for row in reader: polyline_str = row["polyline"] if not polyline_str: continue point_list = [] for pair in polyline_str.split(";"): lng, lat = pair.split(",") point_list.append(arcpy.Point(float(lng), float(lat))) line = arcpy.Polyline(arcpy.Array(point_list)) cursor.insertRow([line, row["name"], row["status"], row["speed"]])逻辑说明:polyline的格式是“分号分隔坐标对、逗号分隔经纬度”,先按分号拆出每个点的坐标,再按逗号拆成经度和纬度,拼成arcpy.Point列表,最后用arcpy.Polyline生成线要素。SHAPE@是 arcpy 插入游标里代表几何对象的关键字,必须写成这个带 @ 的形式。
参数说明:目标工作空间建议放在 File Geodatabase 里,比 shapefile 快,而且能直接存线要素的坐标系属性。插入前先确认polyline里的坐标确实是 GCJ-02,否则后面叠加底图会错位。
5.2 叠加高德瓦片底图并做分级渲染
线要素建好之后,叠加瓦片底图做可视化。这里用的就是“高德地图瓦片”,在 QGIS 里新增 XYZ Tile 图层,URL 模板填:
https://webrd0{1-4}.is.autonavi.com/appmaptile?style=7&x={x}&y={y}&z={z}参数说明:{1-4}是瓦片服务器编号,QGIS 会自动做负载均衡;style=7是默认路网样式。这套底图和你的polyline同为 GCJ-02,叠加后不会有偏移。渲染时按status做分级:0 灰色、1 绿色、2 黄色、3 橙色、4 红色,一眼就能看出全城拥堵分布。
我最早跑这个流程时,用一个大矩形把全市一次性拉完,第一天就把配额烧光了,后面三天只能对着缓存干等。从那以后,凡是涉及高德交通态势的活儿,我都强制自己走一遍“先拆矩形、再落缓存、最后才做 ArcGIS 前处理”的顺序,顺序不乱,数据就一直在。希望帮到你。
本文还有配套的精品资源,点击获取