蠢新使用python利用高德API爬取高德POI数据程序总是假死,求哥哥们救救孩子。
本帖最后由 最爱橙子汁 于 2020-3-19 00:35 编辑毕业设计需要用到一些城市的POI数据,本着自己动手丰衣足食的原则,就从自己写了段python代码从高德地图爬取POI数据。
可是爬取过程中总是不顺利,程序总是爬着爬着就不动了,有时爬几千条假死,有时爬几万条假死。数据库中没有新数据增加,程序也不报错,也不中止。CPU,内存占用也不高,硬盘中也还有空间,现在是实在不知道如何解决了。所以想让请教一番。
奈何本人水平真的有限,这程序总跑着跑着就不跑了,整个武汉的poi点少数也有几十万,我总是爬几万程序就停下了。真是气人!!其实,还有个问题,不知道是不是巧合,我这里说下,就是当我运行程序时,如果我此时我时不时的去数据库瞅瞅增加了多少条,数据都会一直增加,但是当我跑去睡觉,或者出门一会回来之后,想看看跑了多少条数据,这时候程序就处于假死状态。
这里对高德地图的接口做个说明:
多边形搜索API接口,请求方式get
https://restapi.amap.com/v3/place/polygon?parameters请求参数:
polygon:如果多边形为矩形,左上,右下经纬度坐标
高德文档上说这个接口一次最多只能获得1000个POI数据,我实际测试了下,其实最多只能获得800多个数据。
因此,当我爬取一个矩形区域时,
如果返回数据大于800个,我就认为这个区域的poi数据没有获取完全,将当前区域四等分,再逐个爬取。
如果反数据小于800个,则认为该区域poi数据就这么多,就将当前区域的poi数据写入数据库。
我以为武汉市为例,写了如下代码:
import requests
import pymysql
import time
import math
import socket
'''
https://restapi.amap.com/v3/place/polygon?polygon=113.705304,31.366201|115.089744,29.974252&key=10518669c9fd0a0532e41189d61e1e9b&extensions=all&types=010000&offset=10&page=90
'''
# 初始区域的经纬度
lon_l = 113.705304
lan_l = 31.366201
lon_r = 115.089744
lan_r = 29.974252
# 要爬取POI的类型
types = '010000|020000|030000|040000|050000|060000|' \
'070000|080000|090000|100000|110000|120000|' \
'130000|140000|150000|160000|170000|180000|' \
'190000|200000|210000|220000|230000|240000|' \
'970000|990000'
# 获取POI的接口
url = 'https://restapi.amap.com/v3/place/polygon?' \
'polygon={lon_l},{lan_l}|{lon_r},{lan_r}' \
'&key=10518669c9fd0a0532e41189d61e1e9b&extensions=all' \
'&types={types}&offset=25&page={page}'
# point
class Point:
"""
用经纬度来描述一个点
"""
def __init__(self, lon, lan):
self.lon = lon # 经度
self.lan = lan # 纬度
# area
class Rectangle:
"""
用左上角的经纬度和右下角的经纬度描述一个矩形区域
"""
def __init__(self, p_l, p_r):
self.p_l = p_l # 左上角的点
self.p_r = p_r # 右下角的点
# 初始矩形的左上点和右下点
p_l = Point(lon_l,lan_l)
p_r = Point(lon_r,lan_r)
# 初始矩形,內含两点,左上,右下
rec = Rectangle(p_l,p_r)
'''
打开一个URL,获取返回信息
'''
def open_html(real_url):
NET_STATUS=False
while not NET_STATUS:
try:
data = requests.get(real_url).json()
return data
except socket.timeout:
print("NET_STATURS IS NOT GOOD")
NET_STATUS=False
except :
print("OTHER WRONG")
# 把一个区域的POI点存进数据库
def get_pois(rec,url,data_count):
url = url.format(lon_l = rec.p_l.lon , lan_l = rec.p_l.lan ,
lon_r = rec.p_r.lon , lan_r = rec.p_r.lan ,
types = types,page="{page}")
f_url = open("url.txt","a",encoding="UTF-8")
data= open_html(url.format(page=0))
if data["status"] == "1":
# 执行到这里,说明url中含有poi数据,下面开始爬取
all_page=math.ceil(data_count/25)
for page in range(all_page):
try:
# 从第1页开始到第最后一页
url_real = url.format(page = page+1)
f_url.write(url_real+'\n')
data = open_html(url_real)
if data["status"] == "1" : # 判断第page+1页是否有内容
pois = data["pois"]
# 将数据储存在数据库
# 优化点:db先创建好,一次用完不关,最后再关
global db_global
db = db_global
cursor = db.cursor()
str_sql = 'insert into t_poi(id,name,address,typecode,lon,lan,pcode,pname,citycode,cityname,adcode,adname) ' \
'values (%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s)'
count=0
for poi in pois:
try:
count += 1
value = , poi['name'], poi["address"], poi['typecode'],
poi['location'].split(','), poi['location'].split(','),
poi['pcode'], poi['pname'], poi['citycode'], poi['cityname'], poi['adcode'], poi['adname']]
if [] in value:
value)]=''
try:
cursor.execute(str_sql,value)
db.commit()
except:
Exception
# 打印日志文件
f=open("wrong.txt",'a',encoding="UTF-8")
time_fomat = '%Y-%m-%d %X'
time_current = time.strftime(time_fomat)
f.write(time_current+ '第{page}页,第{index}个出错\n'.format(page=page,index=count))
except:
pass
cursor.close()
except:
pass
#检测器
run_count = 0
# 爬取,分析一个区域的POI数量,多于800则将区域四等分,并递归,低于800则进行爬取
def crawl_pois(rec,url):
# 计算进行了几次分析的计数器,判断程序是否卡死
global run_count;
run_count += 1;
f_count = open("count.txt",'a',encoding="UTF-8");
f_count.write("这是第{_count}次运行分析一个区域\n".format(_count=run_count))
# 根据矩形区域的经纬度坐标,拼接url
url_real = url.format(lon_l=rec.p_l.lon, lan_l=rec.p_l.lan,
lon_r=rec.p_r.lon, lan_r=rec.p_r.lan,
types=types, page="{page}")
# 获取url返回的数据
data = open_html(url_real.format(page=0))
if data["status"] == "1":
# 执行到这里,说明url中含有poi数据,下面开始判断区域POI数量
data_count = int(data['count'])
ifdata_count> 800:
# 如果区域POI数据大于800则进行四等分
rec_A = Rectangle(Point(0,0),Point(0,0))
rec_B = Rectangle(Point(0,0),Point(0,0))
rec_C = Rectangle(Point(0,0),Point(0,0))
rec_D = Rectangle(Point(0,0),Point(0,0))
# 经纬度差
lon_d = (rec.p_r.lon - rec.p_l.lon)/2
lan_d = (rec.p_l.lan - rec.p_r.lan)/2
# 计算每个小矩形的左上点,和右下点
rec_A.p_l.lon = float(format(rec.p_l.lon,'6f'))
rec_A.p_l.lan = float(format(rec.p_l.lan,'6f'))
rec_A.p_r.lon = float(format(rec.p_l.lon + lon_d,'6f'))
rec_A.p_r.lan = float(format(rec.p_r.lan + lan_d,'6f'))
rec_B.p_l.lon = float(format(rec.p_l.lon + lon_d,"6f"))
rec_B.p_l.lan = float(format(rec.p_l.lan,"6f"))
rec_B.p_r.lon = float(format(rec.p_r.lon,"6f"))
rec_B.p_r.lan = float(format(rec.p_r.lan + lan_d,"6f"))
rec_C.p_l.lon = float(format(rec.p_l.lon,"6f"))
rec_C.p_l.lan = float(format(rec.p_r.lan + lan_d,"6f"))
rec_C.p_r.lon = float(format(rec.p_l.lon + lon_d,"6f"))
rec_C.p_r.lan = float(format(rec.p_r.lan,"6f"))
rec_D.p_l.lon = float(format(rec.p_l.lon + lon_d,"6f"))
rec_D.p_l.lan = float(format(rec.p_r.lan + lan_d,"6f"))
rec_D.p_r.lon = float(format(rec.p_r.lon,"6f"))
rec_D.p_r.lan = float(format(rec.p_r.lan,"6f"))
recs = []
recs.append(rec_A)
recs.append(rec_B)
recs.append(rec_C)
recs.append(rec_D)
# 对四个小矩形分别进行爬取,这里使用递归
for rec_s in recs :
# 如果一个区域出现异常,就进行下个区域
try:
crawl_pois(rec_s,url)
except:
pass
else:
# 如果该矩形区域poi点的数量少于800就进行爬取
get_pois(rec,url,data_count)
else:
print("出错")
'''
下面是程序的入口
'''
# 创建一个全局的连接
db_global = pymysql.connect("localhost", "root", "111111", "poi")
# 对一个矩形进行爬取分析,这里是对武汉地区的左上角和右下角的经纬度围成的矩形进行爬取
crawl_pois(rec,url)
# 关闭数据库
db_global.close()
帮你看了看,程序逻辑上没有问题,程序卡死也没遇到,我觉得你代码里面不必要的try太多了
还有前面几个朋友说的递归问题,理论上倒也不至于出现栈溢出,不过我还是改用了队列来实现
贴一下我改过运行的代码
```python
import requests
import pymysql
from DBUtils.PooledDB import PooledDB
import time
import queue
import math
import socket
from concurrent.futures import ThreadPoolExecutor
'''
https://restapi.amap.com/v3/place/polygon?polygon=113.705304,31.366201|115.089744,29.974252&key=10518669c9fd0a0532e41189d61e1e9b&extensions=all&types=010000&offset=10&page=90
'''
# 初始区域的经纬度
lon_l = 113.705304
lan_l = 31.366201
lon_r = 115.089744
lan_r = 29.974252
# 要爬取POI的类型
types = '010000|020000|030000|040000|050000|060000|' \
'070000|080000|090000|100000|110000|120000|' \
'130000|140000|150000|160000|170000|180000|' \
'190000|200000|210000|220000|230000|240000|' \
'970000|990000'
# 获取POI的接口
url = 'https://restapi.amap.com/v3/place/polygon?' \
'polygon={lon_l},{lan_l}|{lon_r},{lan_r}' \
'&key=10518669c9fd0a0532e41189d61e1e9b&extensions=all' \
'&types={types}&offset=25&page={page}'
# point
class Point:
"""
用经纬度来描述一个点
"""
def __init__(self, lon, lan):
self.lon = lon# 经度
self.lan = lan# 纬度
# area
class Rectangle:
"""
用左上角的经纬度和右下角的经纬度描述一个矩形区域
"""
def __init__(self, p_l, p_r):
self.p_l = p_l# 左上角的点
self.p_r = p_r# 右下角的点
# 初始矩形的左上点和右下点
p_l = Point(lon_l, lan_l)
p_r = Point(lon_r, lan_r)
# 初始矩形,內含两点,左上,右下
rec = Rectangle(p_l, p_r)
db_pool = PooledDB(pymysql, 10, host='localhost', user='root', port=3306,
passwd='123', db='poi', use_unicode=True)
thread_pool = ThreadPoolExecutor(max_workers=10)
def open_html(real_url):
while True:
try:
data = requests.get(real_url).json()
return data
except socket.timeout:
print("NET_STATURS IS NOT GOOD")
except:
print("OTHER WRONG")
def get_pois(rec: Rectangle, url: str, data_count: int):
"""获取区域数据
:param rec: 目标区域
:param url: api地址
:param data_count: poi数量
"""
url = url.format(lon_l=rec.p_l.lon, lan_l=rec.p_l.lan,
lon_r=rec.p_r.lon, lan_r=rec.p_r.lan,
types=types, page="{page}")
f_url = open("url.txt", "a", encoding="UTF-8")
data = open_html(url.format(page=0))
if data["status"] == "1":
# 执行到这里,说明url中含有poi数据,下面开始爬取
all_page = math.ceil(data_count / 25)
conn = db_pool.connection()
cursor = conn.cursor()
for page in range(all_page):
# 从第1页开始到第最后一页
url_real = url.format(page=page + 1)
f_url.write(url_real + '\n')
data = open_html(url_real)
if data["status"] == "1":# 判断第page+1页是否有内容
pois = data["pois"]
# 将数据储存在数据库
# 优化点:db先创建好,一次用完不关,最后再关
str_sql = 'insert into t_poi(id,name,address,typecode,lon,' \
'lan,pcode,pname,citycode,cityname,adcode,adname)' \
' values (%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s)'
count = 0
for poi in pois:
count += 1
value = , poi['name'], poi["address"],
poi['typecode'], poi['location'].split(','),
poi['location'].split(','), poi['pcode'],
poi['pname'], poi['citycode'], poi['cityname'],
poi['adcode'], poi['adname']]
if [] in value:
value)] = ''
try:
cursor.execute(str_sql, value)
conn.commit()
except Exception as e:
# 打印日志文件
print(e)
f = open("wrong.txt", 'a', encoding="UTF-8")
time_fomat = '%Y-%m-%d %X'
time_current = time.strftime(time_fomat)
f.write(time_current + '第{page}页,第{index}个出错\n'
.format(page=page, index=count))
cursor.close()
conn.close()
def crawl_pois(recs, url: str):
"""分析一个区域的POI数量,多于800则将区域四等分,加入队列
:param recs: 初始队列
:param url:
:return:
"""
count = 0# 计算进行了几次分析的计数器,判断程序是否卡死
with open("count.txt", 'a', encoding="UTF-8") as f_count:
while not recs.empty():
rec = recs.get()
f_count.write("这是第{_count}次运行分析一个区域\n".format(_count=count))
# 根据矩形区域的经纬度坐标,拼接url
url_real = url.format(lon_l=rec.p_l.lon, lan_l=rec.p_l.lan,
lon_r=rec.p_r.lon, lan_r=rec.p_r.lan,
types=types, page="{page}")
# 获取url返回的数据
data = open_html(url_real.format(page=0))
if data["status"] == "1":
# 执行到这里,说明url中含有poi数据,下面开始判断区域POI数量
data_count = int(data['count'])
if data_count > 800:
# 经纬度差
lon_d = (rec.p_r.lon - rec.p_l.lon) / 2
lan_d = (rec.p_l.lan - rec.p_r.lan) / 2
rec_A = Rectangle(
Point(rec.p_l.lon, rec.p_l.lan),
Point(float('%.6f' % (rec.p_l.lon + lon_d)),
float('%.6f' % (rec.p_r.lan + lan_d)))
)
rec_B = Rectangle(
Point(float('%.6f' % (rec.p_l.lon + lon_d)),
rec.p_l.lan),
Point(rec.p_r.lon,
float('%.6f' % (rec.p_r.lan + lan_d)))
)
rec_C = Rectangle(
Point(rec.p_l.lon,
float('%.6f' % (rec.p_r.lan + lan_d))),
Point(float('%.6f' % (rec.p_l.lon + lon_d)),
rec.p_r.lan)
)
rec_D = Rectangle(
Point(float('%.6f' % (rec.p_l.lon + lon_d)),
float('%.6f' % (rec.p_r.lan + lan_d))),
Point(rec.p_r.lon, rec.p_r.lan)
)
temp =
for r in temp:
recs.put(r)
else:
thread_pool.submit(get_pois, rec, url, data_count)
else:
print("出错")
count += 1
if __name__ == '__main__':
recs = queue.LifoQueue()
recs.put(rec)
crawl_pois(recs, url)
```
把没必要的try删掉了,为了提高效率用上了多线程,因为懒得搞个线程锁所以url.txt和wrong.txt文件大概率是不准确的
ps 回复的几分钟,数据已经27万条了 抢个沙发,是否行数达到了上限? 你确定你的递归有出口? Ts灬花痴 发表于 2020-3-19 01:21
抢个沙发,是否行数达到了上限?
是指mysql数据库的的行数么?如果达到行数上限的话,那我每次重新执行,得到的总行数应该是差不多呀,不至于出现有时爬几万条,有时只爬几千条的情况吧。。。。 本帖最后由 最爱橙子汁 于 2020-3-19 09:19 编辑
带色的小马甲 发表于 2020-3-19 08:25
你确定你的递归有出口?
啊,出口?在crawl_pois(rec, url)方法里面,有这么一段
def crawl_pois(rec,url):
# 无关紧要的部分
# 根据矩形区域的经纬度坐标,拼接url
# 获取url返回的数据
data = open_html(url_real.format(page=0))
if data["status"] == "1": # 状态码为1,正常
data_count = int(data['count']) #返回数据的数量
ifdata_count> 800:
# 如果区域POI数据大于800则进行四等分,并再逐个执行本方法
recs = []
recs.append(rec_A)
recs.append(rec_B)
recs.append(rec_C)
recs.append(rec_D)
for rec_s in recs : #进行递归
crawl_pois(rec_s, url)
else:
# 如果该矩形区域poi点的数量少于800,就将数据写入数据库
get_pois(rec,url,data_count)
else:
print("出错")
这个地方,如果获取的数据小于800,就将数据写入数据库,算不算出口呀,小哥哥。
哥们,你不要用递归,换队列试试,你深度遍历一直传值,他的深度就越来越深,空间复杂度太高了电脑会卡死,你换广度遍历会好一点。 德古拉伯 发表于 2020-3-19 09:49
哥们,你不要用递归,换队列试试,你深度遍历一直传值,他的深度就越来越深,空间复杂度太高了电脑会卡死, ...
其实,电脑也不卡,是程序死了。我试下队列,谢谢小哥哥啦。 天黑我隐身 发表于 2020-3-19 18:04
帮你看了看,程序逻辑上没有问题,程序卡死也没遇到,我觉得你代码里面不必要的try太多了
还有前面几个朋 ...
其实本来是没try的,因为程序老自己停,所以我又不知道哪里出问题,所以就加上try了。真是谢谢大佬了,我试下您的程序。 天黑我隐身 发表于 2020-3-19 18:04
帮你看了看,程序逻辑上没有问题,程序卡死也没遇到,我觉得你代码里面不必要的try太多了
还有前面几个朋 ...
大佬,您把武汉的数据爬完了么?我看了下接口的配额已经到上限了。
页:
[1]
2