[简易爬虫]Requests+BeautifulSoup爬取音乐网站所有曲目,多线程实现歌曲本地保存
  • 分类:Python
  • 发表:2019-05-05
  • 围观(1,078)
  • 评论(12)

著名民谣(摇滚😖)歌手李志被官方以行为不端的理由封杀,作品遭到全网下架。虽然事情没有最终的结论,但是最近确实是没办法听到他的歌了。在网易云无聊的刷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()

代码很简单,都是鸡肋的实现,正则不熟悉,多线程也不是很熟练。学习总是一步一步积累,我不幻想一口吃成大胖子,我只希望我能每天进步一点点。相信生活会变得越来越美好。


没有人在热河路谈恋爱,总有人在天黑时伤感
如果年轻时你来过热河路,那你现在是不是已经被他们淹没
没有新的衣服能让你爱恋,总有一种天气让我怀念
醒来或者吃饱又是一年,相遇然后分别就在一天

此处原本因该有音乐,但是我不知道怎么插入,就先放着吧,以后补上,或者永远没有了...

共有 12 条评论

  1. 万一

    qq通过下丫 老兄

  2. Weiney

    Continuity

    没有收到验证消息啊

  3. b粉

    老哥网盘哪里下载

    1. Weiney

      Weiney

      之前网站底部有相应的百度盘地址,不过网站好像挂了哦,一会我上传一份你注意留意邮箱哦

  4. b粉

    老哥回复好快。之前网站的压缩文件我下载下来了,但是没解压。后来网站不能用了,我特么发现压缩包有密码。如果老哥你那边有的话发我一份就美滋滋了,祝老哥周末快乐

    1. Weiney

      Weiney

      为什么回复的快是因为你是这边为数不多的读者,我正打包呢,不要急啊

  5. b粉

    技术宅的小乐趣,哈哈。不着急,谢谢老哥啦。本来准备暴力破解的

    1. Weiney

      Weiney

      百度云的链接分享就挂了,不行你加我百度云好友我推给你,账号:Weiney_

  6. b粉

    好滴,我加你

  7. b粉

    不行,需要互相好友才行。我的百度Chenhzbest

  8. 这个世界不会好了,我需要李逼。谢谢

    1. Weiney

      Weiney

      我对此保持乐观态度,人没事就好了

Top