近日在Python 3.6中尝试了一下百度地图API,遇到一些问题,已经解决,在此记录,希望能给同惑者带来帮助。
先说问题:
1,尝试百度地图API的sn计算示例,遇到错误——{'status': 240, 'message': 'APP 服务被禁用'}
2,解决上述问题后,遇到新错误——{'status': 211, 'message': 'APP SN校验失败'}
上代码,这是地理编码的请求,即通过传递参数“百度大厦”获取对应的经纬度,默认百度坐标系。
# -*- coding: utf-8 -*-
# 第一行必须有,否则报中文字符非ascii码错误
import urllib
import hashlib
# 以get请求为例http://api.map.baidu.com/geocoder/v2/?address=百度大厦&output=json&ak=yourak
queryStr = '/geocoder/v2/?address=百度大厦&output=json&ak=yourak'
# 对queryStr进行转码,safe内的保留字符不转换
encodedStr = urllib.quote(queryStr, safe="/:=&?#+!$,;'@()*[]")
# 在最后直接追加上yoursk
rawStr = encodedStr + 'yoursk'
# md5计算出的sn值7de5a22212ffaa9e326444c75a58f9a0
# 最终合法请求url是http://api.map.baidu.com/geocoder/v2/?address=百度大厦&output=json&ak=yourak&sn=7de5a22212ffaa9e326444c75a58f9a0
print hashlib.md5(urllib.quote_plus(rawStr)).hexdigest()
解决方案:
问题分析:
首先检查了一下控制台,发现所有可用服务都开启了。
没看出哪里有问题,后来偶然看到别的地理编码的请求方式和这个例子有点区别。区别如下:
http://api.map.baidu.com/geocoding/v3/?address=北京市海淀区上地十街10号&output=json&ak=您的ak&callback=showLocation
这里“/geocoding/v3”和上面示例代码的[queryStr = '/geocoder/v2/?address=百度大厦&output=json&ak=yourak']开头部分"/geocoder/v2/"不一样。替换之后没有在报错——{'status': 240, 'message': 'APP 服务被禁用'}。这里应该是API自身的服务代码做了修改,即废除了“/geocoder/v2/”替换为"/geocoding/v3"。
再说说第二个问题,sn校验失败就是说url请求传送到服务器后计算出的sn和我在本地计算的sn不一样。先看本地代码。
# _*_ encoding='utf-8' _*_
from urllib.request import urlopen, quote
from urllib import parse
from bs4 import BeautifulSoup
import requests
import hashlib
import json
if __name__=="__main__":
home_url=r'http://api.map.baidu.com'
# ak 和 sk 用数字0和6替换了,非真实
my_ak='RVh62gHLF000000000000GyrG2uf34Gb'
my_sk='0YWDV66666666666666rSwkjvy1YYo3D'
# 以get请求为例http://api.map.baidu.com/geocoder/v2/?address=百度大厦&output=json&ak=yourak
queryStr = r'/geocoding/v3/?address=百度大厦&output=json&ak=RVh62gHLF000000000000GyrG2uf34Gb'
# 对queryStr进行转码,safe内的保留字符不转换
encodedStr = quote(queryStr, safe="/:=&?#+!$,;'@()*[]")
print(encodedStr)
# 在最后直接追加上yoursk
rawStr = encodedStr + my_sk
print(rawStr.encode('utf-8'))
print(quote(rawStr).encode('utf-8'))
#下面这行注释过的代码会报错,因为hashlib.md5('这里是瞒足某种编码的二进制码')
#print(hashlib.md5(parse.quote(rawStr)).hexdigest())
md = hashlib.md5() #获取一个md5加密算法对象
md.update(quote(rawStr).encode('utf-8')) #制定需要加密的字符串
sn=md.hexdigest()
两次计算的sn不同,有两种可能的原因:
1,本地hashlib.md5()的输入与服务器端hashlib.md5()的输入不同
2,Python 2.x和3.x 版本下hashlib.md5()的计算方式不同
验证阶段:
分别向两个版本下的hashlib.md5()输入了同样的数据,所得结果一样。看来不是第一种可能。
实际上Python 3.x中urllib下没有直接的quote(),即无法使用urllib.quote()。quote('特殊字符,比如中文')可将特殊字符进行url编码,
以防传输到服务器端变成乱码。这个编码功能到了urllib的两个子模块中urllib.request和urllib.parse里面,功能一样。经过测试发现
Python 2.x中quote()会将一个中文编码为两个十六进制码,而Python 3.x中会将一个中文编码为三个十六进制码。
#Python 2.x
import urllib
print quote('百度大厦')
%B0%D9%B6%C8%B4%F3%CF%C3 #长度8
#Python 3.x
from urllib.parse import quote
print(quote('百度大厦'))
%E7%99%BE%E5%BA%A6%E5%A4%A7%E5%8E%A6 #长度12
正是这个编码的区别使得我们在本地计算sn时url的中文转码和服务器端计算sn时的中文转码不同,因而得到不同的sn。
解决方法:
用Python 2.x的quote()转码结果替换3.x的转码结果,或者直接在Python 2.x中计算sn,再拿到3.x的程序中用。毕竟,
我们不能改变服务器端的计算,只好去适应它。多说一句,今天安装Python 2.7.12时看到提示,大意是2020年起,Python 2.x
版本的服务将逐步停止,也许百度地图API的服务器端很快就要用3.x的计算结果了。到时候,我们这些API调用者就可以直接使用本地
计算的sn了。
最终url【http://api.map.baidu.com/geocoding/v3/?address=%B0%D9%B6%C8%B4%F3%CF%C3&output=json&ak=RVh62gHLFZjX345Yx7MlQGyrG2uf34Gb&sn=08ae4326e1758ba537b24516815ee6fc】,将这个链接在浏览器中打开可看到:
{"status":0,"result":{"location":{"lng":116.30695597357377,"lat":40.05738753357608},"precise":1,"confidence":80,"comprehension":100,"level":"商务大厦"}}
到此,已完成本文的工作!