
简介面向需获取百度、高德实时交通态势数据的开发者与研究人员这份源码完整覆盖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服务Keyrectangle是左下角经纬度与右上角经纬度用分号分隔例如116.35,39.88;116.40,39.92level否道路等级取值有1高速、2国道、3省道、4县道、6乡镇村路等extensions否base只返回概要all返回全部道路详情我这里用alloutput否返回格式默认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/?qtvtile_nbx{x}y{y}z{z}styles2v083其中x、y、z是瓦片行列号和缩放级别styles2表示路况样式。直接浏览器访问这个地址能看到一张代表路况的小图片。麻烦出在瓦片坐标计算上。百度地图用的是自己的百度墨卡托投影瓦片网格并不完全等于大家熟悉的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-84GPS卫星定位原始坐标国内外通用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, paramsparams, headersheaders, timeout10) 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?style8x{x}y{y}z{z}其中style8表示路况层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 fhttps://maponline0.bdimg.com/tile/?qtvtile_nbx{x}y{y}z{z}styles2v083 headers { User-Agent: Mozilla/5.0, Referer: https://map.baidu.com/ } resp requests.get(url, headersheaders, timeout10) 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, indexFalse)如果你想长期追踪某条路的拥堵变化可以每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再开始拼接同时给每个瓦片打印状态日志方便定位是哪一块失败。还有一点经验缩放级别太小时路况瓦片经常拿不到有效内容。比如全国范围z5路况瓦片几乎是空白。做城市路况至少用z12以上做路口级路况建议z15到z17具体看你需要的精细程度。4.3 拿到路况数据后怎么验证准确性程序跑通了不代表数据可信。我一般在正式使用前做几轮交叉验证拿高德返回的道路速度和该路段同时间的导航预计到达时间对比看看速度是否合理。拿高德返回的拥堵状态和百度路况瓦片对应位置的颜色对比两家一致的概率很高。如果条件允许用自己的GPS轨迹记录实地跑一段把真实速度和API返回速度做对比。高德的polyline是按道路中心线返回的坐标串如果你需要把路况绑定到自己的路网可以把polyline做抽稀处理再用空间匹配算法关联到最近道路。这块我建议不要直接硬编码最好用PostGIS或Shapely做空间关系计算否则不同道路交叉口附近的匹配结果会乱。最后说点我自己的体会这个项目的核心难点不在于“请求接口”而在于“把数据变成能用的业务信息”。高德API和百度瓦片都只是数据源真正的价值在坐标转换、区域切分、缓存策略和可视化融合这些工程细节上。还有一点就是抓取任何地图数据都要注意频率和用途个人学习研究没问题正式商用前一定去对应开放平台确认服务条款和授权范围。我后续打算把这套采集逻辑封装成一个定时任务服务配上可视化后台再做一版针对历史路况趋势的分析功能到时候继续分享踩坑记录。本文还有配套的精品资源点击获取