著名民谣(摇滚😖)歌手李志被官方以行为不端的理由封杀,作品遭到全网下架。虽然事情没有最终的结论,但是最近确实是没办法听到他的歌了。在网易云无聊的刷live视频,被逼哥震撼的现场所打动,从此被他真实的歌声吸引。作为程序员,典型的pushover,我对这件事并没有太多自己的想法。今天我们仅从技术的角度出发,实现一个对音乐网站的音乐资源的爬取。本爬虫完全是出于学习目的,因为完整资源作者已经打包到百度云,真正想下载的只需要通过百度云下载即可。
实现效果
通过本爬虫,我们可以获取页面的所有媒体资源的URL,并通过多线程方式,将数据保存在本机目录。具体实现思路:requests获取html源码,这里要说明一下,这个页面的数据是通过JS渲染进去的,所以我们本来需要通过selenium通过浏览器来获取网页的html。但是通过分析返回的html网页发现,插入的条目数据都在html的script标签内,用一个变量存储,格式为Json格式。所以这里可以方便的不需要用selenium来获取网页,只需要简单的通过正则取出相应的数据构造资源路径,再通过多线程将数据保存即可。正如我们所说的,所有的活动都是出于学习目的的,因此可能会在后期更新一篇通过selenium方式爬取的文章。
代码分析
运行支持库: requests、BeautifulSoup、threading、Python3.6.5 爬取网站:http://yoerking.com/static/music_player.html
Ⅰ、分析HTML
网页本身不复杂,requests取出网页代码之后通过BeautifulSoup得到script标签内容。我们要取得数据在JS里以Json格式存储,理论上我们通过正则表达式取出这个Json,通过Python自带的Json库load一下就可以在代码内解析出我们想要的数据。但是由于网页的排版问题,读取这段Json文本需要我们进行简单的文本处理,否则会报错。
def get_list(): req = requests.get(url=URL) req.encoding = "utf8" soup = BeautifulSoup(req.text, "html.parser") scripts = soup.find_all("script", {"src": False}) p = re.compile("(?<=allmuisc = \[)([\s\S]*?)(?=\];)") m = p.search(scripts[1].text).group().replace("},{", "}, {") m = m.replace("\n", "").split("}, {") for x in m: x = "{" + x.replace("{", "").replace("}", "") + "}" songs_list = json.loads(x) get_url(songs_list)
函数返回dict格式内容,包含专辑名和歌曲信息。每一个dict就是一张专辑,后续函数通过传入的dict构造资源下载url。Python代码必须围绕去到的HTML编写,之前想当然的写代码造成了一大堆清醒怪状的错误。在此告诫自己切勿想当然的脱离实际。
Ⅱ.资源下载
由于我们的音频文件体积都不是很大,可以通过requests获取保存到硬盘上。下面提供的下载方法只适用于小文件,大文件由于电脑内存限制,需要另作处理。
req = requests.get(self.music_url) with open(self.file_url, "wb") as f: f.write(req.content) print("下载成功:" + self.file_url)
完整代码
import json import os import re import threading import requests from bs4 import BeautifulSoup import time if not os.path.exists("music"): os.mkdir("music") PATH = os.getcwd() + "\\music\\" URL = "http://yoerking.com/static/music_player.html" MUSIC_BASE_URL = "http://yoerking.com/static/music/" def get_list(): req = requests.get(url=URL) req.encoding = "utf8" soup = BeautifulSoup(req.text, "html.parser") scripts = soup.find_all("script", {"src": False}) p = re.compile("(?<=allmuisc = \[)([\s\S]*?)(?=\];)") m = p.search(scripts[1].text).group().replace("},{", "}, {") m = m.replace("\n", "").split("}, {") for x in m: x = "{" + x.replace("{", "").replace("}", "") + "}" songs_list = json.loads(x) get_url(songs_list) def get_url(songs_list): album = songs_list["album"] file_path = PATH + album if not os.path.exists(file_path): os.mkdir(file_path) for song in songs_list["music"]: music_name = song.strip() music_url = MUSIC_BASE_URL + album + "/" + music_name file_url = file_path + "\\" + music_name while True: if threading.active_count() > 20: # 负载线程数 time.sleep(1) # 线程满负荷,等待 else: thread = DownloadThread(file_url, music_url) thread.start() break class DownloadThread(threading.Thread): def __init__(self, file_url, music_url): threading.Thread.__init__(self) self.file_url = file_url self.music_url = music_url def run(self) -> None: req = requests.get(self.music_url) with open(self.file_url, "wb") as f: f.write(req.content) print("下载成功:" + self.file_url) if __name__ == '__main__': get_list()
代码很简单,都是鸡肋的实现,正则不熟悉,多线程也不是很熟练。学习总是一步一步积累,我不幻想一口吃成大胖子,我只希望我能每天进步一点点。相信生活会变得越来越美好。
没有人在热河路谈恋爱,总有人在天黑时伤感
如果年轻时你来过热河路,那你现在是不是已经被他们淹没
没有新的衣服能让你爱恋,总有一种天气让我怀念
醒来或者吃饱又是一年,相遇然后分别就在一天
万一
qq通过下丫 老兄
Continuity
没有收到验证消息啊
b粉
老哥网盘哪里下载
Weiney
之前网站底部有相应的百度盘地址,不过网站好像挂了哦,一会我上传一份你注意留意邮箱哦
b粉
老哥回复好快。之前网站的压缩文件我下载下来了,但是没解压。后来网站不能用了,我特么发现压缩包有密码。如果老哥你那边有的话发我一份就美滋滋了,祝老哥周末快乐
Weiney
为什么回复的快是因为你是这边为数不多的读者,我正打包呢,不要急啊
b粉
技术宅的小乐趣,哈哈。不着急,谢谢老哥啦。本来准备暴力破解的
Weiney
百度云的链接分享就挂了,不行你加我百度云好友我推给你,账号:Weiney_
b粉
好滴,我加你
b粉
不行,需要互相好友才行。我的百度Chenhzbest
汤
这个世界不会好了,我需要李逼。谢谢
Weiney
我对此保持乐观态度,人没事就好了