-
Notifications
You must be signed in to change notification settings - Fork 2
/
baidumapapi.py
312 lines (257 loc) · 8.58 KB
/
baidumapapi.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
from pathlib2 import Path
import json
import requests
from urllib.parse import quote
import aiohttp
import asyncio
import logging
# 初始化日志配置
logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG)
streamHandler = logging.StreamHandler()
streamHandler.setLevel(logging.INFO)
fileHandler = logging.FileHandler('runtime.log', 'a', 'utf-8')
fileHandler.setLevel(logging.DEBUG)
formatter4Stream = logging.Formatter(
'%(asctime)s : %(message)s',
'%H:%M:%S')
formatter4File = logging.Formatter(
'%(levelname)-8s - %(asctime)s(%(name)s):\n%(message)s',
'%Y-%m-%d %H:%M:%S')
streamHandler.setFormatter(formatter4Stream)
fileHandler.setFormatter(formatter4File)
logger.addHandler(streamHandler)
logger.addHandler(fileHandler)
# 初始化,读取配置文件
CONFIG_FILE = './setting.json'
if not Path(CONFIG_FILE).exists():
raise Exception('当前目录下,不存在setting.json配置文件')
with open(CONFIG_FILE, "r", encoding='utf-8') as f:
SETTING = json.load(f)
logger.info('=' * 30 + '配置文件' + '=' * 30)
logger.info(json.dumps(SETTING, indent=2, sort_keys=True))
logger.info('=' * 65)
# 初始化AK索引
AK_INDEX = -1
# 复制AK列表,初始化无效列表
AK_VALID = SETTING['apiKey'].copy()
AK_INVALID = []
# 初始AK总数
AK_AMOUNT = len(AK_VALID)
def getAK(invalidate=False, invalidAK=None):
'''
获取、更换AK
'''
global AK_VALID
global AK_INDEX
if invalidate:
if invalidAK:
ak = invalidAK
else:
ak = AK_VALID[AK_INDEX]
logger.info(
f'AK:{ak}失效。剩余{len(AK_VALID)-1}个AK'
)
AK_INVALID.append(ak)
if ak in AK_VALID:
AK_VALID.remove(ak)
AK_INDEX = -1
if len(AK_VALID) == 0:
raise Exception("AK耗尽")
AK_INDEX += 1
AK_INDEX %= len(AK_VALID)
key = AK_VALID[AK_INDEX]
return key
# 输出AK信息
def akInfo(detail=False):
'''
输出AK信息
'''
global AK_INDEX
global AK_VALID
global AK_INVALID
global AK_AMOUNT
logger.info('=' * 30 + 'AK信息' + '=' * 30)
logger.info(
'总数/有效/无效:' + f'{AK_AMOUNT} / {len(AK_VALID)} / {len(AK_INVALID)}')
if detail:
logger.info('有效AK:\n' + '\n'.join([f'\t{ak}' for ak in AK_VALID]))
logger.info('无效AK:\n' + '\n'.join([f'\t{ak}' for ak in AK_INVALID]))
logger.info('=' * 65)
session = requests.Session()
def baiduApi(url, retry=0):
'''
添加AK,请求百度API,并处理异常
一般来说有三种错误
1、请求是网络原因造成连接失败,自动重新请求
2、解析json出现异常,重新请求没用,抛出异常
3、解析json后,AK访问限制,更换AK并重新请求
'''
# 超过重试次数
if retry >= 5:
logger.info('请求次数超过5次,放弃请求')
return None
# 添加AK的url
url_ak = url + f'&ak={getAK()}'
# 网络连接
try:
res = session.get(url_ak)
except Exception as e:
logger.exception(e)
logger.info(f'请求API接口时发生异常,重试:{retry+1}次')
return baiduApi(url, retry+1)
# json解析
try:
res = json.loads(res.text)
except Exception as e:
logger.info('=' * 30 + 'json文本解析出现异常' + '=' * 30)
logger.info(url)
logger.info(res.text)
logger.info(e)
logger.info('=' * 65)
# raise Exception()
return None
# AK、请求状态
if res['status'] != 0:
'''
错误代码:
http://lbsyun.baidu.com/index.php?title=webapi/guide/webservice-geocoding-abroad
201:APP被用户自己禁用
210:APP IP校验失败
240:APP服务被禁用
302:天配额
401:并发
'''
# 如果是并发限制,重新请求
if res['status'] == 401:
return baiduApi(url)
# 如果是AK限制,更换AK
elif res['status'] == 4 or res['status'] == 302 \
or res['status'] == 240 or res['status'] == 201 \
or res['status'] == 401 or res['status'] == 210:
getAK(True)
return baiduApi(url)
# 服务器内部错误,重新请求
elif res['status'] == 1:
logger.info('服务器内部错误,重新请求')
return baiduApi(url, retry+1)
# 其他不明原因
else:
logger.info('=' * 30 + '其他原因错误' + '=' * 30)
logger.info(url_ak)
logger.info(res)
logger.info('=' * 65)
# raise Exception('返回结果出错')
return None
return res
sessionAsync = None
async def baiduApiAsync(url, retry=0):
'''
添加AK,请求百度API,并处理异常
一般来说有三种错误
1、请求是网络原因造成连接失败,自动重新请求
2、解析json出现异常,重新请求没用,抛出异常
3、解析json后,AK访问限制,更换AK并重新请求
'''
# 超过重试次数
if retry >= 5:
logger.info('请求次数超过5次,放弃请求')
logger.info(url)
return None
# 添加AK的url
ak = getAK()
url_ak = url + f'&ak={ak}'
# 网络连接
try:
# async with aiohttp.ClientSession() as session:
# async with session.get(url_ak) as resp:
# text = await resp.text()
async with sessionAsync.get(url_ak) as resp:
text = await resp.text()
except Exception as e:
logger.exception(e)
logger.info(f'请求API接口时发生异常,重试:{retry+1}次')
return await baiduApiAsync(url, retry+1)
# json解析
try:
res = json.loads(text)
except Exception as e:
logger.info('=' * 30 + 'json文本解析出现异常' + '=' * 30)
logger.info(url)
logger.info(res.text)
logger.info(e)
logger.info('=' * 65)
# raise Exception()
return None
# AK、请求状态
if res['status'] != 0:
'''
错误代码:
http://lbsyun.baidu.com/index.php?title=webapi/guide/webservice-geocoding-abroad
201:APP被用户自己禁用
210:APP IP校验失败
240:APP服务被禁用
302:天配额
401:并发
'''
# 如果是并发限制,重新请求
if res['status'] == 401:
return await baiduApiAsync(url)
# 如果是AK限制,更换AK
elif res['status'] == 4 or res['status'] == 302 \
or res['status'] == 240 or res['status'] == 201 \
or res['status'] == 401 or res['status'] == 210:
getAK(True, ak)
return await baiduApiAsync(url)
# 服务器内部错误,重新请求
elif res['status'] == 1:
logger.info('服务器内部错误,重新请求')
return await baiduApiAsync(url, retry+1)
# 其他不明原因
else:
logger.info('=' * 30 + '其他原因错误' + '=' * 30)
logger.info(url_ak)
logger.info(res)
logger.info('=' * 65)
# raise Exception('返回结果出错')
return None
return res
def fetchApi(ori_loc, dest_loc):
url = (
f'http://api.map.baidu.com/routematrix/v2/walking?output=json&'
f'origins={",".join([format(loc,".5f") for loc in reversed(ori_loc)])}&'
f'destinations={",".join([format(loc,".5f") for loc in reversed(dest_loc)])}'
)
return baiduApi(url)
def reverseGeocode(ori_loc):
url = ('http://api.map.baidu.com/reverse_geocoding/v3/?output=json&'
f'coordtype=wgs84ll&'
f'location={",".join([format(loc,".5f") for loc in reversed(ori_loc)])}')
return baiduApi(url)
def geocode(geo_name, city):
url = (
f'http://api.map.baidu.com/geocoding/v3/?address={quote(geo_name)}'
f'&city={city}&output=json'
)
return baiduApi(url)
async def geocodeAsync(geo_name, city):
url = (
f'http://api.map.baidu.com/geocoding/v3/?address={quote(geo_name)}'
f'&city={city}&output=json'
)
return await baiduApiAsync(url)
async def initSessionAsync():
global sessionAsync
sessionAsync = aiohttp.ClientSession()
# 模块引入时,手动关闭
async def closeSessionAsync():
await sessionAsync.close()
if __name__ == "__main__":
# 模块被加载时,初始化Session
asyncio.run(initSessionAsync())
# asyncio.run(geocodeAsync('武汉大学', '武汉'))
# 主程序运行时,主动关闭
asyncio.run(closeSessionAsync())
pass
else:
akInfo(detail=True)