Python requests.get()与读取json保姆级入门——以musicbrainz API为例

编程小白(就是我)在接触Python爬虫或数据分析时,第一个碰到的可能就是requests函数了。网上已经有很多大牛总结过使用方法(例如:Requests库详解1])。

其中,最常用的方法是requests.get(),参数为:
request.get(url,params=None,**kwargs)

参数含义:

  • url:获取html的网页的url
  • params:url中的额外的参数,字典或字节流格式,可选
  • **kwargs:12个控制访问的参数

在这些参数中,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数据库简介

下面以音乐开放数据库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查询示范

假设我们需要设立一个查询音乐艺人的函数并返回JSON格式,需要使用:

搜索:/ ?query = &limit = &offset = 

该URL的"“部分就是"artist”,部分填入需要查找的艺人,查询参数可以作为字典提供给request.get函数:

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文件是字典,套着列表,再套字典。回忆字典与列表的相关知识,例如:

  • Python 列表(List) | 菜鸟教程
  • Python 字典(Dictionary) | 菜鸟教程

字典是{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辅助查看。


  1. https://www.jianshu.com/p/ada99b7880a6 ↩︎

  2. https://requests.readthedocs.io/en/master/user/quickstart/ ↩︎

你可能感兴趣的:(数据分析,学习笔记)