爬虫+GIS:抓取POI数据并可视化分布——从零开始的实战指南

0 阅读7分钟

免费编程软件「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 注册高德开发者账号

  1. 访问高德开放平台
  2. 创建应用 → 获取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:

  1. 使用多线程/异步请求(如aiohttp库)
  2. 分布式爬虫(Scrapy+Redis)
  3. 预生成代理IP池
  4. 避开高峰时段抓取

Q3:高德API返回"无权限"错误?
A:检查:

  1. API Key是否正确
  2. 是否开通了对应服务的权限(如Web服务API)
  3. 是否超出每日调用配额(免费版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)

转存失败,建议直接上传图片文件


结语:从数据到洞察的完整链路

通过本文,你已掌握:

  1. 使用Python爬取高德POI数据
  2. 数据清洗与坐标处理
  3. 基础到高级的可视化方法
  4. 反爬策略与性能优化技巧

下一步可尝试:

  • 结合人口数据做空间相关性分析
  • 使用机器学习预测POI分布
  • 开发Web应用实时展示地图(Django+Leaflet)

数据采集只是开始,真正的价值在于通过地理空间分析发现隐藏的业务规律。