简介:面向需获取百度、高德实时交通态势数据的开发者与研究人员,这份源码完整覆盖API注册认证、接口调用、数据解析、可视化展示与实时更新处理的实现流程,可服务于城市交通分析、出行路线优化等场景。压缩包包含559个文件,以502个Python脚本为核心,辅以可执行程序、XML配置、TXT说明、BAT环境激活脚本及操作说明文档,整体仅2.65MB,结构紧凑,便于直接部署与二次开发。已有336人在CSDN下载学习。源码中内置密钥配置文件、HTTP请求封装、数据解析逻辑和展示相关代码,并提供virtualenv虚拟环境支持,能帮助使用者快速跨过环境配置与接口鉴权门槛,直接跑通交通态势数据抓取流程。无论用于个人出行决策还是交通管理部门的宏观研判,这套实现都提供了可落地的技术参考。 做地图应用这几年,被问到最多的问题之一就是:能不能把百度、高德上的实时交通态势数据拿下来,用在自己的系统里?有人要接大屏展示,有人做物流调度,也有人只想要个能定时推送拥堵提醒的小工具。我自己在项目里反复折腾过这两家的路况数据,从官方API到瓦片解析都踩过不少坑,这篇直接把我的源码思路拆出来讲。内容包括两家地图数据的底层组织方式、接口参数差异、坐标换算逻辑,以及一套可以直接跑的Python获取与解析方案,适合正在做地图可视化、交通分析、车联网相关开发的同学参考。
1. 项目整体思路与数据来源分析
1.1 百度和高德的实时交通态势是怎么组织的
先抛开代码,明确一件很重要的事:百度和高德对“实时交通态势”的开放程度完全不同,这决定了技术路线的起点。
高德地图提供了比较完整的Web服务API,其中“交通态势”接口可以直接按城市或矩形区域返回结构化JSON数据。返回结果里包含路段名称、车流方向、平均速度、拥堵状态等信息,拿到手就是干干净净的“路况数据”,非常适合做二次加工和业务联动。个人实测下来,城市级路况接口对中小范围覆盖还是比较稳定的,但矩形接口对区域面积有限制,需要自己切网格。
百度地图则不一样,开放平台目前没有提供类似“交通态势”的结构化Web API,对外暴露的主要是路况瓦片。所谓瓦片,就是地图上按固定网格切好的一小块一小块图片,路况瓦片用不同颜色表示拥堵等级:绿色畅通、黄色缓行、红色拥堵、深红严重拥堵。这种形式适合做可视化渲染,但不适合直接做数据统计,因为拿到的是一张张图片,需要再做颜色识别或叠加处理。
所以我在做这个项目时采用混合方案:高德走官方API拿结构化路况,百度走路况瓦片做栅格态势捕获。这看起来是两条完全独立的链路,但最终都能落到“某条路现在堵不堵”这个目标上。
1.2 选型思路:先问自己要“图片”还是要“数据”
很多人在起步阶段没想清楚一个问题:你要的是路况图片,还是路况数据?
如果只是在大屏上展示路况,那直接接瓦片是最省事的,高德有现成的路况图层,百度JS API里也有TrafficLayer,前端几行代码就能叠上去。如果要的是“某条路当前平均速度多少”“哪个路段拥堵持续时间最长”,那就必须走结构化数据解析,这时候高德的交通态势API是成本最低的选择。至于百度,如果你必须使用百度生态且要结构化路况,我目前试过的可行做法是拉路况瓦片后做颜色分类,把每个网格中不同颜色像素的占比统计出来,再结合百度底图道路矢量做空间匹配,这条链路实现起来要复杂很多,效果也依赖图片品质和配色稳定性,只适合没有其他替代方案的情况。
我最后落地给业务方用的是“高德JSON数据为主,百度瓦片快照为辅”的架构。高德负责提供道路级速度与状态,百度负责输出可视化的路况全景图,两套数据也能相互校验。比如某个路段高德返回“拥堵”,我把同一位置附近的百度瓦片颜色拉出来,如果红色占比异常,就说明数据基本可信。
2. 核心技术细节与数据解析难点
2.1 高德交通态势API参数说明与返回格式
高德交通态势接口有两个入口,一个是按城市查询,一个是按矩形区域查询。实际项目里我更喜欢用矩形查询,因为它能和自己的业务网格直接对齐。请求URL如下:
https://restapi.amap.com/v3/traffic/status/rectangle核心参数有这几个:
| 参数 | 必选 | 说明 |
|---|---|---|
| key | 是 | 高德开放平台申请到的Web服务Key |
| rectangle | 是 | 左下角经纬度与右上角经纬度,用分号分隔,例如116.35,39.88;116.40,39.92 |
| level | 否 | 道路等级,取值有1(高速)、2(国道)、3(省道)、4(县道)、6(乡镇村路)等 |
| extensions | 否 | base只返回概要,all返回全部道路详情,我这里用all |
| output | 否 | 返回格式,默认JSON |
返回的JSON结构大致是这样:
{ "status": "1", "trafficinfo": { "description": "二环内", "evaluation": { "status": "2", "status_descript": "缓行" }, "roads": [ { "name": "建国路", "status": "3", "direction": "东", "angle": "90", "speed": "21", "polyline": "116.45,39.91;116.46,39.91" } ] } }这里status字段就是拥堵状态码,高德的取值是:1畅通,2缓行,3拥堵,4严重拥堵。speed单位是公里/小时,polyline是道路中心线坐标串。用这些字段你就能知道某条路现在车速多少、往哪个方向堵、堵到什么程度。
2.2 百度路况瓦片URL规律与坐标问题
百度路况瓦片目前常见的URL格式大致长这样:
https://maponline0.bdimg.com/tile/?qt=vtile_nb&x={x}&y={y}&z={z}&styles=2&v=083其中x、y、z是瓦片行列号和缩放级别,styles=2表示路况样式。直接浏览器访问这个地址能看到一张代表路况的小图片。
麻烦出在瓦片坐标计算上。百度地图用的是自己的百度墨卡托投影,瓦片网格并不完全等于大家熟悉的Google Web Mercator网格。网上很多现成代码直接用标准Web Mercator公式算百度瓦片坐标,结果通常是对不上的,拉出来的图要么偏移,要么花掉。
我踩过几次坑之后,现在的做法是:先在前端页面用百度JS API加载路况图层,通过控制台拿到当前视野对应的瓦片URL,反推出坐标换算关系;在后端真正自动化时,再用一组已知坐标的瓦片URL做标定。这里给一个理解用的示意代码,注意它基于标准Web Mercator,实际使用前务必用自己的标定点校准:
import math def lnglat_to_web_mercator_tile(lng, lat, zoom): n = 2 ** zoom x = (lng + 180.0) / 360.0 * n lat_rad = math.radians(lat) y = (1.0 - math.asinh(math.tan(lat_rad)) / math.pi) / 2.0 * n return int(x), int(y)这段代码可以直接用来拉高德瓦片,因为高德在线瓦片采用的是标准Web Mercator切图规则。
2.3 坐标系三件套:WGS-84、GCJ-02与BD-09
所有做地图开发的人,迟早都会和坐标系打交道,做路况分析更是躲不掉。国内通常遇到三种坐标系:
| 坐标系 | 来源 | 使用场景 |
|---|---|---|
| WGS-84 | GPS卫星定位原始坐标 | 国内外通用 |
| GCJ-02 | 国测局坐标,加密偏移后的经纬度 | 高德、腾讯地图 |
| BD-09 | 百度在GCJ-02基础上再偏移一次 | 百度地图 |
高德API接收的rectangle参数必须使用GCJ-02坐标。如果你是拿GPS设备采集的WGS-84坐标来圈定范围,直接传给高德会整体偏移几百米,路段匹配结果可能串路。百度瓦片则要按BD-09的思路去理解坐标与网格的关系,如果你用WGS-84坐标去套百度瓦片,偏移会更明显。
Python里有一个很成熟的库叫coordtransform,可以直接实现WGS-84、GCJ-02、BD-09三者的互转:
pip install coordtransform转换调用也很简单:
import coordtransform # WGS-84 转 GCJ-02 gcj_lng, gcj_lat = coordtransform.wgs84_to_gcj02(lng, lat) # GCJ-02 转 BD-09 bd_lng, bd_lat = coordtransform.gcj02_to_bd09(gcj_lng, gcj_lat)在实际项目中,我会把原始GPS坐标先用这套逻辑统一转换成GCJ-02,再去调高德接口,这样返回的road polyline才能和底图正确叠加。
3. 实操:编写Python源码抓取实时交通态势
3.1 环境准备与依赖库安装
我用的Python版本是3.8以上,主要依赖这几个库:
pip install requests pillow pandasrequests:发HTTP请求,这个不用多说。pillow:处理路况瓦片,包括拼接和颜色统计。pandas:把解析出来的路况JSON整理成表格,方便后续分析和落库。
如果你要做区域切分,可以顺手装一个shapely,用来判断经纬度点是否在某个格网内,不过我下面的示例用简单循环也能实现。
3.2 用高德官方API抓取道路级路况JSON
先封装一个最基础的高德矩形区域请求函数。假设我们要查询北京国贸附近一块区域,矩形范围是116.45,39.90;116.50,39.93。
import requests import time import pandas as pd AMAP_KEY = "你的高德Web服务Key" AMAP_TRAFFIC_URL = "https://restapi.amap.com/v3/traffic/status/rectangle" headers = { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64)" } def fetch_amap_traffic(min_lng, min_lat, max_lng, max_lat): params = { "key": AMAP_KEY, "rectangle": f"{min_lng},{min_lat};{max_lng},{max_lat}", "extensions": "all", "output": "json" } resp = requests.get(AMAP_TRAFFIC_URL, params=params, headers=headers, timeout=10) data = resp.json() if data["status"] != "1": print("接口异常:", data["info"]) return [] roads = [] for item in data["trafficinfo"]["roads"]: roads.append({ "name": item.get("name"), "status": item.get("status"), "speed": item.get("speed"), "direction": item.get("direction"), "polyline": item.get("polyline"), }) return roads if __name__ == "__main__": result = fetch_amap_traffic(116.45, 39.90, 116.50, 39.93) df = pd.DataFrame(result) print(df.head(10))这段代码跑通之后,输出就是一个标准的DataFrame,列名有道路名称、状态码、速度、方向、轨迹串。状态码可以再映射成人话:
STATUS_MAP = { "1": "畅通", "2": "缓行", "3": "拥堵", "4": "严重拥堵" } df["status_text"] = df["status"].map(STATUS_MAP)实际使用中,矩形区域不要设得太大。官方对单个矩形查询面积有限制,我按经验控制在经纬度跨度0.05度以内比较稳妥,差不多是5公里见方。再大的范围就切成多个小矩形轮询,每次请求之间sleep 0.2秒,避免触发频控。
3.3 用瓦片抓取百度高德路况栅格
高德瓦片可以直接用标准Web Mercator坐标拉,请求地址是:
https://webrd0{s}.is.autonavi.com/appmaptile?style=8&x={x}&y={y}&z={z}其中style=8表示路况层,s可以是1到4中间的任意数字。我用前面写的lnglat_to_web_mercator_tile函数去下载指定范围的高德路况瓦片。
百度瓦片的情况前面说了,坐标换算需要以实际标定为准。我这里给出一个通用下载函数,x、y、z参数由你自己的坐标系转换模块提供,可以是前端JS算好了传后端,也可以是后端标定后自行计算:
import requests from io import BytesIO from PIL import Image def fetch_bmap_tile(x, y, z): url = f"https://maponline0.bdimg.com/tile/?qt=vtile_nb&x={x}&y={y}&z={z}&styles=2&v=083" headers = { "User-Agent": "Mozilla/5.0", "Referer": "https://map.baidu.com/" } resp = requests.get(url, headers=headers, timeout=10) if resp.status_code == 200: return Image.open(BytesIO(resp.content)) return None注意Referer头很关键,有些情况下不带它拿不到瓦片数据。这是我实际调试时踩过的坑,一开始一直返回空白图,加上Referer立刻正常了。
拿到瓦片以后,最常见的一个用途是拼接成一张大路况图。下面这段代码把多张瓦片按位置拼到一块画布上:
def merge_tiles(tiles, zoom): # tiles: {(x, y): PIL.Image} xs = [pos[0] for pos in tiles.keys()] ys = [pos[1] for pos in tiles.keys()] min_x, max_x = min(xs), max(xs) min_y, max_y = min(ys), max(ys) tile_size = 256 width = (max_x - min_x + 1) * tile_size height = (max_y - min_y + 1) * tile_size canvas = Image.new("RGB", (width, height)) for (x, y), img in tiles.items(): px = (x - min_x) * tile_size py = (y - min_y) * tile_size canvas.paste(img, (px, py)) return canvas大图拼出来后,一种快速的数据化方式是统计各拥堵颜色像素占比。百度路况瓦片的配色相对固定,在RGB空间里,纯绿色、亮黄色、红色、深红色各有区间。通过遍历像素颜色,能算出这个区域当前“畅通、缓行、拥堵”各占多少比例,这相当于从瓦片里反推出路况态势。不过要注意,瓦片渲染可能带抗锯齿,颜色会有过渡带,所以判断时要给RGB阈值留一点余量,只统计颜色最饱和的那部分像素。
3.4 数据落地与可视化扩展
抓到的路况数据不要只放在内存里,我一般建议直接落成CSV或SQLite。CSV适合临时分析,SQLite适合定时采集、增量存储和趋势查询。落库示例很简单:
df.to_csv("traffic_yyyymmdd_hhmm.csv", index=False)如果你想长期追踪某条路的拥堵变化,可以每5分钟跑一次采集任务,然后以道路名称+采集时间为维度建表,后面就能画出一条完整的“时间-速度”趋势曲线。
可视化方面,Cesium里可以加载高德底图与高德路况瓦片作为图层,适合做三维场景下的交通态势叠加。QGIS用户直接在“XYZ Tiles”里添加自定义瓦片地址,就能把高德路况当底图看,非常方便。移动端场景,uniapp或微信小程序接入高德地图SDK后,也可以叠加官方路况图层,实现“打开小程序看实时堵车”的效果。后面我会单独写一篇如何在uniapp里封装这套数据的方案。
4. 常见问题与排查技巧实录
4.1 高德API限流与区域过大问题
最常见的问题就是请求返回USER_DAILY_QUERY_OVER_LIMIT或类似限流错误。高德Web服务API对每个Key有每日配额和每秒并发限制,而且路况数据是实时性数据,容易让人想高频轮询。
我的解决办法有三个:
- 把大区域拆成小网格,每个网格独立请求,降低单次查询面积。
- 两次请求之间至少sleep 0.2秒,避免瞬间高并发打爆配额。
- 做好缓存。路况数据并不是每秒钟都在变,5分钟内重复请求同一个区域,拿到的结果基本没差别。我会在本地把每次结果按区域和时间戳缓存起来,10分钟内的重复请求直接读缓存。
4.2 瓦片错位、拼接黑边
瓦片错位最常见的原因是坐标系不统一。你的经纬度是GCJ-02,却拿去了百度瓦片,那拼出来当然对不上。另外行列号的计算必须向下取整,不要四舍五入,否则同一缩放级别下会整体偏一格。
拼接黑边一般是因为某些瓦片请求失败或还没下载完就被paste到画布上。解决方法是先下载完所有瓦片并确认不是None,再开始拼接,同时给每个瓦片打印状态日志,方便定位是哪一块失败。
还有一点经验:缩放级别太小时,路况瓦片经常拿不到有效内容。比如全国范围z=5,路况瓦片几乎是空白。做城市路况至少用z=12以上,做路口级路况建议z=15到z=17,具体看你需要的精细程度。
4.3 拿到路况数据后怎么验证准确性
程序跑通了不代表数据可信。我一般在正式使用前做几轮交叉验证:
- 拿高德返回的道路速度,和该路段同时间的导航预计到达时间对比,看看速度是否合理。
- 拿高德返回的拥堵状态,和百度路况瓦片对应位置的颜色对比,两家一致的概率很高。
- 如果条件允许,用自己的GPS轨迹记录实地跑一段,把真实速度和API返回速度做对比。
高德的polyline是按道路中心线返回的坐标串,如果你需要把路况绑定到自己的路网,可以把polyline做抽稀处理,再用空间匹配算法关联到最近道路。这块我建议不要直接硬编码,最好用PostGIS或Shapely做空间关系计算,否则不同道路交叉口附近的匹配结果会乱。
最后说点我自己的体会:这个项目的核心难点不在于“请求接口”,而在于“把数据变成能用的业务信息”。高德API和百度瓦片都只是数据源,真正的价值在坐标转换、区域切分、缓存策略和可视化融合这些工程细节上。还有一点就是,抓取任何地图数据都要注意频率和用途,个人学习研究没问题,正式商用前一定去对应开放平台确认服务条款和授权范围。我后续打算把这套采集逻辑封装成一个定时任务服务,配上可视化后台,再做一版针对历史路况趋势的分析功能,到时候继续分享踩坑记录。
本文还有配套的精品资源,点击获取