url = 'https://music.163.com/discover/toplist'hd = { 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.111 Safari/537.36'}def get_topic_ids(): r = requests.get(url, headers=hd) html = etree.HTML(r.text) nodes = html.xpath("//ul[@class='f-cb']/li") logger.info('{} {}'.format('榜单 ID', '榜单名称')) ans = dict() for node in nodes: id = node.xpath('./@data-res-id')[0] name = node.xpath("./div/p[@class='name']/a/text()")[0] ans[id] = name logger.info('{} {}'.format(id, name)) return ans
歌曲分析上面我们获取到了所有的榜单数据,那么针对单个榜单来说,就是要获取其下的所有歌曲了。分析页面原属可知,歌曲列表是在一个 table 中的,但是通过 requests.get(url,headers=hd) 方式获取返回的网页文本内容的话,貌似是获取不到 table 元素的。于是我们将其返回值输出后做了仔细分析,发现歌曲是在 class="f-hide" 的 ul 标签中。与获取榜单类似,同样需要先获取所有的 li 标签,然后在逐个获取歌曲 id 和歌曲 name 就可以了。def get_topic_songs(topic_id, topic_name): params = { 'id': topic_id } r = requests.get(url, params=params, headers=hd) html = etree.HTML(r.text) nodes = html.xpath("//ul[@class='f-hide']/li") ans = dict() logger.info('{} 榜单 {} 共有歌曲 {} 首 {}'.format('' 10, topic_name, len(nodes), '' 10)) for node in nodes: id = node.xpath('./a/@href')[0].split('=')[1] name = node.xpath('./a/text()')[0] ans[id] = name logger.info('{} {}'.format(id, name)) return ans
同样该函数返回一个字典,key 为歌曲 id,value 为歌曲名称。下载歌曲我们还需要一个下载歌曲的函数,该函数接收歌曲 id,然后以文件流的形式直接读取到本地。def down_song_by_song_id_name(id, name): if not os.path.exists(download_dir): os.mkdir(download_dir) url = 'http://music.163.com/song/media/outer/url?id={}.mp3' r = requests.get(url.format(id), headers=hd) is_fail = False try: with open(download_dir + name + '.mp3', 'wb') as f: f.write(r.content) except: is_fail = True logger.info("%s 下载出错" % name) if (not is_fail): logger.info("%s 下载完成" % name)
最后将所有的操作组合到 main 函数中,作为程序的入口函数。def main(): ids = get_topic_ids() while True: print('') logger.info('输入 Q 退出程序') logger.info('输入 A 下载全部榜单歌曲') logger.info('输入榜单 Id 下载当前榜单歌曲') id = input('请输入:') if str(id) == 'Q': break elif str(id) == 'A': for id in ids: down_song_by_topic_id(id, ids[id]) else: print('') ans = get_topic_songs(id, ids[id]) print('') logger.info('输入 Q 退出程序') logger.info('输入 A 下载全部歌曲') logger.info('输入歌曲 Id 下载当前歌曲') id = input('请输入:') if str(id) == 'Q': break elif id == 'A': down_song_by_topic_id(id, ans[id]) else: down_song_by_song_id_name(id, ans[id])if __name__ == "__main__": main()
总结今天我们以网易云网页版为数据源来下载音乐文件,其中下载操作是最简单的,比较麻烦的是分析榜单 id 和获取榜单下的歌曲列表,但榜单下的歌曲列表其实远不止 10 条,而我们获取歌曲的函数 get_topic_songs 每次只可以获取 10 条歌曲,这是因为我们没有在 headers 添加 cookie 导致的,因为只有登录之后才会显示所有的歌曲。小伙伴们可以登录自己的账户然后添加 cookie 做下尝试。(图片来源网络,侵删)
0 评论