本篇文章转自CSDN大神Giser@lin的两篇大作:https://blog.csdn.net/abcbbbd?type=blog

方法一,关键词搜索法获取POI(不推荐)

主程,根据指定关键词抓取高德店铺的店名、地址及联系方式信息。

import requests
import json
from urllib.parse import quote
import xlwt
from Coordin_transformlat import gcj02towgs84


def Get_poi(key, city, types, page):
    '''
    这是一个能够从高德地图获取poi数据的函数
    key:为用户申请的高德密钥
    city:目标城市
    types:POI数据的类型
    page:当前页数
    '''

    # 设置header
    header = {
        'User-Agent': "Mozilla/5.0 (Windows; U; Windows NT 6.1; en-us) AppleWebKit/534.50 (KHTML, like Gecko) Version/5.1 Safari/534.50"}

    # 构建url
    # {}在链接里表示占位符,也就是占住位置先不填写,.format()里就是往刚刚占位符的地方填写变量,按照顺序一一对应,第一个{}里是key,第二个{}里是types
    url = 'https://restapi.amap.com/v3/place/text?key={}&types={}&city={}&page={}&output=josn'.format(
        key, types, quote(city), page)

    # 用get函数请求数据
    r = requests.get(url, headers=header)
    # 设置数据的编码为'utf-8'
    r.encoding = 'utf-8'
    # 将请求得到的数据按照'utf-8'编码成字符串
    data = r.text
    print(url)
    return data


def Get_times(key, city, types):
    '''
    这是一个控制申请次数的函数
    '''
    page = 1
    # 创建一个poilist的空列表
    poilist = []
    # 执行以下代码,直到count为0的时候跳出循环
    while True:
        # 调用第一个函数来获取数据
        result = Get_poi(key, city, types, page)

        # json.loads可以对获取回来JSON格式的数据进行解码
        content = json.loads(result)
        # print(content)
        # content的样子其实跟返回结果参数是一样的,可以想象成有很多个字段的excel表格,下面这个语句就是提取出pois那个字段
        pois = content['pois']

        # pois的信息写入空列表里

        for i in range(len(pois)):
            poilist.append(pois[i])

        # 递增page
        page = page + 1

        # 判断当前页下的count是否等于0
        if content['count'] == '0':
            break
    # 将写好poi信息的列表返回
    return poilist


def write_to_excel(poilist, city, types):

    # 这是一个可以将列表写入excel的函数
    # poilist -- 上面得到的poilist
    # city -- 城市名,这个参数是保存excel文件的名字用的
    # types -- poi类别,这个也是为了保存excel文件,可直接看代码最后一行

    # 我们可以把这两行代码理解成新建一个excel表,第一句是新建excel文件
    book = xlwt.Workbook(encoding='utf-8', style_compression=0)
    # 往这个excel文件新建一个sheet表格
    sheet = book.add_sheet(types, cell_overwrite_ok=True)

    # 第一行(列标题)
    sheet.write(0, 0, 'x')
    sheet.write(0, 1, 'y')
    sheet.write(0, 2, 'count')
    sheet.write(0, 3, 'name')
    sheet.write(0, 4, 'tel')
    sheet.write(0, 5, 'address')
    sheet.write(0, 6, 'adname')

    for i in range(len(poilist)):
        name = poilist[i]['name']
        tel = poilist[i]['tel']
        location = poilist[i]['location']
        address = poilist[i]['address']
        adname = poilist[i]['adname']
        lng = str(location).split(",")[0]
        lat = str(location).split(",")[1]

        result = gcj02towgs84(location)
        lng = result[0]
        lat = result[1]

        sheet.write(i + 1, 0, lng)
        sheet.write(i + 1, 1, lat)
        sheet.write(i + 1, 2, 1)
        sheet.write(i + 1, 3, name)
        sheet.write(i + 1, 4, tel)
        sheet.write(i + 1, 5, address)
        sheet.write(i + 1, 6, adname)
    # 最后,将以上操作保存到指定的Excel文件中
    book.save(city + "_" + types + '.xls')


# 这里修改为自己的高德密钥
key = 'b00578b8d5db123455555'

# 这里修改自己的poi类型
types = ['关键词']

# 建议将大区域切分成几个小区域来获取,保证获取的数据齐全
city_list = ['白云区', '天河区', '越秀区', '黄埔区']
# 先遍历city_list里面的每个区域
for city in city_list:
    # 再遍历types里的每个类别
    for type in types:
        poi = Get_times(key, city, type)
        print('当前城市:' + str(city) + ', 分类:' +
              str(type) + ", 总的有" + str(len(poi)) + "条数据")
        write_to_excel(poi, city, type)
        print('*'*50+'分类:' + str(type) + "写入成功"+'*'*50)
print('====爬取完成====')

百度坐标系 (BD-09) 与 火星坐标系 (GCJ-02)的转换,这个文件引入主程,文件名Coordin_transformlat.py。

# * 百度坐标系 (BD-09) 与 火星坐标系 (GCJ-02)的转换
# * 即 百度 转 谷歌、高德
# * @param bd_lon
# * @param bd_lat
# * @returns {*[]}
# */
import math


def bd09togcj02(bd_lon, bd_lat):
    x_pi = 3.14159265358979324 * 3000.0 / 180.0
    x = bd_lon - 0.0065
    y = bd_lat - 0.006
    z = math.sqrt(x * x + y * y) - 0.00002 * math.sin(y * x_pi)
    theta = math.atan2(y, x) - 0.000003 * math.cos(x * x_pi)
    gg_lng = z * math.cos(theta)
    gg_lat = z * math.sin(theta)
    return [gg_lng, gg_lat]


# * 火星坐标系 (GCJ-02) 与百度坐标系 (BD-09) 的转换
# * 即谷歌、高德 转 百度
# */
def gcj02tobd09(lng, lat):
    x_PI = 3.14159265358979324 * 3000.0 / 180.0
    z = math.sqrt(lng * lng + lat * lat) + 0.00002 * math.sin(lat * x_PI)
    theta = math.atan2(lat, lng) + 0.000003 * math.cos(lng * x_PI)
    bd_lng = z * math.cos(theta) + 0.0065
    bd_lat = z * math.sin(theta) + 0.006
    return [bd_lng, bd_lat]


# wgs84转高德
def wgs84togcj02(lng, lat):
    PI = 3.1415926535897932384626
    ee = 0.00669342162296594323
    a = 6378245.0
    dlat = transformlat(lng - 105.0, lat - 35.0)
    dlng = transformlng(lng - 105.0, lat - 35.0)
    radlat = lat / 180.0 * PI
    magic = math.sin(radlat)
    magic = 1 - ee * magic * magic
    sqrtmagic = math.sqrt(magic)
    dlat = (dlat * 180.0) / ((a * (1 - ee)) / (magic * sqrtmagic) * PI)
    dlng = (dlng * 180.0) / (a / sqrtmagic * math.cos(radlat) * PI)
    mglat = lat + dlat
    mglng = lng + dlng
    return [mglng, mglat]


# GCJ02/谷歌、高德 转换为 WGS84 gcj02towgs84
def gcj02towgs84(localStr):
    lng = float(localStr.split(',')[0])
    lat = float(localStr.split(',')[1])
    PI = 3.1415926535897932384626
    ee = 0.00669342162296594323
    a = 6378245.0
    dlat = transformlat(lng - 105.0, lat - 35.0)
    dlng = transformlng(lng - 105.0, lat - 35.0)
    radlat = lat / 180.0 * PI
    magic = math.sin(radlat)
    magic = 1 - ee * magic * magic
    sqrtmagic = math.sqrt(magic)
    dlat = (dlat * 180.0) / ((a * (1 - ee)) / (magic * sqrtmagic) * PI)
    dlng = (dlng * 180.0) / (a / sqrtmagic * math.cos(radlat) * PI)
    mglat = lat + dlat
    mglng = lng + dlng
    return [lng * 2 - mglng, lat * 2 - mglat]


def transformlat(lng, lat):
    PI = 3.1415926535897932384626
    ret = -100.0 + 2.0 * lng + 3.0 * lat + 0.2 * lat * \
        lat + 0.1 * lng * lat + 0.2 * math.sqrt(abs(lng))
    ret += (20.0 * math.sin(6.0 * lng * PI) + 20.0 *
            math.sin(2.0 * lng * PI)) * 2.0 / 3.0
    ret += (20.0 * math.sin(lat * PI) + 40.0 *
            math.sin(lat / 3.0 * PI)) * 2.0 / 3.0
    ret += (160.0 * math.sin(lat / 12.0 * PI) + 320 *
            math.sin(lat * PI / 30.0)) * 2.0 / 3.0
    return ret


def transformlng(lng, lat):
    PI = 3.1415926535897932384626
    ret = 300.0 + lng + 2.0 * lat + 0.1 * lng * lng + \
        0.1 * lng * lat + 0.1 * math.sqrt(abs(lng))
    ret += (20.0 * math.sin(6.0 * lng * PI) + 20.0 *
            math.sin(2.0 * lng * PI)) * 2.0 / 3.0
    ret += (20.0 * math.sin(lng * PI) + 40.0 *
            math.sin(lng / 3.0 * PI)) * 2.0 / 3.0
    ret += (150.0 * math.sin(lng / 12.0 * PI) + 300.0 *
            math.sin(lng / 30.0 * PI)) * 2.0 / 3.0
    return ret

方法二,矩形搜索法获取POI(推荐)

文件名GetPoi_keywords.py,代码如下

import requests
import json
import csv
import re
from Coordin_transformlat import gcj02towgs84

def Get_poi_polygon(key,polygon,keywords,page):
    '''
    这是一个能够从高德地图获取poi数据的函数
    key:为用户申请的高德密钥
    polygon:目标城市四个点的坐标
    keywords:POI数据的类型
    page:当前页数
    '''
    #设置header
    header = {'User-Agent': "Mozilla/5.0 (Windows; U; Windows NT 6.1; en-us) AppleWebKit/534.50 (KHTML, like Gecko) Version/5.1 Safari/534.50"}

    #将输进来的矩形进行格式化
    Polygonstr = str(polygon[0]) + ',' + str(polygon[1]) + '|' + str(polygon[2]) + ',' + str(polygon[3])

    #构建url
    url = 'https://restapi.amap.com/v3/place/polygon?polygon={}&key={}&keywords={}&page={}'.format(Polygonstr, key, keywords, page)
    print(url)
    #用get函数请求数据
    r = requests.get(url, headers=header)

    #设置数据的编码为'utf-8'
    r.encoding = 'utf-8'

    # 将请求得到的数据按照'utf-8'编码成字符串
    data = r.text
    return data

def Get_times_polygon(key,polygon,keywords):
    '''
    这是一个控制Get_poi_polygon申请次数的函数
    '''
    page = 1
    # 执行以下代码,直到count为0的时候跳出循环
    while True:
        # 调用第一个函数来获取数据
        result = Get_poi_polygon(key,polygon, keywords, page)
        # json.loads可以对获取回来JSON格式的数据进行解码
        content = json.loads(result)
        pois = content['pois']
        count = content['count']
        print(count)
        #如果区域内poi的数量大于800,则认为超过上限,返回False请求对区域进行切割
        if int(count) > 800:
            return False
        else:
            for i in range(len(pois)):
                name = pois[i]['name']
                tel = pois[i]['tel']
                location = pois[i]['location']
                if 'address' not in pois[i].keys():
                    address = str(-1)
                else:
                    address = pois[i]['address']
                adname = pois[i]['adname']
                result = gcj02towgs84(location)
                lng = result[0]
                lat = result[1]
                row = [name, address, tel, adname, lng, lat]
                #调用写入函数来保存数据
                writecsv(row, keywords)
            if count == '0':
                break
                # 递增page
            page = page + 1

def writecsv(poilist,keywords):
    """
    这是写入成csv文件的函数
    :param poilist:
    :param keywords:
    :return: 输出的结果存放在代码文件夹下
    """
    with open('{}.csv'.format(keywords),'a',newline='',encoding='utf-8') as csvfile:
        writer = csv.writer(csvfile)
        writer.writerow(poilist)

def get_city_scope(key, cityname):
    parameters = 'key={}&keywords={}&subdistrict={}&output=JSON&extensions=all'.format(key, cityname, 0)
    url = 'https://restapi.amap.com/v3/config/district?'
    # 设置header
    header = {'User-Agent': "Mozilla/5.0 (Windows; U; Windows NT 6.1; en-us) AppleWebKit/534.50 (KHTML, like Gecko) Version/5.1 Safari/534.50"}
    res = requests.get(url,params=parameters)
    jsonData = res.json()
    if jsonData['status'] == '1':
        district = jsonData['districts'][0]['polyline']
        district_list = re.split(';|\|',district)
        xlist, ylist = [], []
        for d in district_list:
            xlist.append(float(d.split(',')[0]))
            ylist.append(float(d.split(',')[1]))
        xmax = max(xlist)
        xmin = min(xlist)
        ymax = max(ylist)
        ymin = min(ylist)
        return [xmin, ymax, xmax, ymin]
    else:
        print ('fail to acquire: {}'.format(jsonData['info']))

主程,根据指定关键词抓取高德店铺的店名、地址及联系方式信息。

import GetPoi_keywords as gp

def Quadrangle(key,polygon,keywords):
    """
    :param key:高德地图密钥
    :param polygon: 矩形左上跟右下坐标的列表
    :param keywords: poi关键词
    :return:
    """
    #准备一个空列表,存放切割后的子区域
    PolygonList = []
    for i in range(len(polygon)):
        currentMinlat = round(polygon[i][0],6)#当前区域的最小经度
        currentMaxlat = round(polygon[i][2],6)#当前区域的最大经度
        currentMaxlon = round(polygon[i][1],6)#当前区域的最大纬度
        currentMinlon = round(polygon[i][3],6)#当前区域的最小纬度

        cerrnt_list = [currentMinlat, currentMaxlon, currentMaxlat, currentMinlon]
        #将多边形输入获取函数中,判断区域内poi的数量
        status = gp.Get_times_polygon(key,cerrnt_list,keywords)
        #如果数量大于800,那么返回False,对区域进行切分,否则返回区域的坐标对
        if status != False:
            print('该区域poi数量小于800,正在写入数据')
        else:
            #左上矩形
            PolygonList.append([
                currentMinlat, #左经
                currentMaxlon, #上纬
                (currentMaxlat+currentMinlat)/2, #右经
                (currentMaxlon+currentMinlon)/2]) #下纬
            #右上矩形
            PolygonList.append([
                (currentMaxlat+currentMinlat)/2,#左经
                currentMaxlon, #上纬
                currentMaxlat, #右经
                (currentMaxlon+currentMinlon)/2#下纬
            ])
            #左下矩形
            PolygonList.append([
                currentMinlat,#左经
                (currentMaxlon+currentMinlon)/2,#上纬
                (currentMaxlat+currentMinlat)/2,#右经
                currentMinlon#下纬
            ])
            #右下矩形
            PolygonList.append([
                (currentMaxlat+currentMinlat)/2,#左经
                (currentMaxlon+currentMinlon)/2,#上纬
                currentMaxlat,#右经
                currentMinlon#下纬
            ])
            print(len(PolygonList))
            if len(PolygonList) == 0:
                break
            else:
                Quadrangle(key,PolygonList,keywords)


#这里修改为自己的高德密钥
key ='12344333304ed3230235cf'

#这里修改自己的poi类型
keywords = '关键词'

#这里输入想要查询的城市
city = '广州'

#调用高德查询行政区的API接口来返回矩形坐标对
Retance = gp.get_city_scope(key,city)

#存储区域矩形的列表
input_polygon = []
input_polygon.append(Retance)

Quadrangle(key,input_polygon,keywords)

本方法也需引入方法一中的Coordin_transformlat.py文件,推荐使用Python3.11,可以最少安装需要的模块,实测运行没有任何问题。