编程小白(就是我)在接触Python爬虫或数据分析时,第一个碰到的可能就是requests函数了。网上已经有很多大牛总结过使用方法(例如:Requests库详解1])。
其中,最常用的方法是requests.get(),参数为:
request.get(url,params=None,**kwargs)
参数含义:
在这些参数中,params表示在URL的查询字符串中发送某种数据,为只有字符串的字典,例如:
2
In [1]: params = {'key1': 'value1', 'key2': ['value2', 'value3']}
r = requests.get('https://httpbin.org/get', params=params)
print(r.url)
Out[1]: https://httpbin.org/get?key1=value1&key2=value2&key2=value3
requests.get()得到的是一个状态代码:
In [2]: requests.get("http://www.baidu.com")
Out[2]: <Response [200]>
其中[200]是请求状态代码(status_code),200代表状态正常,可以通过requests.codes.ok判断状态是否正常,用raise_for_status()输出状态代码,例如我不小心把".com"打成".comm"。
In [3]: r = requests.get("http://www.baidu.comm/")
if r.status_code == requests.codes.ok:
print (r.url)
else:
print (r.raise_for_status())
Out[3]: 500 Server Error: Internal Privoxy Error for url: http://www.baidu.comm/
下面以音乐开放数据库musicbrainz为例:
在官网上可以看到,该API的URL是: https://musicbrainz.org/ws/2/ 。
有13种实体类型
area, artist, event, genre, instrument, label, place, recording, release, release-group, series, work, url
有3种功能:
- 查找:/ <ENTITY_TYPE> / <MBID>?inc = <INC>
- 浏览:/ <RESULT_ENTITY_TYPE>?<BROWSING_ENTITY_TYPE> = <MBID>&limit = <LIMIT>&offset = <OFFSET>&inc = <INC>
- 搜索:/ <ENTITY_TYPE>?query = <QUERY>&limit = <LIMIT>&offset = <OFFSET>
还可以使用查询功能查到上面的资源,其中部分为:
子类资源 | 参数 (parameter) |
---|---|
/ws/2/artist | recordings, releases, release-groups, works |
/ws/2/collection | user-collections (includes private collections, |
/ws/2/recording | artists, releases, isrcs, url-rels |
/ws/2/release | artists, collections, labels, recordings, relea |
音乐资源 | 参数 (parameter) |
---|---|
包括音乐艺人,标签,区域或作品别名 | aliases |
包含注释 | annotation |
包括标签和/或等级 | tags, ratings |
API还提供XML或JSON输出格式,可以添加fmt=json到查询字符串。
假设我们需要设立一个查询音乐艺人的函数并返回JSON格式,需要使用:
搜索:/ ?query = &limit = &offset =
该URL的"
import requests
import json
query_type = { "simple": {},
"atr": {"inc": "aliases+tags+ratings"},
"aliases": {"inc": "aliases"},
"releases": {"inc": "releases"}}
def query_by_name(name, type="simple"):
BASE_URL = "http://musicbrainz.org/ws/2/"
ARTIST_URL = BASE_URL + "artist/"
params = query_type[type]
params["fmt"] = "json"
params["query"] = "artist:" + name
r = requests.get(ARTIST_URL, params=params)
return r.json()
假设我们需要查询,周杰伦的公开发行信息(release):
In [1]: query_by_name("周杰伦","releases")
Out[1]:
{'created': '2020-07-31T04:35:37.895Z',
'count': 278,
'offset': 0,
'artists': [{'id': 'a223958d-5c56-4b2c-a30a-87e357bc121b',
'type': 'Person',
......
(以下省略)
......
通过requests获取一个json文件,之后就是获取需要的信息了。
例如在找到的musicbrainz信息中找到名字相同的歌手数量。
在开始前,我们需要了解整个json文件的结构。为了方便阅读,可以引入"pprint"模块使json打印出来更好看:
In [1]: form pprint import pprint
In [2]: pprint(query_by_name("周杰伦","releases"))
Out[2]: {'artists': [{'aliases': [{'begin-date': None,
'end-date': None,
'locale': 'zh',
'name': '周杰伦',
'primary': True,
'sort-name': '周杰伦',
......(以下省略)
可以看出,获取的json文件是字典,套着列表,再套字典。回忆字典与列表的相关知识,例如:
字典是{key:value}的模式的,可以通过dict[key]来查到对应的value,例如我们想查询周杰伦的其中一个别名,可以通过’aliases’(别名)字典里的’name’这个key查到。
但这个字典是属于key为’aliases’的字典映射的value列表中的第一个数据项;而’aliases’的字典是又以一个key为’artists’的字典映射的value列表中的第一个数据项……
如果我们设这个json文件为dict_jay,要获取如第一个列表{‘name’: ‘周杰伦’}的key,就需要:
In [3]: dict_jay = query_by_name("周杰伦","releases")
In [4]: print(dict_jay['artists'][0]['aliases'][0]['name'])
Out[4]: 周杰伦
如果我们希望获取所有搜到的音乐艺人姓名,可以用for语句遍历每一个列表数据的’name’:
In [6]: for j in dict_jay['artists']:
print (j['name'])
Out[6]:
周杰倫
周杰
周煒傑
周俊傑
王傑
許冠傑
……
……
可以看到,搜索结果与我们输入的内容并非是完全匹配的,有很多名字相近的艺人也出现在’artists’字典中。而且搜索结果有繁体也有简体,这使得我们在查找时有点麻烦。
我们可以引入一个繁简体转换模块’zhconv’:
In [7]: from zhconv import convert
In [8]: names_list = []
dict_jay = query_by_name("周杰伦","releases")
In [9]: for j in dict_jay['artists']:
names_list.append(convert(j['name'], 'zh-hans'))
In [10]: print(names_list.count("周杰伦"))
Out[10]: 1
用for循环可以找到列表每个元素都有的信息,但如果有些信息不是每个元素都有,例如’aliases’(别名)字典就不是艺人都有,如果运行:
In [5]: dict_jay = query_by_name("周杰伦","releases")
for j in dict_jay['artists']:
for a in j['aliases']:
print(a['name'])
Out[5]: 周杰伦
Jay Chuo
Jay Chou 周杰倫
(以下省略)
……
Traceback (most recent call last):
File "json.py", line 3, in <module>
for a in j['aliases']:
KeyError: 'aliases'
就出现了 “KeyError: ‘aliases’”,表示不是每一个’artist’字典对应列表元素都有’aliases’这个key。可以使用ditc.get()方法:
In [11]:dict_jay = query_by_name("周杰伦","releases")
aliases_list=[]
for j in dict_jay['artists']:
if j.get('aliases') == None:
aliases_list.append('None')
else:
a_list = []
for a in j['aliases']:
a_list.append(a['name'])
aliases_list.append(a_list)
In [12]:pprint(aliases_list)
Out[11]: [['周杰伦',
'Jay Chuo',
'Jay Chou 周杰倫',
'周傑倫',……],
'None',
'None',
'None',
'None',
['Dave Wong', 'Wang Jie', 'Dave Wang', '王杰'],
……
……]]
由此可见,成功获取json文件元素的关键,在于对文件本身结构的了解,需要我们进入到文件里细细查看。
而直接“生”看文件可能会有难度,可以借助pprint辅助查看。
https://www.jianshu.com/p/ada99b7880a6 ↩︎
https://requests.readthedocs.io/en/master/user/quickstart/ ↩︎