练习小项目:音乐爬虫

学习爬虫也有些日子了(虽说还是个菜鸡),最近找工作也是各大网站投递,然后都石沉大海。实在闲来无事,想着再写写小项目练练手吧,Lets go<( ̄︶ ̄)↗[GO!]
自定需求

  • 门户网站:百度音乐
  • 使用技术:requests,xpath,re
  • 爬取内容:输入某歌手名,爬取下载该歌手所有歌曲

开始吧

  • 首先第一步,还是分析网站结构,个人觉得这是很难的,很掉头发的,不过也是爬虫的灵魂。直接在html中就能找到的就不说了,稍稍熟悉前端或学过几天requests的都能将他解析出来。第一个麻烦点的地方在,歌手歌曲详情页的翻页

点击几次后,发现和多数的网站不同,并不是在url中传递page的值,那明显就是动态加载的了,OK,打开检查,刷新下页面,翻页后发现,数据都在这个数据包中
练习小项目:音乐爬虫_第1张图片
并且是直接传入的html代码进行渲染,那么直接请求这个数据包,再拿出data中的html代码,就可以通过正则等方式解析出我们想要的数据了。代码中解析出每首歌的href和title即可。

  • 接着我们进入歌曲的播放页面,播放歌曲,并在检查的media选项中找到了歌曲的接口,是这样的:http://audio04.dmhmusic.com/71_53_T10038986645_128_4_1_0_sdk-cpm/cn/0208/M00/E5/61/ChR46119DqeAJGANAD3PrR3qZCk162.mp3?xcode=4455e866b557087b64c3a4a34bfcc1036e7e2ee
  • 看的一脸懵逼,这个路径的组成部分,我这种菜鸡看来是没法从中发现规律了。不过没事,XHR啊JS啊啥的都点开看看,常常都会有意外的收获。然后在无意中,发现了这样一个js文件
    练习小项目:音乐爬虫_第2张图片
    这其中的file_link正是想要请求的歌曲url,OK,分析结束。
  • 思路清晰后,那么接下来的敲代码就容易了,最后推荐一下跟我一样的爬虫初心者,尝试着使用下postman,因为它能非常方便的帮你分析出url中必要与可有可无的params,这样可以快速的,省去对很多没用的参数的困扰。比如该项目中,放下载链接的js的url也是很长:http://musicapi.taihe.com/v1/restserver/ting?method=baidu.ting.song.playAAC&format=jsonp&callback=jQuery172012729838958251238_1588855404448&songid=87967607&from=web&_=1588855423310
    很多奇怪的id参杂在其中,postman筛选后,会发现
    练习小项目:音乐爬虫_第3张图片
    这样4个参数就是足够的,而这4个参数的含义也很好理解。
  • 最后,附上源码,比较粗糙,不足之处还请各位补充
import requests
from fake_useragent import UserAgent
from lxml import etree
import re
import os

artist_url = 'http://music.taihe.com/artist'
song_url = 'http://music.taihe.com/data/user/getsongs'
json_url = 'http://musicapi.taihe.com/v1/restserver/ting'

headers = {
    'User-Agent': UserAgent().random
}

session = requests.session()


# 获得歌手列表
def get_artist():
    response = session.get(url=artist_url, headers=headers)
    response.encoding = response.apparent_encoding
    text = response.text
    tree = etree.HTML(text)
    dl_list = tree.xpath('//div[@class="hot-head clearfix"]/dl')
    artist_list = []
    for dl in dl_list:
        title = dl.xpath('./dd/a/@title')[0]
        href = dl.xpath('./dd/a/@href')[0].split('/')[-1]
        artist_list.append((title, href))
    li_list = tree.xpath('//ul[@class="container"]/li[1]/ul[@class="clearfix"]/li')
    for li in li_list:
        try:
            title = li.xpath('./a/@title')[0]
            href = li.xpath('./a/@href')[0].split('/')[-1]
            artist_list.append((title, href))
        except Exception as e:
            # print(e)
            continue
    return artist_list


# 获得歌曲列表
def get_song(artist: tuple, start=15):
    song_list = []
    while start != 0:
        params = {
            'start': start - 15,
            'size': 15,
            'ting_uid': artist[1],
        }
        response = session.get(url=song_url, headers=headers, params=params)
        response.encoding = response.apparent_encoding
        text = response.json()['data']['html']
        datas = re.findall(r', text)
        song_list += datas
        start -= 15
    return song_list


# 获得下载链接
def get_mp3(song: tuple):
    params = {
        'method': 'baidu.ting.song.playAAC',
        'format': 'jsonp',
        'songid': song[0],
        'from': 'web',
    }
    response = session.get(url=json_url, headers=headers, params=params)
    download_url = response.json()['bitrate']['file_link']
    return download_url, song[1]


# 存储
def save(download_url: str, artist: str, title: str):
    response = session.get(url=download_url, headers=headers)
    if not os.path.exists('./' + artist):
        os.mkdir('./' + artist)
    with open('./' + artist + '/' + title + '.mp3', 'wb') as file:
        print(f'{artist}/{title} \t 正在下载 ...')
        file.write(response.content)


def main():
    while True:
        artist_list = get_artist()
        index = 0
        for artist in artist_list:
            index += 1
            if index % 8 == 0:
                print(artist[0])
            else:
                print(artist[0] + '\t', end='')
        singer = input('输入歌手>>>')
        for artist in artist_list:
            if singer == artist[0]:
                count = input('输入下载数量>>>')
                song_list = get_song(artist, eval(count))
                for song in song_list:
                    download_url, title = get_mp3(song)
                    save(download_url, singer, title)
                print('下载完成'.center(100, '-'))
                break


if __name__ == '__main__':
    main()

有一起学习爬虫的小伙伴也可以加我微信,共同学习共同进步~我们要坚信,明天会更好,头发会更少!av62811906

你可能感兴趣的:(爬虫学习,python,http,经验分享,javascript,xpath)