2020-03-08 | Python | 6 min read

此文章发表于 678 天前,请注意文章时效

之前在没有学习使用python之前看到有人使用爬虫爬取了很多民谣,分析了歌词的组成,非常有意思。最近想着很久没有给妹妹惊喜了,是不是应该做点什么。刚好也可以练习一下python的一些库的使用,省得长时间不用就给忘记怎么用了(实事证明,我还真的是忘记了怎么用……)。

目标

获取有关爱情的歌曲,分析歌词,分词清洗之后,绘制成词云。

具体实现

  1. 找到爱情歌曲的合集,分析网页代码,看看如何爬取
  2. 写好爬虫,爬取歌词
  3. 歌词分词,清洗
  4. 绘制词云

歌曲合集

获取歌曲id

这个步骤说起来很简单,但是在具体做的时候,却着实让我楞了一下。咋形容爱情歌曲呢……想了大半天,还好想起不少音乐平台都有歌单的功能,去歌单里找一找,果然发现好多。随便选了一个歌曲多的歌单 有500多首,数量也足够了。

然后就是分析网站,这种其实想都不用想,肯定是动态生成的,直接使用浏览器inspect功能,果然发现一个名字叫做playlist?id=2069951086请求里,包含了所有的歌单信息。

根据这个请求的表头,就可以构造出爬虫request所需要的参数:

def get_song_id(urls):
    url = urls
    headers = {#为了防止被反爬虫,按照表头也增加了cookie信息。
        'accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9',
        'accept-encoding': 'gzip, deflate, br',
        'accept-language': 'en-GB,en;q=0.9,zh;q=0.8,zh-CN;q=0.7,zh-TW;q=0.6,ja;q=0.5',
        'cookie': '_iuqxldmzr_=32; _ntes_nnid=17a6d9410df165186c84022c752b4438,1577479004809; _ntes_nuid=17a6d9410df165186c84022c752b4438; WM_TID=iXoDW5wstqNFRBUBBUIovnXCRx8pJpKg; vinfo_n_f_l_n3=207e84c7da36a25f.1.0.1578577509635.0.1578577603314; hb_MA-BFF5-63705950A31C_source=www.google.com; JSESSIONID-WYYY=HBcDngRY%2BYlmce3%2FnICVq2sHUqzhf6lNpzFC0pMEa8STPmfdWsJB%2FmUGXN%5CyD9xf2ksArknY0xy%5Cq7vKCIjCYy4y428%2FS%5CXSCyN4INi9RIhFxkI9fnkY6HfKfC82S8sF0BACUqI43FVDIOKEUY6wuASapZa0nC2xp53yHigPkgzyyzWZ%3A1583164791677; WM_NI=svQbgFtZLExadgOi7pKFKoM8NRxTtAwKny3aGq1ZVk8eGEgsy4Ug2kMra3ZJxjrlSwIUOv%2FyMReiNDcvfe9w2ZWsqXIwg9DBpfexIU5eZKurO34kKQsPP1EUxRKqpoUYOUY%3D; WM_NIKE=9ca17ae2e6ffcda170e2e6eed0ed42f3b69c86ec34a8928fb6d55e938b9faff55b8b95aadaf57d9bb0a799e12af0fea7c3b92ab1aa9f83e64eb88684a6fb6ba8b9818ab67c899a8590d0348e8eb8b5f74bb595a29ab26b85afffbbd561fcbb89a6e679bbec9b88b1618faeafd4d942bcb6be8dd27d8b8b8197ae738f8eab8acd50a1aaaa86bc7df2869994f4548ba9a9dac74ab4baab99cc5ff7bb96acf87a8dedfc8cca5bb190a0d2c85fbc8e8592f365978781b6dc37e2a3',
        'referer': 'https://music.163.com/',
        'sec-fetch-dest': 'iframe',
        'sec-fetch-mode': 'navigate',
        'sec-fetch-site': 'same-origin',
        'upgrade-insecure-requests': '1',
        'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.122 Safari/537.36'}
    webcontent = requests.get(url, headers=headers)
    webtext = webcontent.content

我们分析一下返回的html内容就可以发现,所有的歌曲名与链接都放在了<li>标签下,具体是这样的<li><a href="/song?id=1381755293">山楂树之恋</a></li> 所以下一步就是提取出歌曲链接,然后从链接中得到歌曲的id

    html = etree.HTML(webtext) #使用xpath解析之后,需要将文件内容转换为html对象
    song_lyrc_url = html.xpath('//div[@id="song-list-pre-cache"]/ul/li/a/@href')#得到所有歌曲的链接
    id_pattan = re.compile(r'[0-9]+')
    song_id = []
    for idurl in song_lyrc_url:
        id_result = id_pattan.findall(idurl)
        song_id.extend(id_result)#使用正则表达式,得到歌曲id
    return song_id

获取歌词

上面一步中我们得到了歌曲的id,然后如何获得歌词呢?
随便打开一首歌的链接,分析网页发现了这样一个请求lyric?csrf_token= 里面打开一个,果然是歌词(废话嘛,都写了lyric了),然后分析这个请求的表头信息,发现是post请求,这个post请求的form data是样的:

这是啥……解析不能呀!(事后证实这是token验证信息)既然这样不能解析,哪可怎么获得歌词。谷歌一番之后,发现有一个链接http://music.163.com/api/song/lyric?' + 'id=' + str(id) + '&lv=1&kv=1&tv=-1 果断使用这个链接获得歌词。

def get_song_lyrc(id):
    headers = {
        'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9',
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.122 Safari/537.36',
        'Cookie': 'vjuids=633b23b2.1640e217940.0.c9d5e94c2b52d; vjlast=1529245432.1533478804.23; _iuqxldmzr_=32; _ntes_nnid=74ffb4f3efc5c648c5f4675d36a88bca,1575827003556; _ntes_nuid=74ffb4f3efc5c648c5f4675d36a88bca; WM_TID=lPwkACGiBMJFFAQFEAI4qals15qis3TX; WM_NI=HoIzRrrkX84mT7QgRTznPWQ43qeLC%2BPah4TYpoW71IGRLB7iPzcBAyLL6l0xtHE9NueJ0l55V59w2%2B%2BQJ78brun4MLTZMHWTC0Yzj8gc0uBheOX13INcucQaoRU6dvvuOFI%3D; WM_NIKE=9ca17ae2e6ffcda170e2e6eeb8db3f938685d1d04986b48bb6c44e878b8a85b55e9a91fdb7d47c8ee9ae92d12af0fea7c3b92a8cb1879bd17ab2b598d9b76798e99f88d160bab2a0b8d440f1afabadc22194bfa8d5d753b4b199d8b443858e84a3b66394a6ada3c4739094fa8db374b8988bdacd7fa796008db83f96ebf798b121e98f86d8bb6ab2aba2d5d048988683a9d66081f0979be934a5bd0096c164a79198d2b2438ebdbb84d46bad8e9bd7e77eb6ab81a7cc37e2a3; JSESSIONID-WYYY=VX%2FEiMGwpXTbV9DST718KECK%2BqiQ%2Fo%5CuhvkS%2B9r6ZSOZhiiu9b3i3RECpORir4a9%2BFsxTmhwBOq%2BXHgRPmlS1gTei6VD%2F1fMK0%2FECCsl%5CYrd8usX%2Fc%2FTg5sebrbxDRHmyyHxZK8R5Dpbo1KlRsrnff%2F0TmeY%5C2yb9ycMF2Gh%2F%2Faeqdbp%3A1583190953365',
        'Connection': 'keep-alive',
    }
    url = 'http://music.163.com/api/song/lyric?' + 'id=' + str(id) + '&lv=1&kv=1&tv=-1'
    content = requests.get(url, headers=headers).content #返回的结果就直接是一个json格式的文件
    j = json.loads(content)
    try:
        lyrc = j["lrc"]["lyric"]
        lyrc_pattan = re.compile(r'[A-Za-z0-9!%\[\],:\.]')
        lyrc_text = lyrc_pattan.sub("", lyrc)#既然都是中文歌曲,直接使用正则表达式,排除所有的字母,数字,符号之类的,得到中文。
    except KeyError: #这里防止有歌曲没有歌
        lyrc_text = ''
    return lyrc_text

歌词存储

得到歌词之后,为了方便后面对歌词进行分析,先将歌词存储下来

def write_lyrc_file(filename, path, list):
    file_path = os.path.join(path, filename)
    y = ''
    for ids in list:
        text = get_song_lyrc(ids)
        y = y + text
        d = list.index(ids)
        if d % 10 == 0:
            print("已经写入%d个文件" % d) #增加输出显示,让自己知道大概程序进行到什么地步了
    with codecs.open(file_path, 'a+', encoding='utf-8') as f:
        f.write(y)

至此,我们就得到歌词,并且存储下来了,后面需要做就是对歌词进行清洗,然后分析,绘制就可以。

总结

使用到的库

  • Requests
  • re
  • lxml
  • json
  • codecs #使用这个库是为了解决文件读取,存储时编码问题的

本文链接:https://willisfusu.github.io/post/python-wordcloud/

此文章由李二先生采用知识共享署名-相同方式共享 4.0 国际许可协议进行许可,转载请注明出处。

🎉🎉🎉 我开通了Newsletter,欢迎订阅! 🎉🎉🎉