闲来无事,想对近来学习的爬虫做个简单的尝试,做些有趣的东西;同时也记录下自己在实际应用过程中遇到的各种坑,故此写下学习笔记。第一次写这类笔记,很多不足之处,欢迎指正

该项目想实现的功能是:

1、根据某个关键字,通过百度地图、高德地图等途径获取该关键字相关的所有地址;如「超市」,即为获取所有超市地址,仅限上海市。

2、根据获取的地址,匹配出相应的地址经纬度,将其标注于地图上;同时计算出对应某个地址一定距离内的超市个数,最终可视化实现上海市的超市分布情况及核心经济区域。

针对上述两个功能,分两步走实现最终目标。

一、爬取超市地址

1、高德地图爬取尝试

首先第一个想到的是在高德地图上爬取超市信息,那么我们先来看下其地图界面(图1)。很明显,输入关键字「超市」后,左侧出现的竖框中包含了所有的超市信息,一页20个,但最多只能显示45页。但上海市肯定不止900家超市,故需要根据上海市各区来进行筛选。

图1

上海有14个区县,人工去筛选费时费力。观察到其URL特别长,包含相当多的信息,故我们尝试从URL入手分析,看能否从中找到规律。下面是两个区的URL地址,特别长的一串字元,但还是可以从中找出点规律来的。

「query=%E8%B6%85%E5%B8%82」:这个后面一大堆百分比和字母,其实翻译过来就是「超市」,只是因为编码的问题显示差异。

「city=310000」:这个是上海市的编号吧,有点想不通为啥是这个

「geoobj=121.256723%7C31.165719%7C121.294747%7C31.182353」:这个很明显应该是经纬度数据,应该是每个区的最高经纬度值和最低经纬度值吧,猜的

「zoom=17」:这个应该是地图的缩放比例

「classify_data=business_area_flag%3D1%3Badcode%3D310101%2Bfilter_keywords%3D%E8%B6%85%E5%B8%82%2Bsort_rule%3D0%3Breserved_keywords%3Dtrue」:这个最长了,只看懂了一部分,其中「3D310101」正是我们要区分的区县编号,但是我逐个看了下14个区县的编号,并不是连续的从01-14的,所以爬取的时候还需要知道这些编号。

「pagenum=1」:这个不用解释都知道,就是图1左侧框下面的页码

黄埔区超市:ditu.amap.com/search?

徐汇区超市:

ditu.amap.com/search?

分析完URL后,那么就可以通过区县编码的调整和页码的调整,逐个去爬取所有超市信息了。所以,之后的问题就是页面编码解析的问题了。

打开火狐浏览器的查看器,可以看到左侧框的所有信息都包含在

<div class=」serp-body」>(.*?)</div>里面,也就是说我只需要将每一页这部分代码里的信息提取出来就行了,胜利就在前方。

但是,我们再来看看这个页面的源代码,源代码下面只有简单的三行,查看器里显示的内容是没有的(应该是被隐藏了或者是调用其他方式显示),因此此路不通····。在网上查了半天资料,也未能发现有效的解决办法(怪自己能力有限),故只能换条路再走。

2、百度地图爬取

上面的路短时间内没办法通了,那么就只能改走其他路 。最好的办法就是百度,感谢伟大的度娘,终于找到一篇可以借鉴的文章。

在知乎上,有大神写了一篇《用Python抓取百度地图里的店名,地址和联系方式》,地址链接为zhuanlan.zhihu.com/p/25。啥都不用说了,代码写得相当简洁明了,直接改成我所需要的。

当然,原始代码爬取的是以城市为单位的,而我这同样做了简单的处理,对上海市所有区县分别进行爬取。最终爬取出来9757家超市,包含超市名称、超市地址等信息。(感觉还是少了,删重后才8900多条数据)

二、地址可视化

1、经纬度获取

要将地址呈现在地图上,同时计算地址间的距离,很明显需要地址对应的经纬度。之前用python中的geocoder库对地址进行经纬度提取,但该方法缺陷在于提取速度受网路稳定性影响较大,同时部分地址难于解析。怎么办?继续度娘

终于找到一个批量解析地址的软体xGeocoding,该软体可以快速解析出地址,而且简单易学。不用说了,就用它了。

2、距离计算

花了近2个小时终于将9700多条数据经纬度解析出来,接下来就是需要计算各个经纬度之间的距离。百度了好久,发现有好多种经纬度距离计算方法,最终也没研究透到底为啥这么计算,最后选择了其中一种计算公式(反正测试了下结果都差不多)

distance=6371004*acos(sin(radians(lng))*sin(radians(lng_other))+cos(radians(lng))*cos(radians(lng_other))*cos(radians(lat_other-lat)))

「radians」:用于计算弧度,其实就是角度,即lng/180*pi;

要做的事很简单,计算每个地址与其他地址之间的距离,然后统计下距离小于max_distance的个数(我这里设置为距离1000米以内)。但是,问题是python中math库下面的sin、acos、radians等函数貌似只能用float,而无法用向量进行直接计算。在这样的情况下,如果用for循环,那数据量是杠杠的(9700*9700,近1亿)。不死心尝试了下,半个小时只运行了10%都不到,快哭了,果断放弃。

用for循环肯定不行了,那么只能用向量进行计算了。突然想起来,python中的numpy库貌似也有sin函数,该函数可以对向量进行计算;radians这个函数可以直接用公式转换计算,这样也可以转换为向量;唯一不能用向量的是acos这个函数。

最后把程序改了下,重新开始跑数据,结果500s不到就跑完,这提升杠杠的。

3、可视化呈现

万事具备,只欠可视。现在啥都准备好了,只需要将数据点根据经纬度标注于地图上,然后根据最近距离超市个数大小画点。该想法可以用python实现,但我更希望能够看到热力地图,目前受个人能力限制,无法在python上实现热力地图,故最终选择采用其他软体来呈现可视化效果。

选取的是BDP个人版,操作简单易学,直接上手,最终效果如下:

值得注意的是,红框内还可以加入条件栏位,筛选自己想要的数据点进行呈现。如:上海市超市名称中含华联的数据

至此,本次学习暂告一段落,最大的感受就是:不动手实践下,你永远不知道有多少坑等著你去填


推荐阅读:
查看原文 >>
相关文章