欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

教你如何使Python爬取酷我在線音樂

 更新時間:2022年04月11日 08:23:58   作者:之一Yo  
這篇文章主要介紹了如何利用?Python?實現(xiàn)酷我在線音樂的爬取,文中的示例代碼講解詳細,感興趣的小伙伴可以跟隨小編一起了解一下

前言

寫這篇博客的初衷是加深自己對網(wǎng)絡(luò)請求發(fā)送和響應(yīng)的理解,僅供學習使用,請勿用于非法用途!文明爬蟲,從我做起。下面進入正題。

獲取歌曲信息列表

在酷我的搜索框中輸入關(guān)鍵詞 aiko,回車之后可以看到所有和 aiko 相關(guān)的歌曲。打開開發(fā)者模式,在網(wǎng)絡(luò)面板下按下 ctrl + f,搜索 二人,可以找到響應(yīng)結(jié)果中包含 二人 的請求,這個請求就是用來獲取歌曲信息列表的。

請求參數(shù)分析

請求的具體格式如下圖所示,可以看到請求路徑為 http://www.kuwo.cn/api/www/search/searchMusicBykeyWord,請求參數(shù)包括:

  • key: 搜索關(guān)鍵詞,此處為 aiko
  • pn: 頁碼,page number 的縮寫,此處為 1
  • rn: 每頁條目數(shù),應(yīng)該是 row number 的縮寫,默認為 30
  • httpsStatus:https 的狀態(tài)?感覺沒啥大用,看了源代碼里面是直接寫死 t.url = t.url + "?reqId=".concat(n, "&httpsStatus=1")
  • reqId:請求標識,刷新頁面之后值會發(fā)生改變,不知道有啥用,待會兒模擬請求的時候試著不帶上他會怎么樣

打開 Apifox(當然 postman 也行),新建一個接口,把請求路徑和參數(shù)設(shè)置為下圖所示的樣子,為了讓響應(yīng)結(jié)果簡短點,這里把每頁的條目數(shù)設(shè)置為 1 而非默認的 30

在沒有設(shè)置額外請求頭的情況下發(fā)個請求試試,發(fā)現(xiàn) 403 Forbidden 了,emmmmm,應(yīng)該是防盜鏈所致:

可以看到瀏覽器發(fā)出的請求的請求頭中有設(shè)置 Referer 字段,把它加上,應(yīng)該不會再報錯了吧:

這次狀態(tài)碼為 200,但是沒有收到任何數(shù)據(jù),success 為 false 說明請求失敗了,message 指明了失敗原因是缺少 CSRF token。問題不大,接著把瀏覽器發(fā)出的請求中的 csrf 加到 Apifox 請求頭中,再發(fā)請求,還是報錯 CSRF token Invalid!。算了,還是老老實實把 Cookie 也加上吧,但也不是全部加上,只加 kw_token=CCISYM2HV96 部分,因為 Cookie 里面只有這個字段和 token 有關(guān)系且它的值和 csrf 相同。

在源代碼面板按下 ctrl + shift + f,搜索一下 csrf,可以看到 csrf 本來就是來自 Object(h.b)("kw_token"),這個函數(shù)用來取出 document.cookie 中的 kw_token 字段值。至于 Cookie 中的 kw_token 怎么計算得到的,那就是服務(wù)器的事情了,咱們只管 CV 操作即可。

準備好參數(shù)和請求頭,重新發(fā)送請求,可以得到想要的數(shù)據(jù)。如果去掉 reqId 參數(shù),也可以拿到數(shù)據(jù),但是會有略微的不同,這里就不貼出來了:

{
    "code": 200,
    "curTime": 1649482287185,
    "data": {
        "total": "741",
        "list": [
            {
                "musicrid": "MUSIC_11690555",
                "barrage": "0",
                "ad_type": "",
                "artist": "aiko",
                "mvpayinfo": {
                    "play": 0,
                    "vid": 8530326,
                    "down": 0
                },
                "nationid": "0",
                "pic": "http://img4.kuwo.cn/star/starheads/500/24/88/4146545084.jpg",
                "isstar": 0,
                "rid": 11690555,
                "duration": 362,
                "score100": "42",
                "ad_subtype": "0",
                "content_type": "0",
                "track": 1,
                "hasLossless": true,
                "hasmv": 1,
                "releaseDate": "1970-01-01",
                "album": "",
                "albumid": 0,
                "pay": "16515324",
                "artistid": 1907,
                "albumpic": "http://img4.kuwo.cn/star/starheads/500/24/88/4146545084.jpg",
                "originalsongtype": 0,
                "songTimeMinutes": "06:02",
                "isListenFee": false,
                "pic120": "http://img4.kuwo.cn/star/starheads/120/24/88/4146545084.jpg",
                "name": "戀をしたのは",
                "online": 1,
                "payInfo": {
                    "play": "1100",
                    "nplay": "00111",
                    "overseas_nplay": "11111",
                    "local_encrypt": "1",
                    "limitfree": 0,
                    "refrain_start": 89150,
                    "feeType": {
                        "song": "1",
                        "vip": "1"
                    },
                    "down": "1111",
                    "ndown": "11111",
                    "download": "1111",
                    "cannotDownload": 0,
                    "overseas_ndown": "11111",
                    "refrain_end": 126247,
                    "cannotOnlinePlay": 0
                },
                "tme_musician_adtype": "0"
            }
        ]
    },
    "msg": "success",
    "profileId": "site",
    "reqId": "4b55cf4b0171253c33ce1d71b999c42f",
    "tId": ""
}

請求代碼

響應(yīng)結(jié)果的 data 字段中有很多東西,這里只提取需要的部分。在提取之前先來定義一下歌曲信息實體類,這樣在其他函數(shù)中要一首歌曲的信息時只要把實體類的實例傳入即可。

# coding:utf-8
from copy import deepcopy
from dataclasses import dataclass


class Entity:
    """ Entity abstract class """

    def __setitem__(self, key, value):
        self.__dict__[key] = value

    def __getitem__(self, key):
        return self.__dict__[key]

    def get(self, key, default=None):
        return self.__dict__.get(key, default)

    def copy(self):
        return deepcopy(self)


@dataclass
class SongInfo(Entity):
    """ Song information """
    file: str = None
    title: str = None
    singer: str = None
    album: str = None
    year: int = None
    genre: str = None
    duration: int = None
    track: int = None
    trackTotal: int = None
    disc: int = None
    discTotal: int = None
    createTime: int = None
    modifiedTime: int = None

上述代碼顯示定義了實體類的基類,并且重寫了 __getitem__ 和 __setitem__ 魔法方法,這樣我們可以像訪問字典一樣來訪問實體類對象的屬性。接著讓歌曲信息實體類繼承了實體類基類,并且使用 @dataclass 裝飾器,這是 python 3.7 引入的新特性,使用它裝飾之后的實體類無需實現(xiàn)構(gòu)造函數(shù)、__str__等常用函數(shù),python 會幫我們自動生成。

在發(fā)送請求的過程中可能會遇到各種異常,如果在代碼里面寫 try except 語句會顯得很亂,這里同樣可以用裝飾器來解決這個問題。

# coding:utf-8
from copy import deepcopy


def exceptionHandler(*default):
    """ decorator for exception handling

    Parameters
    ----------
    *default:
        the default value returned when an exception occurs
    """

    def outer(func):

        def inner(*args, **kwargs):
            try:
                return func(*args, **kwargs)
            except BaseException as e:
                print(e)
                value = deepcopy(default)
                if len(value) == 0:
                    return None
                elif len(value) == 1:
                    return value[0]
                else:
                    return value

        return inner

    return outer

下面是發(fā)送獲取歌曲信息請求的代碼,使用 exception_handler 裝飾了 getSongInfos 方法,這樣發(fā)生異常時會打印異常信息并返回默認值:

# coding:utf-8
import json
from urllib import parse
from typing import List, Tuple

import requests


class KuWoMusicCrawler:
    """ Crawler of KuWo Music """

    def __init__(self):
        super().__init__()
        self.headers = {
            'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) '
                          'AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36',
            'Cookie': 'kw_token=C713RK6IJ8J',
            'csrf': 'C713RK6IJ8J',
            'Host': 'www.kuwo.cn',
            'Referer': ''
        }

    @exceptionHandler([], 0)
    def getSongInfos(self, key_word: str, page_num=1, page_size=10) -> Tuple[List[SongInfo], int]:
        key_word = parse.quote(key_word)

        # configure request header
        headers = self.headers.copy()
        headers["Referer"] = 'http://www.kuwo.cn/search/list?key='+key_word

        # send request for song information
        url = f'http://www.kuwo.cn/api/www/search/searchMusicBykeyWord?key={key_word}&pn={page_num}&rn={page_size}&reqId=c06e0e50-fe7c-11eb-9998-47e7e13a7206'
        response = requests.get(url, headers=headers)
        response.raise_for_status()

        # parse the response data
        song_infos = []
        data = json.loads(response.text)['data']
        for info in data['list']:
            song_info = SongInfo()
            song_info['rid'] = info['rid']
            song_info.title = info['name']
            song_info.singer = info['artist']
            song_info.album = info['album']
            song_info.year = info['releaseDate'].split('-')[0]
            song_info.track = info['track']
            song_info.trackTotal = info['track']
            song_info.duration = info["duration"]
            song_info.genre = 'Pop'
            song_info['coverPath'] = info.get('albumpic', '')
            song_infos.append(song_info)

        return song_infos, int(data['total'])

獲取歌曲下載鏈接

免費歌曲

雖然我們實現(xiàn)了搜索歌曲的功能,但是沒拿到每一首歌的播放地址,也就沒辦法把歌曲下載下來。我們先來播放一首不收費的歌曲試試??梢钥吹綖g覽器發(fā)送了一個獲取播放鏈接的請求,路徑為 http://www.kuwo.cn/api/v1/www/music/playUrl,有兩個需要關(guān)注的參數(shù):

  • mid:音樂 Id,此處的值為 941583,和頁面 url 中的編號一致,由于我們是通過點擊搜索結(jié)果頁面中 二人 跳轉(zhuǎn)過來的,而 二人 這條結(jié)果也是動態(tài)加載出來的,超鏈接中的 Id 肯定也來自于上一節(jié)中響應(yīng)結(jié)果的某個字段。二人 是第四條記錄,通過對比可以發(fā)現(xiàn) data.list[3].rid 就是 mid
  • type:音樂類型?此處的值為 music,發(fā)送請求的時候也設(shè)置為 music 即可

在 Apifox 中新建一個獲取歌曲播放地址的請求,如下所示,發(fā)現(xiàn)可以成功拿到播放地址:

付費歌曲

現(xiàn)在換一首歌,比如 aiko - 橫顏,點擊歌曲頁面上的播放按鈕時會彈出要求在客戶端中付費收聽的對話框。直接發(fā)送請求,響應(yīng)結(jié)果會是下面這個樣子,狀態(tài)碼為 403:

其實酷我在 2021 年 9 月份的時候換過獲取播放地址的接口,那時候的請求接口為 http://www.kuwo.cn/url,支持以下幾個參數(shù):

  • format: 在線音樂的格式,可以是 mp3
  • type: 和現(xiàn)在的接口中的 type 參數(shù)一樣,但是值為 convert_url3
  • rid: 音樂 Id,和 mid 一樣
  • br: 在線音樂的比特率,越大則音質(zhì)越高,可選的有 128kmp3、 192kmp3 和 320kmp3

這個接口不管是付費音樂還是免費音樂都可以用。如果將現(xiàn)在這個接口的 type 參數(shù)的值換成 convert_url3,請求結(jié)果如下所示,說明成功了:

請求代碼

下面是獲取在線音樂播放鏈接的代碼,只需調(diào)用 downloadSong 函數(shù)并把爬取到的歌曲傳入就能完成歌曲的下載:

@exceptionHandler('')
def getSongUrl(self, song_info: SongInfo) -> str:
    # configure request header
    headers = self.headers.copy()
    headers.pop('Referer')
    headers.pop('csrf')

    # send request for play url
    url = f"http://www.kuwo.cn/api/v1/www/music/playUrl?mid={song_info['rid']}&type=convert_url3"
    response = requests.get(url, headers=headers)
    response.raise_for_status()
    play_url = json.loads(response.text)['data']['url']

    return play_url

@exceptionHandler('')
def downloadSong(self, song_info: SongInfo, save_dir: str) -> str:
    # get play url
    url = self.getSongUrl(song_info)
    if not url:
        return ''

    # send request for binary data of audio
    headers = self.headers.copy()
    headers.pop('Referer')
    headers.pop('csrf')
    headers.pop('Host')
    response = requests.get(url, headers=headers)
    response.raise_for_status()

    # save audio file
    song_path = os.path.join(
        save_dir, f"{song_info.singer} - {song_info.title}.mp3")
    with open(song_path, 'wb') as f:
        f.write(data)

    return song

后記

除了獲取歌曲的詳細信息和播放地址外,我們還能拿到歌詞、歌手信息等,方法是類似的,在我的 Groove 中提供了在線歌曲的功能,一部分接口就是來自酷我,還有一些來自酷狗和網(wǎng)易云,爬蟲的代碼在 app/common/crawler 目錄下,喜歡的話可以給個 star 哦,以上~~

以上就是教你如何使Python爬取酷我在線音樂的詳細內(nèi)容,更多關(guān)于Python爬取音樂的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Pygame實現(xiàn)游戲最小系統(tǒng)功能詳解

    Pygame實現(xiàn)游戲最小系統(tǒng)功能詳解

    這篇文章主要介紹了Pygame實現(xiàn)游戲最小系統(tǒng),Pygame是一個專門用來開發(fā)游戲的 Python 模塊,主要為開發(fā)、設(shè)計 2D 電子游戲而生,具有免費、開源,支持多種操作系統(tǒng),具有良好的跨平臺性等優(yōu)點
    2022-11-11
  • python抽取指定url頁面的title方法

    python抽取指定url頁面的title方法

    今天小編就為大家分享一篇python抽取指定url頁面的title方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2018-05-05
  • 如何寫python的配置文件

    如何寫python的配置文件

    在本篇文章里小編給大家分享了關(guān)于python寫配置文件方法,對此有興趣的朋友們可以學習參考下。
    2020-06-06
  • python計算機視覺opencv矩形輪廓頂點位置確定

    python計算機視覺opencv矩形輪廓頂點位置確定

    這篇文章主要為大家介紹了python計算機視覺opencv矩形輪廓頂點位置確定,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2022-05-05
  • 你所不知道的Python奇技淫巧13招【實用】

    你所不知道的Python奇技淫巧13招【實用】

    有時候你會看到很Cool的Python代碼,你驚訝于它的簡潔,你不由自主地贊嘆:竟然還能這樣寫。其實,這些優(yōu)雅的代碼都要歸功于Python的特性,只要你能掌握這些Pythonic的技巧,你一樣可以寫出像詩一樣的Python代碼。本文主要介紹Python應(yīng)用的一些小技巧。一起來看下吧
    2016-12-12
  • Python Grid使用和布局詳解

    Python Grid使用和布局詳解

    這篇文章主要為大家詳細介紹了Python Grid使用和布局,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2018-06-06
  • Python可視化Matplotlib散點圖scatter()用法詳解

    Python可視化Matplotlib散點圖scatter()用法詳解

    這篇文章主要介紹了Python可視化中Matplotlib散點圖scatter()的用法詳解,文中附含詳細示例代碼,有需要得朋友可以借鑒參考下,希望能夠有所幫助
    2021-09-09
  • 在Python中使用Neo4j數(shù)據(jù)庫的教程

    在Python中使用Neo4j數(shù)據(jù)庫的教程

    這篇文章主要介紹了在Python中使用Neo4j數(shù)據(jù)庫的教程,Neo4j是一個具有一定人氣的非關(guān)系型的數(shù)據(jù)庫,需要的朋友可以參考下
    2015-04-04
  • python基礎(chǔ)之while循環(huán)、for循環(huán)詳解及舉例

    python基礎(chǔ)之while循環(huán)、for循環(huán)詳解及舉例

    所謂循環(huán)結(jié)構(gòu)就是程序中控制某條或某些指令重復(fù)執(zhí)行的結(jié)構(gòu),下面這篇文章主要給大家介紹了關(guān)于python基礎(chǔ)之while循環(huán)、for循環(huán)的相關(guān)資料,文中通過實例代碼介紹的非常詳細,需要的朋友可以參考下
    2022-04-04
  • python爬取m3u8連接的視頻

    python爬取m3u8連接的視頻

    這篇文章主要為大家詳細介紹了python如何爬取m3u8連接的視頻,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2018-02-02

最新評論