爬蟲實戰(一):爬酷我音樂


前言

此方法僅供學習爬蟲,切勿用在其他途徑

要使用的庫

模塊

import requests, os, time, loguru
import pandas as pd
from concurrent.futures import ThreadPoolExecutor

分析

搜索分析

首先,我們搜索晴天這首歌

通過瀏覽器的抓包工具可得到,url = "http://kuwo.cn/api/www/search/searchMusicBykeyWord?key=%E6%99%B4%E5%A4%A9&pn=1&rn=30&httpsStatus=1&reqId=162909a0-78d8-11ec-b238-fdbe74073c65"

其中,“%E6%99%B4%E5%A4%A9”這為晴天的url編碼

那我們進一步分析,那個key攜帶的值,是不是我們要搜索的值呢?

;pn,按照常量,我們猜測,其為頁數;rn,我們猜測其為一頁顯示的數量,注意rid可以直接忽略,其為沒有用處的隨機值

那么如果我們,吧那個key的值換成一個歌手,再修改pn的值,我們是不是可以下載那名歌手的全部的歌曲呢?

打開其響應,可以得到:id = MUSIC_80456317

主頁分析

然后,我們進入音樂主頁

通過抓包工具。可以發現

分析其響應可得

然后我們訪問那個地址,可以發現,那就是我們的歌曲存放的地址

通過主頁分析,我們可以的到存儲音樂url的接口,以及得到這個接口的方式

musicrid = "MUSIC_80456317".split("_")[-1]  # _后面的數字即為url_api的mid
url_api = f"http://kuwo.cn/api/v1/www/music/playUrl?mid={musicrid}&type=convert_url&httpsStatus=1"

# 同時,可以得到musicrid的鏈接為
name = input("請輸入歌曲名稱/歌手名字:")
page = input("請輸入要下載的頁數:")

url_get_mid = f"http://kuwo.cn/api/www/search/searchMusicBykeyWord?key={name}&pn={page}&rn=30&httpsStatus=1"

分析完畢,上代碼

代碼

得到mid

def get_mid(url):  # 得到歌曲的mid
    headers = {  # 傳入必要請求頭,反爬
    "Accept": "application/json, text/plain, */*",
    "Accept-Encoding": "gzip, deflate",
    "Accept-Language": "zh-CN,zh;q=0.9",
    "Connection": "keep-alive",
    "Cookie": "_ga=GA1.2.2058800930.1642474837; _gid=GA1.2.1174281146.1642474837; Hm_lvt_cdb524f42f0ce19b169a8071123a4797=1642490265,1642490289,1642490402,1642498818; Hm_lpvt_cdb524f42f0ce19b169a8071123a4797=1642499234; kw_token=YK4UZW2UPL",
    "csrf": "YK4UZW2UPL",
    "Host": "www.kuwo.cn",
    "Referer": "http://www.kuwo.cn/",
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.93 Safari/537.36",
}
    resp = requests.get(url=url, headers=headers).json()
    content = resp["data"]["list"]  
    content_lis = []
    for i in content:
        mid = i["musicrid"].split("_")[-1] 
        atr = i["artist"]
        names = i["name"]
        content_lis.append({"編號": mid, "歌名": names, "歌手": atr})
    return content_lis

界面

def show_menu_song(df, data):  # 得到選擇要下載的歌曲,UI展示,選擇要下載的歌曲
    print("*************************************************")
    print("******** 這里找到了30首歌,請選擇下載一首或多首 ********")
    while True:
        choice = input("******* 請輸入編號(如果有多個編號請用空格分開)*********\n").split()
        temp = [i for i in choice if i in list(df.index)]
        if temp:
            choice1 = []
            for i in data:
                if i["編號"] in choice:
                    choice1.append({"name": f"{i['歌手']}", "mid": f"{i['編號']}"})  # 返回字典,便於索引
            print("正在准備下載,請稍等!")
            return choice1
        else:
            for i in choice:
                if i not in choice:
                    print(f"沒有編號:{i}")
            print("請重新輸入!")

            
def main1():  # 按照歌名下載
    print("*************************************************")
    name = input("************* 請輸入您想要下載的歌曲*****************\n")
    print("*************************************************")
    url = f"http://www.kuwo.cn/api/www/search/searchMusicBykeyWord?key={name}&pn=1&rn=30&httpsStatus=1"
    data = get_mid(url)  # 通過調用函數的到mid
    df = pd.DataFrame(data).set_index("編號")
    print(df)
    choice = show_menu_song(df, data)  # 存儲的是歌曲的編號
    url_api = [f"http://www.kuwo.cn/api/v1/www/music/playUrl?mid={i['mid']}&type=convert_url&httpsStatus=1" for i in choice]  # 使用列表生成式快速生成url_api
    url_name = [f"{name}_{choice[i]['name']}" for i in range(len(url_api))]
    url_list = []  # 存儲名字和url
    for i in range(len(url_api)):
        url_list.append({"name": url_name[i], "url": url_api[i]})  # 以鍵值對的方式存儲,便於命名

    return url_list # 返回結果


def main2():  # 按照歌手名字下載
    print("*************************************************")
    name = input("************** 請輸入歌手名字 *****************\n")
    while True:
        page = input("******* 請輸入要下載的頁數,一頁30首歌 ***********\n")
        if 0 < eval(page) < 10 and isinstance(eval(page), int):  # 一般9頁,210首歌應該足夠,防止輸入頁數過多,造成程序奔潰
            break
        else:
            print("請輸入1到10的數字")  # 防止隨便輸入,提高程序的可用性
    print("*************************************************")
    url = f"http://www.kuwo.cn/api/www/search/searchMusicBykeyWord?key={name}&pn={page}&rn=30&httpsStatus=1"
    data = get_mid(url)  # 得到mid
    url_list = []  # 存放url
    for i in data:
        url_list.append({"name": i["歌名"],
                         "url": f'http://www.kuwo.cn/api/v1/www/music/playUrl?mid={i["編號"]}&type=convert_url&httpsStatus=1'})  

    return url_list


def main():
    while True:  # 界面展示,供自己選擇
        print("*************************************************")
        print("***************注意,不要下載太頻繁******************")
        print("*************************************************")
        choice = input("*****根據歌手名字下載音樂還是歌名?1(歌名)/2(歌手)*******\n")
        print("*************************************************")
        if choice == "1":
            return main1()
        elif choice == "2":
            return main2()
        else:
            print("請按要求輸入!")

下載

def download(url_dic):  # 下載音樂
    headers1 = {  # 得到音樂下載地址的請求頭
        "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9",
        "Accept-Encoding": "gzip, deflate",
        "Accept-Language": "zh-CN,zh;q=0.9",
        "Cache-Control": "max-age=0",
        "Connection": "keep-alive",
        "Cookie": "_ga=GA1.2.2058800930.1642474837; _gid=GA1.2.1174281146.1642474837; Hm_lvt_cdb524f42f0ce19b169a8071123a4797=1642509517,1642511738,1642514710,1642551220; Hm_lpvt_cdb524f42f0ce19b169a8071123a4797=1642551237; kw_token=GR2I2YW69IE",
        "Host": "www.kuwo.cn",
        "Upgrade-Insecure-Requests": "1",
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.93 Safari/537.36",
    }
    resp = requests.get(url=url_dic["url"], headers=headers1).json()
    dl = resp["data"]["url"]  # 得到的歌曲url
    time.sleep(1)
    print(dl, "\n如果沒有這首歌,則可以通過這個鏈接下載")
    time.sleep(1)
    resp = requests.get(url=dl)
    if resp.status_code == 200:  # 判斷訪問是否成功,如果失敗則直接退出,避免報錯
        rp = resp.content
        try:  # 判斷有沒有D盤和E盤
            if not os.path.exists('e:/music'):
                os.mkdir("e:/music")
            with open(f"e:/music/{url_dic['name']}.mp3", "wb") as f:
                f.write(rp)
            loguru.logger.info(f"{url_dic['name']}下載完成")
            print("保存的歌曲在e盤的music文件夾里面")
        except:
            if not os.path.exists('d:/music'):
                os.mkdir("d:/music")
            with open(f"d:/music/{url_dic['name']}.mp3", "wb") as f:
                f.write(rp)
            loguru.logger.info(f"{url_dic['name']}下載完成")
            print("保存的歌曲在d盤的music文件夾里面")
    else:
        print("訪問失敗")

啟動

if __name__ == '__main__':

    with ThreadPoolExecutor(50) as pol:  # 使用線程池開啟下載
        pol.map(download, main())

    time.sleep(5)

總代碼

import requests, os, time, loguru
import pandas as pd
from concurrent.futures import ThreadPoolExecutor


def get_mid(url):  # 得到歌曲的mid
    headers = {  # 傳入必要請求頭,反爬
    "Accept": "application/json, text/plain, */*",
    "Accept-Encoding": "gzip, deflate",
    "Accept-Language": "zh-CN,zh;q=0.9",
    "Connection": "keep-alive",
    "Cookie": "_ga=GA1.2.2058800930.1642474837; _gid=GA1.2.1174281146.1642474837; Hm_lvt_cdb524f42f0ce19b169a8071123a4797=1642490265,1642490289,1642490402,1642498818; Hm_lpvt_cdb524f42f0ce19b169a8071123a4797=1642499234; kw_token=YK4UZW2UPL",
    "csrf": "YK4UZW2UPL",
    "Host": "www.kuwo.cn",
    "Referer": "http://www.kuwo.cn/",
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.93 Safari/537.36",
}
    resp = requests.get(url=url, headers=headers).json()
    content = resp["data"]["list"]  
    content_lis = []
    for i in content:
        mid = i["musicrid"].split("_")[-1] 
        atr = i["artist"]
        names = i["name"]
        content_lis.append({"編號": mid, "歌名": names, "歌手": atr})
    return content_lis


def download(url_dic):  # 下載音樂
    headers1 = {  # 得到音樂下載地址的請求頭
        "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9",
        "Accept-Encoding": "gzip, deflate",
        "Accept-Language": "zh-CN,zh;q=0.9",
        "Cache-Control": "max-age=0",
        "Connection": "keep-alive",
        "Cookie": "_ga=GA1.2.2058800930.1642474837; _gid=GA1.2.1174281146.1642474837; Hm_lvt_cdb524f42f0ce19b169a8071123a4797=1642509517,1642511738,1642514710,1642551220; Hm_lpvt_cdb524f42f0ce19b169a8071123a4797=1642551237; kw_token=GR2I2YW69IE",
        "Host": "www.kuwo.cn",
        "Upgrade-Insecure-Requests": "1",
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.93 Safari/537.36",
    }
    resp = requests.get(url=url_dic["url"], headers=headers1).json()
    dl = resp["data"]["url"]  # 得到的歌曲url
    time.sleep(1)
    print(dl, "\n如果沒有這首歌,則可以通過這個鏈接下載")
    time.sleep(1)
    resp = requests.get(url=dl)
    if resp.status_code == 200:  # 判斷訪問是否成功,如果失敗則直接退出,避免報錯
        rp = resp.content
        try:  # 判斷有沒有D盤和E盤
            if not os.path.exists('e:/music'):
                os.mkdir("e:/music")
            with open(f"e:/music/{url_dic['name']}.mp3", "wb") as f:
                f.write(rp)
            loguru.logger.info(f"{url_dic['name']}下載完成")
            print("保存的歌曲在e盤的music文件夾里面")
        except:
            if not os.path.exists('d:/music'):
                os.mkdir("d:/music")
            with open(f"d:/music/{url_dic['name']}.mp3", "wb") as f:
                f.write(rp)
            loguru.logger.info(f"{url_dic['name']}下載完成")
            print("保存的歌曲在d盤的music文件夾里面")
    else:
        print("訪問失敗")


def show_menu_song(df, data):  # 得到選擇要下載的歌曲,UI展示,選擇要下載的歌曲
    print("*************************************************")
    print("******** 這里找到了30首歌,請選擇下載一首或多首 ********")
    while True:
        choice = input("******* 請輸入編號(如果有多個編號請用空格分開)*********\n").split()
        temp = [i for i in choice if i in list(df.index)]
        if temp:
            choice1 = []
            for i in data:
                if i["編號"] in choice:
                    choice1.append({"name": f"{i['歌手']}", "mid": f"{i['編號']}"})  # 返回字典,便於索引
            print("正在准備下載,請稍等!")
            return choice1
        else:
            for i in choice:
                if i not in choice:
                    print(f"沒有編號:{i}")
            print("請重新輸入!")

            
def main1():  # 按照歌名下載
    print("*************************************************")
    name = input("************* 請輸入您想要下載的歌曲*****************\n")
    print("*************************************************")
    url = f"http://www.kuwo.cn/api/www/search/searchMusicBykeyWord?key={name}&pn=1&rn=30&httpsStatus=1"
    data = get_mid(url)  # 通過調用函數的到mid
    df = pd.DataFrame(data).set_index("編號")
    print(df)
    choice = show_menu_song(df, data)  # 存儲的是歌曲的編號
    url_api = [f"http://www.kuwo.cn/api/v1/www/music/playUrl?mid={i['mid']}&type=convert_url&httpsStatus=1" for i in choice]  # 使用列表生成式快速生成url_api
    url_name = [f"{name}_{choice[i]['name']}" for i in range(len(url_api))]
    url_list = []  # 存儲名字和url
    for i in range(len(url_api)):
        url_list.append({"name": url_name[i], "url": url_api[i]})  # 以鍵值對的方式存儲,便於命名

    return url_list # 返回結果


def main2():  # 按照歌手名字下載
    print("*************************************************")
    name = input("************** 請輸入歌手名字 *****************\n")
    while True:
        page = input("******* 請輸入要下載的頁數,一頁30首歌 ***********\n")
        if 0 < eval(page) < 10 and isinstance(eval(page), int):  # 一般9頁,210首歌應該足夠,防止輸入頁數過多,造成程序奔潰
            break
        else:
            print("請輸入1到10的數字")  # 防止隨便輸入,提高程序的可用性
    print("*************************************************")
    url = f"http://www.kuwo.cn/api/www/search/searchMusicBykeyWord?key={name}&pn={page}&rn=30&httpsStatus=1"
    data = get_mid(url)  # 得到mid
    url_list = []  # 存放url
    for i in data:
        url_list.append({"name": i["歌名"],
                         "url": f'http://www.kuwo.cn/api/v1/www/music/playUrl?mid={i["編號"]}&type=convert_url&httpsStatus=1'})  

    return url_list


def main():
    while True:  # 界面展示,供自己選擇
        print("*************************************************")
        print("***************注意,不要下載太頻繁******************")
        print("*************************************************")
        choice = input("*****根據歌手名字下載音樂還是歌名?1(歌名)/2(歌手)*******\n")
        print("*************************************************")
        if choice == "1":
            return main1()
        elif choice == "2":
            return main2()
        else:
            print("請按要求輸入!")

            
if __name__ == '__main__':

    with ThreadPoolExecutor(50) as pol:  # 使用線程池開啟下載
        pol.map(download, main())

    time.sleep(5)

注意

本案例只供學習,切勿他用


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM