百度地图POI数据爬取实战:从AK申请到JSON解析
1. 百度地图POI数据爬取入门指南
第一次接触百度地图POI数据爬取时,我也被各种专业术语搞得一头雾水。POI(Point of Interest)其实就是我们常说的兴趣点,比如餐馆、医院、商场这些地点信息。而百度地图开放平台提供的API接口,让我们能够批量获取这些数据。这在实际项目中非常有用,比如做商业分析时需要统计某个区域的餐饮店铺分布,或者做城市规划时需要了解医疗资源覆盖情况。
要完成整个爬取流程,我们需要先申请一个AK(Access Key),这是调用百度地图API的钥匙。整个过程可以分为四个关键步骤:注册开发者账号、创建应用获取AK、构造请求URL、解析返回的JSON数据。听起来可能有点复杂,但跟着我的步骤操作,你会发现其实很简单。我刚开始做的时候也踩过不少坑,比如AK类型选错、忘记设置IP白名单等等,这些经验教训都会在后续内容中详细说明。
2. 申请百度地图AK的完整流程
2.1 注册开发者账号
首先打开百度地图开放平台官网,点击右上角的"注册"按钮。这里有个小技巧:建议使用企业邮箱注册,因为个人邮箱可能会遇到验证问题。注册时需要填写基本信息,包括手机号验证,整个过程大概需要5分钟。注册完成后别急着退出,系统通常会发送一封激活邮件,记得去邮箱点击确认链接。
2.2 创建应用获取AK
登录后进入控制台,找到"应用管理"-"我的应用"-"创建应用"。这里有几个关键设置需要注意:
- 应用名称:建议取个有意义的名称,比如"XX项目POI采集"
- 应用类型:根据你的使用场景选择"浏览器端"或"服务端"
- IP白名单:如果是固定服务器调用就设置具体IP,本地测试可以填0.0.0.0(不推荐长期使用)
创建成功后,系统会生成一个AK字符串,这个就是我们的通行证。建议把它保存在安全的地方,同时做好备份。我曾经因为电脑重装系统丢失过AK,导致项目中断了半天。
2.3 AK使用限制说明
免费版的AK有调用频率限制:
- 未认证用户:每天10万次,每分钟6000次
- 认证用户:每天30万次,每分钟1万次
对于POI搜索接口,每次请求最多返回20条记录,通过分页参数可以获取最多400条数据。如果目标区域的数据量超过400条,就需要采用网格划分的方法,这个我们会在第4章详细讲解。
3. 构造POI请求URL详解
3.1 基础URL参数解析
百度地图POI搜索接口的基础URL格式如下:
http://api.map.baidu.com/place/v2/search关键参数说明:
- query:搜索关键词,如"医院"、"餐厅"
- region:搜索区域,可以是城市或省份
- page_size:每页返回数量(最大20)
- page_num:页码(从0开始)
- output:返回格式(json/xml)
- ak:你的访问密钥
3.2 实战URL示例
假设我们要搜索北京市的咖啡厅,AK是"your_ak",那么请求URL应该是:
base_url = "http://api.map.baidu.com/place/v2/search" params = { "query": "咖啡厅", "region": "北京", "page_size": 20, "page_num": 0, "output": "json", "ak": "your_ak" }可以用Python的requests库发送这个请求:
import requests response = requests.get(base_url, params=params) data = response.json() print(data)3.3 分页获取全部数据
由于每次最多只能获取20条数据,我们需要循环获取所有页面的数据:
all_pois = [] page_num = 0 while True: params["page_num"] = page_num response = requests.get(base_url, params=params) data = response.json() if not data.get("results"): break all_pois.extend(data["results"]) page_num += 1 # 避免触发频率限制 time.sleep(0.1)4. 处理大数据量的高级技巧
4.1 区域网格划分方法
当某个区域的POI数量超过400条时,我们需要将该区域划分为多个小网格分别查询。具体步骤:
- 获取目标城市的经纬度范围
- 将区域划分为若干个小矩形
- 对每个小矩形区域发起POI查询
这里提供一个简单的网格划分函数:
def create_grid(bounds, rows=10, cols=10): """ 将矩形区域划分为网格 bounds: (min_lng, min_lat, max_lng, max_lat) rows: 行数 cols: 列数 """ min_lng, min_lat, max_lng, max_lat = bounds lng_step = (max_lng - min_lng) / cols lat_step = (max_lat - min_lat) / rows grids = [] for i in range(rows): for j in range(cols): grid_min_lng = min_lng + j * lng_step grid_max_lng = grid_min_lng + lng_step grid_min_lat = min_lat + i * lat_step grid_max_lat = grid_min_lat + lat_step grids.append((grid_min_lng, grid_min_lat, grid_max_lng, grid_max_lat)) return grids4.2 多线程爬取优化
为了提高爬取效率,可以使用多线程技术:
from concurrent.futures import ThreadPoolExecutor def fetch_pois(grid): # 实现单个网格的POI获取 pass grids = create_grid(bounds) with ThreadPoolExecutor(max_workers=5) as executor: results = list(executor.map(fetch_pois, grids))5. JSON数据解析与存储
5.1 解析返回的JSON结构
百度地图返回的POI数据包含丰富的信息,主要字段包括:
- name:POI名称
- location:经纬度(lng,lat)
- address:详细地址
- telephone:联系电话
- uid:唯一标识
- detail_info:详细信息(如营业时间、评分等)
解析示例:
for poi in data["results"]: print(f"名称: {poi['name']}") print(f"地址: {poi['address']}") print(f"经纬度: {poi['location']['lng']}, {poi['location']['lat']}") if "telephone" in poi: print(f"电话: {poi['telephone']}")5.2 数据存储方案
常见的存储方式有三种:
- CSV文件:适合小规模数据
import csv with open("pois.csv", "w", newline="", encoding="utf-8") as f: writer = csv.writer(f) writer.writerow(["名称", "地址", "经度", "纬度", "电话"]) for poi in all_pois: writer.writerow([ poi["name"], poi["address"], poi["location"]["lng"], poi["location"]["lat"], poi.get("telephone", "") ])- MySQL数据库:适合大规模数据
import pymysql conn = pymysql.connect(host="localhost", user="root", password="123456", db="poi_db") cursor = conn.cursor() sql = """CREATE TABLE IF NOT EXISTS pois ( id INT AUTO_INCREMENT PRIMARY KEY, name VARCHAR(255), address VARCHAR(255), lng FLOAT, lat FLOAT, telephone VARCHAR(50), uid VARCHAR(50) )""" cursor.execute(sql) insert_sql = "INSERT INTO pois (name, address, lng, lat, telephone, uid) VALUES (%s, %s, %s, %s, %s, %s)" for poi in all_pois: cursor.execute(insert_sql, ( poi["name"], poi["address"], poi["location"]["lng"], poi["location"]["lat"], poi.get("telephone", ""), poi["uid"] )) conn.commit() conn.close()- MongoDB:适合非结构化数据
from pymongo import MongoClient client = MongoClient("mongodb://localhost:27017/") db = client["poi_db"] collection = db["pois"] collection.insert_many(all_pois)6. 常见问题与解决方案
在实际项目中,我遇到过各种奇怪的问题。比如有一次AK突然失效,排查后发现是因为IP地址变化导致的白名单限制。还有一次爬取速度太快,触发了百度地图的反爬机制。这里分享几个典型问题的解决方法:
- AK无效或没有权限
- 检查AK是否正确复制
- 确认应用类型(浏览器端/服务端)选择正确
- 检查IP白名单设置
- 返回数据为空
- 确认查询关键词是否正确
- 检查区域参数是否有效
- 尝试扩大搜索范围
- 请求频率过高
- 添加适当的延时(如time.sleep(0.1))
- 使用多个AK轮询
- 考虑申请付费套餐提升配额
- 数据不完整
- 检查是否处理了所有分页
- 对于大数据量区域使用网格划分
- 验证返回结果中的total字段
对于商业项目,建议考虑使用百度地图的商业API服务,它提供更高的调用限额和更稳定的服务保障。免费版适合个人学习和小型项目,但在数据量和稳定性上都有一定限制。
