免费编程软件「python+pycharm」 链接:pan.quark.cn/s/48a86be2f…
引言:为什么需要爬虫+GIS?
当你想分析北京所有咖啡馆的分布规律,或研究上海三甲医院的覆盖范围时,单纯的数据表格已无法满足需求。我们需要将地理信息(GIS)与网络爬虫结合,抓取POI(兴趣点)数据并可视化展示。本文将通过Python实现:从高德地图API抓取数据,到用QGIS/Folium生成专业地图的全流程。
一、环境准备:工具与库的选择
1.1 核心工具包
# 爬虫相关
import requests # HTTP请求
from fake_useragent import UserAgent # 随机User-Agent
import time # 请求间隔控制
# 数据处理
import pandas as pd # 数据清洗
import json # JSON解析
# 可视化
import folium # 交互式地图
import matplotlib.pyplot as plt # 统计图表
import geopandas as gpd # 高级GIS分析(可选)
1.2 开发环境建议
- Python 3.8+
- Jupyter Notebook(交互式开发)
- QGIS(专业GIS软件,用于复杂分析)
二、数据抓取:从高德API获取POI
2.1 注册高德开发者账号
- 访问高德开放平台
- 创建应用 → 获取Key(示例:
your_amap_key)
2.2 构造API请求
def get_poi_data(keywords, city, page_size=20, page_num=1):
"""
高德POI搜索API封装
:param keywords: 搜索关键词(如"咖啡馆")
:param city: 城市限制(如"北京")
:param page_size: 每页结果数(最大50)
:param page_num: 页码
"""
base_url = "https://restapi.amap.com/v3/place/text"
params = {
"key": "your_amap_key", # 替换为你的key
"keywords": keywords,
"city": city,
"types": "", # 可指定分类代码(如"050000"餐饮)
"offset": page_size,
"page": page_num,
"extensions": "base" # 基础信息(all为详细信息)
}
headers = {"User-Agent": UserAgent().random}
response = requests.get(base_url, params=params, headers=headers)
if response.status_code == 200:
return response.json()
else:
print(f"请求失败: {response.status_code}")
return None
2.3 分页抓取与数据合并
def crawl_all_pages(keywords, city, max_pages=10):
all_data = []
for page in range(1, max_pages+1):
print(f"正在抓取第{page}页...")
result = get_poi_data(keywords, city, page_num=page)
if result and result['status'] == '1':
pois = result['pois']
all_data.extend(pois)
time.sleep(1) # 礼貌性延迟
else:
break
# 转换为DataFrame
df = pd.DataFrame(all_data)
return df[['id', 'name', 'type', 'location', 'address', 'pname', 'cityname']]
# 示例:抓取北京100家咖啡馆
beijing_coffee = crawl_all_pages("咖啡馆", "北京", max_pages=5)
beijing_coffee.head()
输出示例:
id name ... cityname
0 B0FFFAB5J 星巴克 ... 北京市
1 B0FFG1234 瑞幸咖啡 ... 北京市
...
2.4 坐标处理与保存
高德返回的location是"经度,纬度"格式的字符串:
# 拆分经纬度
def split_location(row):
lon, lat = row['location'].split(',')
return pd.Series([float(lon), float(lat)])
beijing_coffee[['lon', 'lat']] = beijing_coffee.apply(split_location, axis=1)
# 保存为CSV
beijing_coffee.to_csv("beijing_coffee.csv", index=False)
三、数据可视化:从基础到进阶
3.1 使用Folium创建交互式地图
import folium
# 创建基础地图(中心点为北京天安门)
m = folium.Map(location=[39.9087, 116.3975], zoom_start=12)
# 添加POI点(使用蓝色标记)
for idx, row in beijing_coffee.iterrows():
folium.CircleMarker(
location=[row['lat'], row['lon']],
radius=3,
color='blue',
fill=True,
fill_color='blue'
).add_to(m)
# 保存为HTML
m.save("beijing_coffee_map.html")
效果:打开HTML文件可看到交互式地图,支持缩放/平移/点击查看详情。
3.2 热力图展示密度分布
from folium.plugins import HeatMap
# 提取经纬度列表
heat_data = beijing_coffee[['lat', 'lon']].values.tolist()
# 创建热力图
heat_map = folium.Map(location=[39.9087, 116.3975], zoom_start=12)
HeatMap(heat_data).add_to(heat_map)
heat_map.save("beijing_coffee_heatmap.html")
3.3 结合行政区划分析(使用GeoPandas)
# 加载北京行政区划数据(需提前下载shapefile)
# 示例数据源:https://gadm.org/download_country_v3.html
china = gpd.read_file("CHN_adm1.shp")
beijing_boundary = china[china['NAME_1'] == 'Beijing']
# 绘制基础地图
fig, ax = plt.subplots(figsize=(10, 8))
beijing_boundary.plot(ax=ax, color='lightgray', edgecolor='black')
# 绘制POI散点
plt.scatter(
beijing_coffee['lon'],
beijing_coffee['lat'],
s=10,
alpha=0.6,
color='red'
)
plt.title("北京咖啡馆分布")
plt.xlabel("经度")
plt.ylabel("纬度")
plt.show()
四、进阶技巧:提升抓取效率与质量
4.1 代理IP池实现
# 简易代理测试函数
def test_proxy(proxy):
try:
proxies = {"http": f"http://{proxy}", "https": f"https://{proxy}"}
response = requests.get("https://httpbin.org/ip", proxies=proxies, timeout=5)
if "origin" in response.json():
return True
except:
return False
# 示例:从文件读取代理列表并测试
with open("proxies.txt") as f:
proxies = [line.strip() for line in f]
valid_proxies = [p for p in proxies if test_proxy(p)]
print(f"可用代理数: {len(valid_proxies)}")
4.2 异常处理与重试机制
from requests.exceptions import RequestException
def safe_request(url, params, max_retries=3):
for _ in range(max_retries):
try:
headers = {"User-Agent": UserAgent().random}
response = requests.get(url, params=params, headers=headers, timeout=10)
if response.status_code == 200:
return response.json()
except RequestException:
time.sleep(2) # 等待后重试
return None
4.3 多关键词抓取策略
keywords_list = ["咖啡馆", "星巴克", "瑞幸咖啡", "Costa"]
all_data = []
for keyword in keywords_list:
print(f"正在抓取关键词: {keyword}")
data = crawl_all_pages(keyword, "北京", max_pages=3)
all_data.append(data)
time.sleep(3) # 避免频繁请求
# 合并数据
final_df = pd.concat(all_data, ignore_index=True)
final_df.to_csv("beijing_all_coffee.csv", index=False)
五、常见问题Q&A
Q1:被网站封IP怎么办?
A:立即启用备用代理池,建议使用隧道代理(如站大爷IP代理),配合每请求更换IP策略。对于高德API,合理控制请求频率(建议间隔1-2秒),避免短时间内大量请求。
Q2:如何提高数据抓取速度?
A:
- 使用多线程/异步请求(如
aiohttp库) - 分布式爬虫(Scrapy+Redis)
- 预生成代理IP池
- 避开高峰时段抓取
Q3:高德API返回"无权限"错误?
A:检查:
- API Key是否正确
- 是否开通了对应服务的权限(如Web服务API)
- 是否超出每日调用配额(免费版5000次/天)
Q4:如何获取更详细的POI信息?
A:在API请求中设置extensions=all,可获取营业时间、评分等额外字段,但会消耗更多配额。
Q5:坐标系不匹配怎么办?
A:高德使用GCJ-02坐标系(火星坐标),如需转换为WGS-84(GPS坐标),可使用pyproj库:
from pyproj import Transformer
transformer = Transformer.from_crs("EPSG:4490", "EPSG:4326") # GCJ02→WGS84
lon, lat = transformer.transform(39.9087, 116.3975)
结语:从数据到洞察的完整链路
通过本文,你已掌握:
- 使用Python爬取高德POI数据
- 数据清洗与坐标处理
- 基础到高级的可视化方法
- 反爬策略与性能优化技巧
下一步可尝试:
- 结合人口数据做空间相关性分析
- 使用机器学习预测POI分布
- 开发Web应用实时展示地图(Django+Leaflet)
数据采集只是开始,真正的价值在于通过地理空间分析发现隐藏的业务规律。