[Python]百度慧眼人口热力图数据爬取--以深圳市为例

利用python爬取深圳市百度慧眼人口热力图数据,线形回归分析对爬取坐标进行转换,最后对爬取数据进行可视化展示。

数据爬取

深圳市百度慧眼人口热力图:http://huiyan.baidu.com/cms/heatmap/shenzhen.html (网址获取来源:利用App Store的“HTTP Traffic”工具对 “i深圳”APP中的“人流热力图”功能进行抓包分析所得,具体操作可查看APP内置教程,这里不再赘述)。
网址后缀为shenzhen,理论上还可以爬取其他城市,如wuhan、shanghai、beijing等。截止目前,查找相关报道得知,百度慧眼只开放了深圳、上海两个城市数据接口,后续大家可自行尝试其他城市。

另外,还请大家不要对端口进行恶意攻击或高频访问,一次请求即可获取全市数据,而且数据按小时更新。

以chrome浏览器为例,打开深圳地区百度慧眼人口热力图网址,按F12进入开发者模式,点击“Network”并刷新网页,查看网络连接解析。按文件大小进行排序,第一个即为所需数据(第二个是深汕合作区的数据,对应的cityId=440300_255_1),对其单击右键 Copy/Copy as cURL(bash),复制为curl。
在这里插入图片描述
打开“curl 转 python requests网站”:https://curl.trillworks.com/,将curl在线转化为requests请求。
在这里插入图片描述
获取requests代码如下

import requests

headers = {
    'Connection': 'keep-alive',
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36',
    'Accept': '*/*',
    'Origin': 'http://huiyan.baidu.com',
    'Sec-Fetch-Site': 'cross-site',
    'Sec-Fetch-Mode': 'cors',
    'Sec-Fetch-Dest': 'empty',
    'Referer': 'http://huiyan.baidu.com/cms/heatmap/shenzhen.html',
    'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8',
}

params = (
    ('cityId', '440300'),
    ('ak', '3gHV4PFiWyZ7xnwnVjDq6vEel99V3jQc'),
)

response = requests.get('https://huiyan.baidu.com/openapi/v1/heatmap/heatmapsearch', headers=headers, params=params)

#NB. Original query string below. It seems impossible to parse and
#reproduce query strings 100% accurately so the one below is given
#in case the reproduced version is not "correct".
# response = requests.get('https://huiyan.baidu.com/openapi/v1/heatmap/heatmapsearch?cityId=440300&ak=3gHV4PFiWyZ7xnwnVjDq6vEel99V3jQc', headers=headers)

对爬取数据进行结构解析,发现数据储存形式为“横坐标_纵坐标_地名计数”,并且每个值由“|”分隔。
在这里插入图片描述

# coding=utf-8
import pandas as pd

# 将数据转化为dataframe格式
js1= response.json()	# 获取响应文件的json格式
js2=js1['result']['data']	# 提取data部分
str1 = js2.split("|")	# 以“|”符号对data进行分隔
df = pd.DataFrame(str1)	# 传入dataframe

# 利用str.split()将数据分为多列
df['x'] = df[0].str.split('_').str[0]
df['y'] = df[0].str.split('_').str[1]
df['value'] = df[0].str.split('_').str[2]

需要注意的是,由于“data”是以 “|”结尾,df最后一行为空值,需要对其剔除。

# 剔除含有空值的行
df = df.dropna()

得到最终结果:
在这里插入图片描述

坐标转换

我们注意到,这里得所到的横纵坐标为{126XXXXX,25XXXXX}形式,根据网页源码(coordType: ‘bd09mc’)可知这是百度地图墨卡托坐标,需要转换为百度经纬度坐标。如果大家后续用echarts做可视化,也可不用转换,直接将坐标系参数设置为’bd09mc’。
由于百度墨卡托坐标转换需要调用百度JavaScript的API,实现起来比较麻烦,这里我们利用坐标映射的方法做近似转换。首先,利用excel的PowerMap功能对既有数据进行可视化展示(这里主要考虑到数据量不大,做轻量化分析即可,当然,也可以根据自己熟悉的其他工具展示,如ARCGIS、QGIS、echarts等),发现整体形状与深圳城市肌理相似,据此判断我们得到的横纵坐标与经纬度坐标存在线性相关。
在这里插入图片描述
将地图缩放到最大级别,对比发现存在一些识别度较高的孤点
在这里插入图片描述
我们找出一系列映射点,利用百度拾取坐标系统:http://api.map.baidu.com/lbsapi/getpoint/index.html,获取映射点的百度经纬度坐标,形成一系列坐标映射关系表:

LNGLATXY
113.90457922.656801126799672573996
113.79025122.676882126672282576368
114.57261722.505705127543022555841
114.28426122.801803127222052591362
113.93143422.848654126829162596991
113.88237322.853966126774862597634

利用pandas、sklearn模块对坐标映射表进行回归分析:

1、读取坐标映射表

# 复制上表,读取剪切板数据
data = pd.read_clipboard()

在这里插入图片描述
我们查看一下各列数据之间的相关性,发现“LNG与X”以及“LAT与Y”之间相关性接近1,说明他们之间线性相关较强,印证了前面的猜想,理论上我们选取的映射坐标对越多,精度越高。

# 相关性系数分析
data.corr()

在这里插入图片描述

2、利用sklearn进行回归分析

  • 对经度和横坐标拟合:

代码参考《Python回归分析五部曲(一)—简单线性回归》:https://www.cnblogs.com/shujufenxi/p/9054439.html

from sklearn.linear_model import LinearRegression
#估计模型参数,建立回归模型
'''
(1) 首先导入简单线性回归的求解类LinearRegression
(2) 然后使用该类进行建模,得到lrModel的模型变量
'''

lrModel = LinearRegression()
#(3) 接着,我们把自变量和因变量选择出来
x = data[['X']]
y = data[['LNG']]

#模型训练
'''
调用模型的fit方法,对模型进行训练
这个训练过程就是参数求解的过程
并对模型进行拟合
'''
lrModel.fit(x,y)

#对回归模型进行检验
print('决定系数(R^2):',lrModel.score(x,y))

#查看截距
alpha = lrModel.intercept_[0]

#查看参数
beta = lrModel.coef_[0][0]

print('alpha值:',alpha,"    beta值:",beta)

输出结果:

决定系数(R^2: 0.9999998457083179
alpha值: -0.027743944995606284     beta值: 8.98523370006391e-06

得到经度与横坐标之间的联系:
经度:lng = -0.027743944995606284 + 8.98523370006391e-06 * x

  • 同样,对纬度和纵坐标拟合:
lrModel_2 = LinearRegression()

x_2 = data[['Y']]
y_2 = data[['LAT']]

#模型训练
'''
调用模型的fit方法,对模型进行训练
这个训练过程就是参数求解的过程
并对模型进行拟合
'''
lrModel_2.fit(x_2,y_2)

#对回归模型进行检验
print('决定系数(R^2):',lrModel_2.score(x,y))

#查看截距
alpha_2 = lrModel_2.intercept_[0]

#查看参数
beta_2 = lrModel_2.coef_[0][0]

print('alpha_2值:',alpha_2,"    beta_2值:",beta_2)

输出结果:

决定系数(R^2: 0.9999993702024847
alpha_2值: 1.20316833550093     beta_2值: 8.33483092560397e-06

得到纬度与纵坐标之间的联系:
纬度:lat = 1.20316833550093 + 8.33483092560397e-06 * y

3、坐标转换输出

# 添加百度坐标
df['lng'] = alpha+beta*df['x'].astype('f')
df['lat'] = alpha_2+beta_2*df['y'].astype('f')

# 剔除首列
df = df.drop([0],axis=1)

# 获取当前时间,请求结果中的time字段
time = js1['result']['time']

#输出到csv文件,结合自己所需文件编码类型,自行调整encoding的值
df.to_csv('./data/'+time+ '.csv',index=0,encoding='utf-8')

完整代码

# coding=utf-8
import requests
import pandas as pd
import json
from sklearn.linear_model import LinearRegression
import time

def req_data(url):
    '''
    数据爬取,返回json类结构
    '''
    headers = {
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64; rv:68.0) Gecko/20100101 Firefox/68.0',
        'Accept': '*/*',
        'Accept-Language': 'zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2',
        'Referer': 'http://huiyan.baidu.com/cms/heatmap/shenzhen.html',
        'Origin': 'http://huiyan.baidu.com',
        'DNT': '1',
        'Connection': 'keep-alive',
        'Cache-Control': 'max-age=0',
    }

    params = (
        ('cityId', '440300'),
        ('ak', '3gHV4PFiWyZ7xnwnVjDq6vEel99V3jQc'),
    )
    
    try:
        r = requests.get(url=url, headers=headers, params=params)
        r.raise_for_status()
        r.encoding = r.apparent_encoding
        return r.json()
    except:
        return "网络传输异常"


def linear_regression(df_1,df_2):
    '''
    对df_1,df_2两个单列进行线性回归分析
    '''
    lrModel = LinearRegression() # 构建线性回归模型
    lrModel.fit(df_1,df_2)      # 拟合
    #对回归模型进行检验
    print('决定系数(R^2):',lrModel.score(df_1,df_2))
    #查看截距
    alpha = lrModel.intercept_[0]
    #查看参数
    beta = lrModel.coef_[0][0]
    return alpha,beta

def clean_data(json):
    '''
    数据整理,对返回数据进行重构
    '''
    data_cell = json['result']['data'].split("|")
    df = pd.DataFrame(data_cell)
    # 以‘_’为分隔符,拆分为多列,并删除首列
    df['x'] = df[0].str.split('_').str[0]
    df['y'] = df[0].str.split('_').str[1]
    df['value'] = df[0].str.split('_').str[2]
    df = df.drop([0],axis=1)
    # 相应文件的‘data’以“|”结尾,需剔除最后一行
    df = df.dropna()
    # 传入映射坐标列表,格式为{百度坐标系经度,百度坐标系维度,横坐标,纵坐标}
    data = pd.read_csv('./data/mapping_coordinates.csv')
    # 拟合映射坐标
    alpha_1,beta_1 = linear_regression(data[['X']],data[['LNG']])
    alpha_2,beta_2 = linear_regression(data[['Y']],data[['LAT']])
    # 添加百度坐标
    df['lng'] = alpha_1+beta_1*df['x'].astype('f')
    df['lat'] = alpha_2+beta_2*df['y'].astype('f')
    return df

def main():
    url = r"https://huiyan.baidu.com/openapi/v1/heatmap/heatmapsearch" # 深圳市百度慧眼人流热力图网址
    js = req_data(url)
    # 获取当前时间,请求结果中的time字段
    global file_time
    file_time = js['result']['time']
    df = clean_data(js)
    #输出到csv文件,结合自己所需文件编码类型,自行调整encoding的值
    df.to_csv('./data/'+file_time+'.csv',index=0,encoding='utf-8')

if __name__ == '__main__':
    while True:
        main()
        with open('spider.log','a',encoding='utf-8') as f:
            f.write(file_time + '已录入\n')
        time.sleep(3600)
  • 注:mapping_coordinates.csv文件格式如下
    在这里插入图片描述
评论 541
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值