高德地图商家电话导出工具 百度高德腾讯实体门店采集教程
本文讨论的是通过高德、百度、腾讯地图官方开放平台的 Web 服务 API,检索并导出商家主动公开的电话信息。示例代码仅调用官方授权接口,不涉及网页爬取、登录绕过或风控对抗。数据使用需遵守各平台服务条款,不得用于骚扰或未经同意的商业推广。
实体门店电话本质上属于地图 POI(Point of Interest)数据的一部分。三家平台都在官方开放平台提供了地点检索接口,返回结构化 JSON,其中直接包含电话字段。相比模拟浏览器抓取地图前台页面,调用官方 API 有三个明显好处:
代价是每个平台返回电话的条件不一样,需要分别处理。
高德 | 百度 | 腾讯 | |
|---|---|---|---|
地点搜索接口 | /v3/place/text(关键字搜索) | /place/v2/search | /ws/place/v1/search |
电话字段 | pois[].tel | results[].detail_info.telephone(仅 scope=2 时返回) | data[].tel |
获取电话的关键参数 | extensions=all | scope=2 | filter=tel<>null(筛选有电话的地点,需 URL 编码)-21 |
分页 | page / offset(默认每页 20 条) | page_num / page_size | page_index / page_size(最大 20 条)-21 |
返回结构 | status=1 为成功 | status=0 为成功 | status=0 为正常-21 |
高德的 tel 字段可能包含多个号码,用分号隔开;百度如果不加 scope=2,detail_info 整个字段都不会出现;腾讯的 filter=tel<>null 是最直接的“只要带电话的结果”的筛选方式。
下面的代码把三个平台的检索封装成同一套输出结构,方便后续合并导出 CSV。依赖只有 requests:
pip install requestsimport csv
import os
import time
from dataclasses import dataclass, field
from typing import List, Optional
from urllib.parse import quote
import requests
OUTPUT_CSV = "poi_contacts.csv"
REQUEST_INTERVAL = 0.5 # 秒,自行根据配额调整
@dataclass
class PoiContact:
platform: str
name: str
address: str
phone: str
category: str = ""
poi_id: str = ""
# ---------------- 高德 ----------------
def search_amap(key: str, keywords: str, city: str, page: int = 1) -> List[PoiContact]:
url = "https://restapi.amap.com/v3/place/text"
params = {
"key": key,
"keywords": keywords,
"city": city,
"citylimit": "true",
"offset": 20,
"page": page,
"extensions": "all", # 不加这个拿不到 tel
"output": "json",
}
results = []
try:
resp = requests.get(url, params=params, timeout=10)
data = resp.json()
except Exception as e:
print(f"[高德] 请求异常:{e}")
return results
if data.get("status") != "1":
print(f"[高德] 失败:{data.get('info')}")
return results
for poi in data.get("pois", []):
tel = poi.get("tel", "") or ""
# 高德 tel 可能是空的,也可能用分号分隔多个号码
results.append(PoiContact(
platform="高德",
name=poi.get("name", ""),
address=poi.get("address", "") if isinstance(poi.get("address"), str) else "",
phone=tel,
category=poi.get("type", ""),
poi_id=poi.get("id", ""),
))
return results
# ---------------- 百度 ----------------
def search_baidu(key: str, keywords: str, region: str, page: int = 0) -> List[PoiContact]:
url = "https://api.map.baidu.com/place/v2/search"
params = {
"ak": key,
"query": keywords,
"region": region,
"output": "json",
"page_size": 20,
"page_num": page,
"scope": 2, # 必须为 2,才会返回 detail_info,其中包含 telephone
}
results = []
try:
resp = requests.get(url, params=params, timeout=10)
data = resp.json()
except Exception as e:
print(f"[百度] 请求异常:{e}")
return results
if data.get("status") != 0:
print(f"[百度] 失败:{data.get('message')}")
return results
for item in data.get("results", []):
detail = item.get("detail_info") or {}
results.append(PoiContact(
platform="百度",
name=item.get("name", ""),
address=item.get("address", ""),
phone=detail.get("telephone", "") or "",
category=detail.get("tag", "") or "",
poi_id=item.get("uid", ""),
))
return results
# ---------------- 腾讯 ----------------
def search_tencent(key: str, keywords: str, region: str, page: int = 1) -> List[PoiContact]:
url = "https://apis.map.qq.com/ws/place/v1/search"
# filter=tel<>null 用于筛选有电话的地点,参数值需要 URL 编码
params = {
"key": key,
"keyword": keywords,
"boundary": f"region({region},0)",
"page_size": 20,
"page_index": page,
"filter": quote("tel<>null"),
"output": "json",
}
results = []
try:
resp = requests.get(url, params=params, timeout=10)
data = resp.json()
except Exception as e:
print(f"[腾讯] 请求异常:{e}")
return results
if data.get("status") != 0:
print(f"[腾讯] 失败:{data.get('message')}")
return results
for item in data.get("data", []):
results.append(PoiContact(
platform="腾讯",
name=item.get("title", ""),
address=item.get("address", ""),
phone=item.get("tel", "") or "",
category=item.get("category", ""),
poi_id=item.get("id", ""),
))
return results
# ---------------- 汇总与导出 ----------------
def collect_all(
amap_key: Optional[str],
baidu_key: Optional[str],
tencent_key: Optional[str],
keywords: str,
city: str,
max_pages: int = 3,
) -> List[PoiContact]:
all_contacts: List[PoiContact] = []
if amap_key:
for p in range(1, max_pages + 1):
batch = search_amap(amap_key, keywords, city, page=p)
all_contacts.extend(batch)
time.sleep(REQUEST_INTERVAL)
if len(batch) < 20:
break
if baidu_key:
for p in range(max_pages):
batch = search_baidu(baidu_key, keywords, city, page=p)
all_contacts.extend(batch)
time.sleep(REQUEST_INTERVAL)
if len(batch) < 20:
break
if tencent_key:
for p in range(1, max_pages + 1):
batch = search_tencent(tencent_key, keywords, city, page=p)
all_contacts.extend(batch)
time.sleep(REQUEST_INTERVAL)
if len(batch) < 20:
break
return all_contacts
def dedupe_and_export(contacts: List[PoiContact], output: str = OUTPUT_CSV):
# 只保留有电话的记录,并按“电话 + 名称”去重(同一门店可能在多个平台出现)
with_phone = [c for c in contacts if c.phone.strip()]
seen = set()
unique = []
for c in with_phone:
key = (c.phone.strip(), c.name.strip())
if key not in seen:
seen.add(key)
unique.append(c)
os.makedirs(os.path.dirname(output) or ".", exist_ok=True)
with open(output, "w", newline="", encoding="utf-8-sig") as f:
writer = csv.writer(f)
writer.writerow(["平台", "名称", "地址", "电话", "分类", "POI ID"])
for c in unique:
writer.writerow([c.platform, c.name, c.address, c.phone, c.category, c.poi_id])
print(f"[完成] 有电话记录 {len(unique)} 条 → {output}")
if __name__ == "__main__":
# 在环境变量中设置 Key,不要硬编码到代码里
amap_key = os.getenv("AMAP_KEY")
baidu_key = os.getenv("BAIDU_KEY")
tencent_key = os.getenv("TENCENT_KEY")
contacts = collect_all(
amap_key=amap_key,
baidu_key=baidu_key,
tencent_key=tencent_key,
keywords="咖啡馆",
city="北京",
max_pages=2, # 先小批量验证
)
dedupe_and_export(contacts)1. 百度不加 scope=2,电话永远是空的。 telephone 藏在 detail_info 里,而 detail_info 只有在 scope=2 时才返回。这是百度接口最容易被忽略的一点。
2. 腾讯的 filter 参数必须 URL 编码。 tel<>null 里的 <> 直接拼进 URL 可能被错误解析,代码中用 quote() 处理是必要的。-21
3. 高德的 tel 可能包含多个号码。 返回格式可能是 "010-xxxx;138xxxx",如果要拆开单独分析,需要在导出后按分号切分。
4. 分页终止条件用“返回条数 < page_size”来判断。 不要直接用 max_pages 硬翻到底,既浪费配额,也容易触发限流。
5. 先小批量、低频率验证。 把 max_pages 设为 1~2,确认三个平台的 Key 都能正常返回、字段解析无误之后,再扩大范围。每家平台的免费配额不同,代码里用 time.sleep 做基础限频是下限而不是保证。
高德、百度、腾讯三家地图开放平台的地点检索 API 都能返回商家公开电话,但触发条件不同:高德靠 extensions=all,百度靠 scope=2,腾讯可以直接用 filter=tel<>null 筛掉无电话的结果。把三个平台统一成同一套数据结构后,去重和导出 CSV 就变得很直接。代码里的限频、分页终止和 Key 环境变量管理,是让它稳定运行的关键。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。