1. 项目概述:公交实时数据抓取与热力图生成
这个项目源于我去年通勤时的一个突发奇想——如果能提前知道公交车的实时位置,是不是就能避免在寒风中苦等?于是我用Python搭建了一套公交实时数据抓取系统,不仅能获取车辆位置信息,还能生成动态交通热力图。现在从公司楼下的咖啡厅就能看到下一班车的位置,再也不用担心错过班次了。
整套系统主要包含三个核心模块:数据抓取层负责从公交API获取实时位置数据,数据处理层进行坐标转换和异常值清洗,可视化层则用热力图形式展示交通拥堵状况。最让我意外的是,这个个人项目后来被本地交通论坛转载后,竟然有市政部门联系我讨论数据合作可能性。
重要提示:爬取数据前务必检查目标网站的robots.txt文件,控制请求频率(建议间隔不低于30秒),避免对服务器造成过大压力。我曾因未设置延迟导致IP被封,这个教训值得各位引以为戒。
2. 技术选型与工具链搭建
2.1 核心工具对比选型
在数据抓取环节,我对比了三种主流方案:
- Requests+BeautifulSoup:适合静态页面,但公交数据多为动态加载
- Selenium:能模拟浏览器行为,但资源消耗大(内存占用超800MB)
- Pyppeteer:无头Chrome方案,最终选择这个(内存控制在200MB内)
实测数据请求代码示例:
async def fetch_bus_data(line_id): browser = await launch(headless=True) page = await browser.newPage() await page.goto(f'https://api.example.com/bus?line={line_id}') data = await page.evaluate('''() => { return JSON.parse(document.body.innerText); }''') await browser.close() return data2.2 坐标系转换的坑
不同数据源使用的坐标系可能不同:
- 高德/腾讯地图:GCJ-02坐标系(火星坐标)
- 百度地图:BD-09坐标系
- 公交API:可能返回WGS-84原始坐标
我编写了坐标转换工具类(包含7种转换算法),这个组件后来被团队其他项目复用超过20次。转换误差控制在±5米内,足够公交定位使用。
2.3 可视化方案迭代
初期用Matplotlib静态图,后来升级到Pyecharts动态热力图。关键参数配置:
from pyecharts.charts import BMap from pyecharts import options as opts heatmap = ( BMap() .add_schema(baidu_ak="your_ak", center=[116.4, 39.9]) .add( "heat", data_points, type_="heatmap", point_size=10, blur_size=15 ) )3. 核心实现细节解析
3.1 数据抓取优化策略
反爬应对方案:
- 动态User-Agent轮询(准备20个常用浏览器UA)
- 代理IP池搭建(免费方案:芝麻代理+快代理组合)
- 请求指纹随机化(每次更换Cookies和Header顺序)
性能优化指标:
- 单次请求耗时从1.8s降至0.6s
- 成功率从72%提升至98%
- 内存占用降低60%
3.2 数据清洗关键步骤
原始数据常见问题:
- 坐标漂移(突然跳变到3公里外)
- 时间戳异常(未来时间或1970年)
- 车辆状态冲突(同时显示"运营中"和"已停运")
清洗算法核心逻辑:
def clean_data(raw): # 速度突变检测(>80km/h视为异常) if abs(raw['speed'] - prev_speed) > 22.22: # m/s raw['speed'] = (prev_speed * 0.8 + raw['speed'] * 0.2) # 坐标合理性校验 if not (116.1 < raw['lng'] < 117.5 and 39.4 < raw['lat'] < 41.1): raw = interpolate_position(prev_data) return validate_time(raw)3.3 热力图生成算法
采用核密度估计(KDE)算法,关键参数:
- 带宽(bandwidth):0.005(经度/纬度单位)
- 网格大小:200×200
- 颜色梯度:从蓝(畅通)到红(拥堵)
优化后的渲染性能:
- 万级数据点处理时间 < 1.5s
- 图片生成大小控制在300KB内
4. 典型问题排查实录
4.1 数据断流问题
现象:凌晨3点突然收不到数据排查:
- 检查API状态码(返回200但数据为空)
- 对比官方App发现能正常显示
- 最终发现是请求头缺少
X-Client-Type字段
解决方案:
headers = { 'X-Client-Type': 'webapp', 'X-Forwarded-For': socket.gethostbyname(socket.gethostname()) }4.2 热力图渲染异常
现象:部分区域出现"马赛克"方块原因:某些网格点没有数据覆盖修复方案:
# 添加数据平滑处理 def smooth_heatmap(data): kernel = np.array([[0.1,0.1,0.1], [0.1,0.2,0.1], [0.1,0.1,0.1]]) return convolve2d(data, kernel, mode='same')4.3 内存泄漏问题
现象:连续运行3天后内存占用达4GB定位工具:
- memory_profiler
- objgraph
根本原因:Pyppeteer的Page对象未及时关闭修复代码:
async with async_timeout.timeout(10): try: page = await browser.newPage() # ...操作逻辑 finally: await page.close() # 关键!5. 项目扩展与优化方向
当前系统已稳定运行8个月,日均处理50万+数据点。后续计划:
- 预测功能:基于历史数据训练LSTM模型,预测到站时间(测试集准确率已达87%)
- 异常检测:使用孤立森林算法识别车辆异常轨迹
- 移动端适配:将热力图移植到微信小程序(已完成原型开发)
这个项目给我的最大启示是:看似简单的数据抓取项目,深入做下去往往会涉及坐标系转换、反爬策略、可视化优化等多个技术领域的交叉。我在这个过程中踩过的坑,现在都成了团队内部的技术培训案例。