概览
「车来了」是中国最大的实时公交信息平台,覆盖中国大陆 488 座城市及城镇,累计用户约 3.34 亿,提供实时公交到站查询、路线规划、上下车提醒等功能[1]。
用户分享的小程序路径为 #小程序://车来了/XVgIUdrGgMSdkpG,其对应的微信小程序 AppID 为 wx71d589ea01ce3321。通过分析开源项目 chelaile-mcp[2]的源码以及早期 PHP 接口提取文章[3],本文档完整提取了该小程序使用的全部后端 API 接口。
提取方法
微信小程序运行在微信客户端内部的 双线程模型 中(渲染线程 WebView + 逻辑线程 JSCore/V8),无法通过浏览器 DevTools 直接查看网络请求[4]。提取小程序接口通常采用以下三种方式之一:
- 反编译 wxapkg — 从设备缓存获取小程序包文件(
.wxapkg),使用wxappUnpacker或Gwxapkg等工具[5]反编译后静态分析 JS 源码中的wx.request调用 - 动态抓包 — 使用 Charles / mitmproxy 等工具在运行小程序时拦截 HTTPS 流量,需处理 SSL Pinning 等对抗措施
- 开源项目分析 — 从 GitHub 上的开源封装项目中直接提取已逆向的接口定义(本文档采用此方式)
本文档接口信息主要来源于 GitHub 开源项目 PeanutSplash/chelaile-mcp,该项目基于 TypeScript 实现了车来了完整 API 的 MCP 服务端封装,包含了签名算法、AES 解密密钥、请求头伪装等完整实现。补充信息来自 2019 年的 PHP 接口提取文章(旧版 API 域名 api.chelaile.net.cn)。
技术架构
域名与基础路径
| 项目 | 值 | 说明 |
|---|---|---|
BASE_DOMAIN | https://web.chelaile.net.cn | 主域名 |
BASE_URL | https://web.chelaile.net.cn/api | API 基础路径(加密接口) |
| 旧版域名 | https://api.chelaile.net.cn | 2019 年 PHP 文档中的旧版域名,部分接口路径仍兼容 |
默认请求参数
所有接口请求均携带以下默认参数(作为 URL Query String 拼接):
{
s: "h5",
wxs: "wx_app",
sign: "1",
h5RealData: "1",
v: "3.11.28", // 小程序版本号
src: "weixinapp_cx", // 来源标识
ctm_mp: "mp_wx", // 渠道标识
vc: "2",
favoriteGray: "1",
gpstype: "wgs", // GPS 坐标系类型
geo_type: "wgs",
scene: "1256" // 微信场景值
}
请求头伪装
车来了服务端会校验请求头指纹,篡改会导致 status:400 错误。必须使用以下请求头:
Host: web.chelaile.net.cn
Connection: keep-alive
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.0.0 Safari/537.36 MicroMessenger/7.0.20.1781(0x6700143B) NetType/WIFI MiniProgramEnv/Windows WindowsWechat/WMPF WindowsWechat(0x63090a13) UnifiedPCWindowsWechat(0xf254160a) XWEB/18055
xweb_xhr: 1
Content-Type: text
Accept: */*
Sec-Fetch-Site: cross-site
Sec-Fetch-Mode: cors
Sec-Fetch-Dest: empty
Referer: https://servicewechat.com/wx71d589ea01ce3321/814/page-frame.html
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9
Referer 头中的 wx71d589ea01ce3321 是车来了小程序的 AppID,814 是小程序版本号。服务端通过此头部验证请求是否来自合法的微信小程序环境。
签名机制(cryptoSign)
除城市列表接口外,所有请求参数需要附加 cryptoSign 签名参数。签名算法为 MD5:
签名算法
function cryptoSign(params):
// 1. 将所有参数按 key 排列,格式为 "key"="value",用 & 连接
str = params.entries()
.map(([k, v]) => `"${k}"="${v}"`)
.join("&")
// 2. 末尾拼接盐值
str += SIGN_SALT // 盐值 = "qwihrnbtmj"
// 3. 计算 MD5
return md5(str)
盐值(SIGN_SALT)为:qwihrnbtmj
签名示例
原始参数: { cityId: "034", s: "h5", v: "3.11.28", src: "weixinapp_cx" }
拼接结果: "cityId"="034"&"s"="h5"&"v"="3.11.28"&"src"="weixinapp_cx"qwihrnbtmj
MD5 结果: a1b2c3d4e5f6... (32位十六进制)
响应解密机制
部分接口的响应数据经过 AES-256-ECB 加密。加密接口的响应格式为:
{
"jsonr": {
"status": "00", // "00" 表示成功
"data": {
"encryptResult": "Base64编码的AES密文",
...其他元数据
}
}
}
解密参数:
- 算法:AES-256-ECB
- 密钥:
FF32AE65FBFD19414EAAFF6291A54B42(32字节 UTF-8 字符串) - 输入编码:Base64
- 输出编码:UTF-8
旧版接口(2019年)的响应格式使用 YGKJ## 前缀和 **YGKJ 后缀包裹 JSON 数据,解析时需去除这些标记。
接口分类
根据响应处理方式,接口分为三类:
- PLAIN 明文接口 — 返回普通 JSON,无需签名和解密(如城市列表)
- SIGNED 签名接口 — 需要附加
cryptoSign签名,返回普通 JSON(如逆地理编码) - ENCRYPTED 加密接口 — 需要签名,且响应数据需 AES 解密(大部分核心接口)
完整接口列表
| # | 接口名称 | HTTP | 路径 | 类型 | 功能 |
|---|---|---|---|---|---|
| 1 | 城市列表 | GET | /wwd/ncitylist | PLAIN | 获取所有支持的城市 |
| 2 | 城市配置 | GET | /api/bus/cityMaxInterval.action | ENCRYPTED | 获取城市刷新间隔等配置 |
| 3 | 关键词搜索 | GET | /api/bus/query!nSearch.action | ENCRYPTED | 搜索线路/站点/POI |
| 4 | 搜索更多 | GET | /api/bus/query!searchMore.action | ENCRYPTED | 分类分页获取更多结果 |
| 5 | 附近站点 | GET | /api/bus/stop!encryptedHomePage.action | ENCRYPTED | 获取附近站点及到站信息 |
| 6 | 站点详情 | GET | /api/bus/stop!encryptedPhyStnDetail.action | ENCRYPTED | 获取站点所有线路和地铁 |
| 7 | 线路详情 | GET | /api/bus/line!encryptedLineDetail.action | ENCRYPTED | 线路完整站点列表和车辆 |
| 8 | 线路轨迹 | GET | /api/bus/line!lineRoute.action | ENCRYPTED | 获取线路地图轨迹坐标 |
| 9 | 线路实时 | GET | /api/bus/line!encryptedBusDetail.action | ENCRYPTED | 获取到站车辆实时信息 |
| 10 | 车辆列表 | GET | /api/bus/line!busList.action | ENCRYPTED | 线路上所有车辆位置 |
| 11 | 时刻表 | GET | /api/bus/line!preStartTimetableNew.action | ENCRYPTED | 首末班/发车间隔/时刻表 |
| 12 | 批量刷新 | GET | /api/bus/line!encryptedTsfRealInfos.action | ENCRYPTED | 批量刷新多条线路到站信息 |
| 13 | 逆地理编码 | GET | /api/transfer/transit!getLocationByGps.action | SIGNED | GPS坐标转中文地址 |
| 14 | 公交换乘规划 | GET | /api/transfer/transit!integrate.action | ENCRYPTED | 公交+地铁换乘方案规划 |
| 15 | IP定位 | GET | http://ip-api.com/json/ | EXTERNAL | 基于IP的城市级定位(外部API) |
城市相关接口
获取车来了支持的所有城市(约 480+ 个),包含城市 ID、名称、拼音、是否支持地铁等。返回普通 JSON,无需签名。
请求参数
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
s | string | 默认 | 平台标识,值为 "h5" |
v | string | 默认 | 版本号,值为 "3.11.28" |
src | string | 默认 | 来源标识,值为 "weixinapp_cx" |
| ...其余 DEFAULT_PARAMS 默认参数 | |||
{
"cityList": [
{
"cityId": "034",
"cityName": "上海",
"pinyin": "ShangHai",
"supportSubway": 1,
"isHot": 1,
"isSupport": 1,
"cityVersion": 3
},
{ "cityId": "027", "cityName": "北京", ... }
]
}
获取某城市的运行配置:最大轮询间隔、"即将到站"时间阈值等。主要用于决定刷新频率。
请求参数
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
cityId | string | 是 | 城市 ID,如 "034"(上海) |
localCityId | string | 是 | 同 cityId |
| ...DEFAULT_PARAMS + cryptoSign | |||
{
"maxInterval": 30, // 最大刷新间隔(秒)
"arrivingStationLimitSeconds": 180, // "即将到站"阈值(秒)
"busDisplayConfig": {
"lineDetail": "time#order#distance",
"other": "time#order"
}
}
搜索接口
在城市内按关键词搜索,返回匹配的线路、站点和 POI。支持线路号、站点名、地名搜索。
请求参数
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
cityId | string | 是 | 城市 ID |
localCityId | string | 是 | 同 cityId |
key | string | 是 | 搜索关键词,如 "71"、"人民广场" |
supportPhyStn | string | 是 | 固定为 "true",返回物理站点 ID |
| ...DEFAULT_PARAMS + cryptoSign | |||
{
"result": {
"lines": [
{
"lineId": "21283603183",
"name": "71",
"lineNo": "r95817",
"direction": 0,
"startSn": "延安东路外滩",
"endSn": "申昆路枢纽站",
"subwayV2": 0
}
],
"stations": [
{ "sId": "021-15232", "sn": "西藏中路",
"lat": 31.231, "lng": 121.474, "gpsType": "wgs",
"physicalStId": "...", "namesakeStId": "...",
"subwayV2": 0 }
],
"pois": [
{ "sn1": "71路", "sn1Address": "...", "adname": "黄浦区",
"lat": 31.233, "lng": 121.490 }
]
},
"highlightKey": "71路"
}
坐标系说明:pois 坐标为 GCJ-02(火星坐标),用于换乘规划;stations 坐标为 WGS-84(真实 GPS 坐标),用于站点查询。
对某分类进行分页"查看更多",结构与搜索接口相同但仅返回指定分类。
请求参数
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
cityId | string | 是 | 城市 ID |
key | string | 是 | 同搜索接口的关键词 |
type | enum | 是 | "1"=线路,"2"=站点,"3"=POI |
supportPhyStn | string | 是 | 固定 "true" |
站点相关接口
根据 GPS 坐标获取附近公交站点,包含经过线路和实时到站车辆信息。这是小程序首页核心接口。
请求参数
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
cityId | string | 是 | 城市 ID |
localCityId | string | 是 | 固定为 "undefined" |
lat | string | 是 | WGS-84 纬度 |
lng | string | 是 | WGS-84 经度 |
geo_lat | string | 是 | 同 lat |
geo_lng | string | 是 | 同 lng |
type | string | 是 | 固定 "5" |
permission | string | 是 | 固定 "0" |
{
"nearSts": [
{
"sId": "021-15232",
"sn": "西藏中路",
"distance": 87,
"isSubway": 0,
"physicalStId": "...",
"namesakeStId": "...",
"firstLineId": "...",
"lines": [
{
"line": {
"lineId": "...", "name": "71", "direction": 0,
"endSn": "申昆路枢纽站",
"firstTime": "05:30", "lastTime": "23:30",
"price": "2元"
},
"targetStation": { "sId": "...", "order": 2 },
"stnStates": [
{
"busId": "沪D-B1234",
"order": 2,
"arrivalTime": 1779070466055,
"travelTime": 25,
"distanceToDest": 90,
"capacity": 0
}
]
}
],
"subwayV2Lines": [
{ "line": { "lineName": "地铁2号线", "shortName": "2号线", "bgColor": "140,194,32" },
"sublines": [{ "destName": "...", "firstTime": "05:31", "lastTime": "23:24" }] }
]
}
]
}
字段说明:capacity 拥挤度:0=空,1=适中,2=拥挤;travelTime 剩余秒数;arrivalTime 到站时间戳(毫秒),-1 表示未知。
获取站点完整详情:经过该站的所有线路(含首末班、票价)、实时车辆和附近地铁线路。
请求参数
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
cityId | string | 是 | 城市 ID |
localCityId | string | 是 | 同 cityId |
physicalStId | string | 是 | 物理站点 ID(从搜索/附近站点获取) |
namesakeStId | string | 否 | 同名站点 ID |
firstLineId | string | 否 | 高亮的线路 ID |
stationId | string | 是 | 固定空字符串 "" |
lat / lng | string | 否 | 用户位置,用于计算距离 |
permission | string | 是 | 固定 "0" |
线路相关接口
获取线路完整信息:站点列表、当前运营车辆、反方向线路、发车信息。
请求参数
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
cityId | string | 是 | 城市 ID |
localCityId | string | 是 | 同 cityId |
lineId | string | 是 | 线路 ID(从搜索/附近站点获取) |
lat / lng | string | 否 | 用户位置 |
geo_lat / geo_lng | string | 否 | 同 lat / lng |
{
"line": {
"lineId": "21283603183",
"name": "71",
"direction": 0,
"startSn": "延安东路外滩",
"endSn": "申昆路枢纽站",
"firstTime": "05:30",
"lastTime": "23:30",
"price": "2元",
"stationsNum": 20
},
"stations": [
{ "order": 0, "sId": "...", "sn": "延安东路外滩",
"wgsLat": 31.231, "wgsLng": 121.474,
"physicalStId": "...", "namesakeStId": "...",
"metros": [] }
],
"buses": [
{ "busId": "沪D-B1234", "order": 5, "lat": 31.232, "lng": 121.475,
"speed": 30, "capacity": 0 }
],
"otherlines": [
{ "lineId": "21283603182", "startSn": "申昆路枢纽站",
"endSn": "延安东路外滩" }
],
"depDesc": "下一班10:12发车",
"preArrivalTime": "10:12"
}
地铁线路 ID(如 "1057")调用此接口会返回空数据壳。获取地铁首末班时间应通过附近站点接口的 subwayV2Lines 字段。
获取线路在地图上的轨迹坐标点列表,用于绘制线路走向。
请求参数
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
cityId | string | 是 | 城市 ID |
lineId | string | 是 | 线路 ID |
{
"route": [
{ "lat": 31.231, "lng": 121.474, "stopOrder": 0 },
{ "lat": 31.232, "lng": 121.475 },
// ... 通常400-500个坐标点
{ "lat": 31.245, "lng": 121.501, "stopOrder": 19 }
]
}
获取即将到达目标站点的车辆实时信息,包含车牌、位置、速度、ETA。
请求参数
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
cityId | string | 是 | 城市 ID |
lineId | string | 是 | 线路 ID |
targetOrder | string | 是 | 目标站点在线路上的序号 |
stationId | string | 是 | 目标站点 sId |
lat / lng | string | 是 | WGS-84 坐标(可使用站点坐标) |
geo_lat / geo_lng | string | 是 | 同 lat / lng |
{
"buses": [
{
"busId": "沪D-B1234", "licence": "沪D-B1234",
"order": 5, "lat": 31.232, "lng": 121.475,
"speed": 30, "capacity": 0,
"distanceToWaitStn": 500,
"travels": [
{ "travelTime": 180, "arrivalTime": 1779070466055,
"recommTip": "约3分钟" }
]
}
],
"line": { "lineId": "...", "name": "71", "endSn": "..." },
"targetOrder": 2,
"realData": true
}
获取线路上所有运营车辆的位置和载客率。
请求参数
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
cityId | string | 是 | 城市 ID |
lineId | string | 是 | 线路 ID |
targetOrder | string | 是 | 目标站点序号 |
stationName | string | 是 | 目标站点显示名称 |
获取线路时刻表信息:首末班时间、发车间隔或完整时刻表。
请求参数
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
cityId | string | 是 | 城市 ID |
lineId | string | 是 | 线路 ID |
lineNo | string | 是 | 线路短名(如 "71",非内部 lineNo) |
direction | enum | 是 | 方向:"0" 或 "1" |
{
"line": {
"lineId": "...", "name": "71", "direction": 0,
"startSn": "延安东路外滩", "endSn": "申昆路枢纽站"
},
"timeTableType": 1, // 1=按时刻表, 2=按间隔, 3=特殊
"timetable": [...], // timeTableType=1时有详细时刻表
"scheduleTags": [...]
}
一次性批量刷新多个 (线路, 站点) 对的实时信息,用于多线路同时监控。
请求参数
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
cityId | string | 是 | 城市 ID |
localCityId | string | 是 | 同 cityId |
lineStn | string | 是 | 格式见下方说明 |
// 格式: lineId,stopId,nextId,targetOrder
// 多组用分号 ; 分隔,nextId 可为空
// 示例:
"21283603183,021-15232,,2;21283603184,021-15233,,3"
地理编码接口
将 WGS-84 经纬度转换为中文地址(省、市、区、乡镇)。需要签名但返回普通 JSON。
请求参数
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
lat | string | 是 | WGS-84 纬度 |
lng | string | 是 | WGS-84 经度 |
geo_lat / geo_lng | string | 是 | 同 lat / lng |
gpsType | string | 是 | 固定 "wgs" |
gpstype | string | 是 | 固定 "wgs" |
geo_type | string | 是 | 固定 "wgs" |
{
"regeocode": {
"formatted_address": "上海市黄浦区...",
"addressComponent": {
"province": "上海市",
"city": "上海市",
"district": "黄浦区",
"township": "南京东路街道",
"citycode": "021",
"adcode": "310101"
}
}
}
换乘规划接口
规划两点之间的公交+地铁换乘路线,返回多个方案,每个方案包含步行段和乘车段。
此接口要求 GCJ-02(火星坐标),不是 WGS-84。建议从搜索接口的 pois 字段获取坐标(已为 GCJ-02)。
请求参数
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
cityId | string | 是 | 城市 ID |
origin_name | string | 是 | 起点名称 |
origin_lat | string | 是 | 起点纬度(GCJ-02) |
origin_lng | string | 是 | 起点经度(GCJ-02) |
dest_name | string | 是 | 终点名称 |
dest_lat | string | 是 | 终点纬度(GCJ-02) |
dest_lng | string | 是 | 终点经度(GCJ-02) |
gpstype | string | 是 | 固定 "gcj" |
geo_type | string | 是 | 固定 "gcj" |
strategy | enum | 是 | "0"=推荐, "1"=少换乘, "2"=少步行, "3"=最短时间(仅公交) |
isSelectTime | string | 是 | 固定 "0" |
departure_time | string | 是 | 当前时间戳(毫秒) |
{
"route": {
"origin": "121.490,31.233",
"destination": "121.373,31.197",
"distance": "17982",
"transits": [
{
"duration": "3056", // 总耗时(秒)
"walking_distance": "1466",
"distance": "19086",
"cc": 1, // 换乘次数
"tag": "直达",
"segments": [
{
"walking": { "distance": "837", "duration": "717" }
},
{
"bus": {
"buslines": [{
"name": "地铁2号线",
"lineType": 1, // 0=公交, 1=地铁
"color": "...",
"departure_stop": { "name": "人民广场" },
"arrival_stop": { "name": "虹桥2号航站楼" },
"via_num": "8",
"station_start_time": "0537",
"station_end_time": "2330",
"duration": "1800",
"distance": "17620"
}]
}
}
]
}
]
}
}
Python 代码示例
以下 Python 代码演示了如何调用车来了 API,包含签名生成、AES 解密和请求发送的完整流程。
完整调用示例
import hashlib
import json
import requests
from Crypto.Cipher import AES
from urllib.parse import urlencode
# ===== 常量定义 =====
BASE_DOMAIN = "https://web.chelaile.net.cn"
BASE_URL = f"{BASE_DOMAIN}/api"
SIGN_SALT = "qwihrnbtmj"
AES_KEY = b"FF32AE65FBFD19414EAAFF6291A54B42"
DEFAULT_PARAMS = {
"s": "h5",
"wxs": "wx_app",
"sign": "1",
"h5RealData": "1",
"v": "3.11.28",
"src": "weixinapp_cx",
"ctm_mp": "mp_wx",
"vc": "2",
"favoriteGray": "1",
"gpstype": "wgs",
"geo_type": "wgs",
"scene": "1256",
}
REQUEST_HEADERS = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 "
"(KHTML, like Gecko) Chrome/132.0.0.0 Safari/537.36 "
"MicroMessenger/7.0.20.1781(0x6700143B) NetType/WIFI "
"MiniProgramEnv/Windows WindowsWechat/WMPF "
"WindowsWechat(0x63090a13) XWEB/18055",
"Content-Type": "text",
"Referer": "https://servicewechat.com/wx71d589ea01ce3321/814/page-frame.html",
"Accept": "*/*",
"Accept-Language": "zh-CN,zh;q=0.9",
}
# ===== 签名生成 =====
def crypto_sign(params: dict) -> str:
"""生成 cryptoSign 签名"""
# 按 key 排列,格式为 "key"="value",用 & 连接
str_to_sign = '&'.join(
f'"{k}"="{v}"' for k, v in params.items()
) + SIGN_SALT
return hashlib.md5(str_to_sign.encode()).hexdigest()
# ===== AES 解密 =====
def decrypt_result(ciphertext: str) -> dict:
"""解密 AES-256-ECB 加密的响应数据"""
cipher = AES.new(AES_KEY, AES.MODE_ECB)
decrypted = cipher.decrypt(
__import__('base64').b64decode(ciphertext)
)
# 去除 PKCS7 padding
pad_len = decrypted[-1]
decrypted = decrypted[:-pad_len]
return json.loads(decrypted.decode('utf-8'))
# ===== 响应解析 =====
def parse_encrypted_response(raw_text: str) -> dict:
"""解析加密响应"""
# 找到 JSON 起始位置
json_start = raw_text.index('{')
# 匹配 JSON 大括号
depth = 0
json_end = json_start
for i in range(json_start, len(raw_text)):
if raw_text[i] == '{': depth += 1
elif raw_text[i] == '}': depth -= 1
if depth == 0:
json_end = i + 1
break
envelope = json.loads(raw_text[json_start:json_end])
data = envelope.get("jsonr", {}).get("data", {})
if "encryptResult" in data:
return decrypt_result(data["encryptResult"])
return data
# ===== 发送加密请求 =====
def request_encrypted(url: str, params: dict) -> dict:
"""发送加密接口请求"""
# 附加签名
params_with_sign = {**params, "cryptoSign": crypto_sign(params)}
full_url = f"{url}?{urlencode(params_with_sign)}"
resp = requests.get(full_url, headers=REQUEST_HEADERS, timeout=15)
return parse_encrypted_response(resp.text)
# ===== 发送签名请求(明文响应) =====
def request_signed(url: str, params: dict) -> dict:
"""发送签名接口请求(响应为普通 JSON)"""
params_with_sign = {**params, "cryptoSign": crypto_sign(params)}
full_url = f"{url}?{urlencode(params_with_sign)}"
resp = requests.get(full_url, headers=REQUEST_HEADERS, timeout=15)
return json.loads(resp.text)
# ===== 发送明文请求 =====
def request_plain(url: str, params: dict) -> dict:
"""发送明文接口请求"""
full_url = f"{url}?{urlencode(params)}"
resp = requests.get(full_url, headers=REQUEST_HEADERS, timeout=15)
return json.loads(resp.text).get("data", {})
# ===== 使用示例 =====
if __name__ == "__main__":
# 1. 获取城市列表
cities = request_plain(
f"{BASE_DOMAIN}/wwd/ncitylist",
{**DEFAULT_PARAMS}
)
print(f"支持城市数: {len(cities.get('cityList', []))}")
# 2. 搜索线路
search_result = request_encrypted(
f"{BASE_URL}/bus/query!nSearch.action",
{**DEFAULT_PARAMS,
"cityId": "034", # 上海
"localCityId": "034",
"key": "71",
"supportPhyStn": "true"}
)
lines = search_result.get("result", {}).get("lines", [])
print(f"找到线路数: {len(lines)}")
for line in lines:
print(f" {line.get('name')} -> {line.get('endSn')}")
# 3. 获取附近站点
nearby = request_encrypted(
f"{BASE_URL}/bus/stop!encryptedHomePage.action",
{**DEFAULT_PARAMS,
"cityId": "034",
"localCityId": "undefined",
"lat": "31.230416",
"lng": "121.473701",
"geo_lat": "31.230416",
"geo_lng": "121.473701",
"type": "5",
"permission": "0"}
)
stops = nearby.get("nearSts", [])
print(f"附近站点数: {len(stops)}")
for stop in stops[:3]:
print(f" {stop.get('sn')} ({stop.get('distance')}m)")
运行上述代码需要安装:pip install requests pycryptodome --break-system-packages
法律与合规声明
使用须知
本文档仅供技术研究和学习交流使用。车来了小程序及其 API 接口的版权归 车来了(chelaile.net.cn) 所有。
合法使用场景:个人学习研究、安全审计、接口文档整理。
禁止行为:未经授权的商业化使用、大规模数据爬取、恶意攻击服务、倒卖数据。
未经授权大量调用接口可能违反《网络安全法》和《数据安全法》,请务必在合法、正当且已获得充分授权的前提下使用。使用者需自行承担一切法律责任。