主頁 > 後端開發 > python爬蟲爬取網易云音樂(超詳細教程,附原始碼)

python爬蟲爬取網易云音樂(超詳細教程,附原始碼)

2022-12-07 07:17:49 後端開發

一、 前言

先說結論,目前無法下載無損音樂,也無法下載vip音樂,
此代碼模擬web網頁js加密的程序,向api介面發送引數并獲取資料,僅供參考學習,如果需要下載網易云音樂,不如直接在客戶端下載,客戶端還可以下載無損音樂,
代碼還是半成品,打算再做個音樂播放器,直接打包成exe,等有時間做好了再傳到github上去,現在先把決議程序記錄下來發布,
至于音樂搜索器,我所知道的有一個,地址:https://iw233.cn/music/
在這里插入圖片描述
上面這個網頁直接回傳所有的搜索結果和音樂資訊,如要使用,請自行決議(很簡單的一個頁面),
網上流傳的網易云音樂外鏈地址:http://music.163.com/song/media/outer/url?id=534544522.mp3,我也不知道怎么來的,輸入音樂id即可獲得下載鏈接,本著學習的態度,我認為還是從頭到尾決議下載鏈接才好,因此不考慮使用該外鏈,
介面檔案已發布
鏈接: https://25ukpfkme3.apifox.cn 訪問密碼: qtlXaZPH

二、決議程序

1、音樂搜索

(1)獲取鏈接

來到網易云音樂首頁,輸入音樂名稱,得到搜索結果
搜索結果
按F12,打開開發者工具,重新重繪一下界面,點擊網路,在篩選器里只篩選XHR和Fetch資料,點擊預覽,一個一個鏈接往下找,直到找到我們需要的資料為止,
回傳資料
得到音樂搜索的api介面:https://music.163.com/weapi/cloudsearch/get/web?csrf_token=
api

csrf_token只有你登錄時才有,有沒有這個值不影響回傳的結果,
需要注意,這個api介面的POST請求,后面所有的api介面都是POST請求

(2)分析引數

點擊負載,查看我們需要傳入哪些資料
引數
可以看到我們需要傳入params和encSecKey兩個引數,才能獲取資料,否則得到的資料為空,
那么如何獲取這兩個引數呢?點擊發起程式,隨便點擊一個,進入js腳本界面
發起程式
點下面的{},將js代碼格式化,這樣我們更好查看代碼
js
在js頁面里按ctrl+f,直接搜索encSecKey,總共有三個結果,encSecKey
在這里插入圖片描述

可以看到,在執行window.asrsea()函式后,生成了params和encSecKey,
這里我們貼一下js原始碼,后面還會用到

var bMr5w = window.asrsea(JSON.stringify(i8a), bsg1x(["流淚", "強"]), bsg1x(TH5M.md), bsg1x(["愛心", "女孩", "驚恐", "大笑"]));
            e8e.data = https://www.cnblogs.com/pikeduo/p/j8b.cr9i({
                params: bMr5w.encText,
                encSecKey: bMr5w.encSecKey
            })

那么window.asrsea()是什么呢,搜索asrsea,可以看到window.asrsea()=d
asrsea
找到d函式,就在window.asrsea()上面,d
把js代碼復制出來

function d(d, e, f, g) {
        var h = {}
          , i = a(16);
        return h.encText = b(d, g),
        h.encText = b(h.encText, i),
        h.encSecKey = c(i, e, f),
        h
    }

其共涉及到三個函式a,b,c,我們先不急研究這三個函式的作用,先查看d函式傳進去的四個引數d,e,f,g是什么,
其實從前面的window.asrsea()函式里我們就知道,這四個引數分別為JSON.stringify(i8a), bsg1x(["流淚", "強"]), bsg1x(TH5M.md), bsg1x(["愛心", "女孩", "驚恐", "大笑"]),
在這行打一個斷點,重新重繪一下界面,進入除錯模式,除錯
在控制臺依次輸入這四個引數,獲得其值
控制臺
點擊執行,繼續執行下一步,再次查看四個引數的值,可以在監視里面輸入四個引數,每次執行后會顯示引數的值
執行
經過多次除錯后我們發現,除了第一個引數有變化之外,后面三個引數都是固定的,window.asrsea()函式會將這四個引數傳入到d函式中,也就是d,e,f,g

e = '010001'
f = '00e0b509f6259df8642dbc35662901477df22677ec152b5ff68ace615bb7b725152b3ab17a876aea8a5aa76d2e417629ec4ee341f56135fccf695280104e0312ecbda92557c93870114af6c9d05c4f7f0c3685b7a46bee255932575cce10b424d813cfe4875d3e82047b97ddef52741d546b8e289dc6935b3ece0462db0a22b8e7'
g = '0CoJUm6Qyw8W8jud'

那么d引數是什么呢?在d函式里打個斷點,除錯頁面直至進入我們的api介面https://music.163.com/weapi/cloudsearch/get/web?csrf_token=為止
介面

Y8Q = Y8Q.replace("api", "weapi");

仔細查看此段代碼,這段代碼是將鏈接里的api替換成weapi,因此可以根據Y8Q來定位到當前的鏈接地址,然后進行下一步除錯,
在該行打一個斷點,開始除錯,
這里有個除錯的小技巧,我們可以先在Y8Q那一行打一個斷點,先進行除錯執行,當Y8Q變為搜索的api介面后,再在window.asrsea()那里打一個斷點,然后在d函式打一個斷點,進行除錯,
記得在Y8Q那里除錯時先重繪一下頁面,但是后面除錯不要
定位
這里我們定位到了api介面,注意api還沒有換成weapi,點擊執行下一步后,api被替換了
api
然后在window.asrsea打一個斷點,點擊執行
在這里插入圖片描述
然后在d函式打一個斷點,點擊執行,得到d的值
在這里插入圖片描述
直接將d復制過來,注意字串前要加個r,要不然字串里的''會被當做轉義字符處理,

d = r'{"hlpretag":"<span class=\"s-fc7\">","hlposttag":"</span>","s":"自由の翅","type":"1","offset":"0","total":"true","limit":"30","csrf_token":""}'
e = '010001'
f = '00e0b509f6259df8642dbc35662901477df22677ec152b5ff68ace615bb7b725152b3ab17a876aea8a5aa76d2e417629ec4ee341f56135fccf695280104e0312ecbda92557c93870114af6c9d05c4f7f0c3685b7a46bee255932575cce10b424d813cfe4875d3e82047b97ddef52741d546b8e289dc6935b3ece0462db0a22b8e7'
g = '0CoJUm6Qyw8W8jud'

可以看到,s里面的就是我們搜索的音樂名稱了,后面可以更改s的值來改變搜索的音樂,

(3)函式分析

好了,講了這么多,結果只分析出四個引數的值是什么,接下來我們發現a,b,c,d這四個函式的作用,

a函式

function a(a) {
        var d, e, b = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789", c = "";
        for (d = 0; a > d; d += 1)
            e = Math.random() * b.length,
            e = Math.floor(e),
            c += b.charAt(e);
        return c
    }

根據我十分粗糙的js知識來看,這個函式回傳的是一個b中的隨機字串,函式接收字串的長度,我們將它改寫成Python代碼,

# 獲取一個隨意字串,length是字串長度
def generate_str(lenght):
    str = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
    res = ''
    for i in range(lenght):
        index = random.random() * len(str)  # 獲取一個字串長度的亂數
        index = math.floor(index)  # 向下取整
        res = res + str[index]  # 累加成一個隨機字串
    return res

其實這個隨機字串完全可以定死,但為了盡量還原js腳本的執行程序,我們還是直接照搬過來吧,

b函式

    function b(a, b) {
        var c = CryptoJS.enc.Utf8.parse(b)
          , d = CryptoJS.enc.Utf8.parse("0102030405060708")
          , e = CryptoJS.enc.Utf8.parse(a)
          , f = CryptoJS.AES.encrypt(e, c, {
            iv: d,
            mode: CryptoJS.mode.CBC
        });
        return f.toString()
    }

b函式是一個AES加密程序,a是加密內容,也就是encText,b是一個key,是一個固定值,也就是上面四個引數中的g

g = '0CoJUm6Qyw8W8jud'

加密的模式為CBC,參照Python AES的加密程序,將js代碼改寫成了Python代碼

# AES加密獲得params
def AES_encrypt(text, key):
    iv = '0102030405060708'.encode('utf-8')  # iv偏移量
    text = text.encode('utf-8')  # 將明文轉換為utf-8格式
    pad = 16 - len(text) % 16
    text = text + (pad * chr(pad)).encode('utf-8')  # 明文需要轉成二進制,且可以被16整除
    key = key.encode('utf-8')  # 將密鑰轉換為utf-8格式
    encryptor = AES.new(key, AES.MODE_CBC, iv)  # 創建一個AES物件
    encrypt_text = encryptor.encrypt(text)  # 加密
    encrypt_text = base64.b64encode(encrypt_text)  # base4編碼轉換為byte字串
    return encrypt_text.decode('utf-8')

c函式

    function c(a, b, c) {
        var d, e;
        return setMaxDigits(131),
        d = new RSAKeyPair(b,"",c),
        e = encryptedString(d, a)
    }

c函式是RSA加密程序,其中a是隨機字串,b是一個key,也就是上面四個引數中的e,f也是四個引數中的f,回傳的是encSeckey

e = '010001'
f = '00e0b509f6259df8642dbc35662901477df22677ec152b5ff68ace615bb7b725152b3ab17a876aea8a5aa76d2e417629ec4ee341f56135fccf695280104e0312ecbda92557c93870114af6c9d05c4f7f0c3685b7a46bee255932575cce10b424d813cfe4875d3e82047b97ddef52741d546b8e289dc6935b3ece0462db0a22b8e7'

由于本人學習的js知識十分粗淺,因此難以看懂這段代碼,參考網上流傳的版本,改寫了代碼

# RSA加密獲得encSeckey
def RSA_encrypt(str, key, f):
    str = str[::-1]  # 隨機字串逆序排列
    str = bytes(str, 'utf-8')  # 將隨機字串轉換為byte型別的資料
    sec_key = int(codecs.encode(str, encoding='hex'), 16) ** int(key, 16) % int(f, 16)  # RSA加密
    return format(sec_key, 'x').zfill(256)  # RSA加密后字串長度為256,不足的補x

RSA加密規則不是很熟悉,感興趣的自行百度

d函式

    function d(d, e, f, g) {
        var h = {}
          , i = a(16);
        return h.encText = b(d, g),
        h.encText = b(h.encText, i),
        h.encSecKey = c(i, e, f),
        h
    }

最后就是d函式了,i是一個16位的隨機字串,可以定死,使用b函式先對d函式進行了AES加密,由于d,g都是固定值,所以得到的encText也是固定值,可以通過除錯來獲得第一次加密后的encText,然后在運行一下你的Python代碼,查看encText是否一致,用來驗證d是否正確,
第一次加密得到encText,再次對encText進行第二次加密,不過key換成了隨機字串i,兩次加密后得到encText,

# 獲取引數
def get_params(d, e, f, g):
    i = generate_str(16)    # 生成一個16位的隨機字串
    # i = 'aO6mqZksdJbqUygP'
    encText = AES_encrypt(d, g)
    # print(encText)    # 列印第一次加密的params,用于測驗d正確
    params = AES_encrypt(encText, i)  # AES加密兩次后獲得params
    encSecKey = RSA_encrypt(i, e, f)  # RSA加密后獲得encSecKey
    return params, encSecKey

至此,引數params和encSecKey都決議完畢,由于字串是隨機的,因此每次運行后得到的params和encSecKey都不一樣,

(4)分析回傳結果

知道引數和介面后,就可以向服務器發送請求,獲取回傳結果了,注意請求為post請求
由于后續api的決議程序基本一致,因此將代碼封裝起來,

e = '010001'
f = '00e0b509f6259df8642dbc35662901477df22677ec152b5ff68ace615bb7b725152b3ab17a876aea8a5aa76d2e417629ec4ee341f56135fccf695280104e0312ecbda92557c93870114af6c9d05c4f7f0c3685b7a46bee255932575cce10b424d813cfe4875d3e82047b97ddef52741d546b8e289dc6935b3ece0462db0a22b8e7'
g = '0CoJUm6Qyw8W8jud'

# 傳入msg和url,獲取回傳的json資料
def get_data(msg, url):
    encText, encSecKey = get_params(msg, e, f, g)   # 獲取引數
    params = {
        "params": encText,
        "encSecKey": encSecKey
    }
    re = requests.post(url=url, params=params, verify=False)    # 向服務器發送請求
    return re.json()    #回傳結果

# 搜索回傳的資料
serch_msg = r'{"hlpretag":"<span class=\"s-fc7\">","hlposttag":"</span>","s":"自由の翅","type":"1","offset":"0","total":"true","limit":"30","csrf_token":""}'
serch_url = 'https://music.163.com/weapi/cloudsearch/get/web?csrf_token='
print(get_data(serch_msg, serch_url))

三個引數e,f,g固定不變,只更改msg的值,
注意msg字串前要加個r,防止編譯器將字串里的\當做轉義字符處理,
回傳結果如下

{
    "needLogin": true,
    "result": {
        "searchQcReminder": null,
        "songs": [
            {
                "name": "自由の翅",
                "id": 473403600,
                "pst": 0,
                "t": 0,
                "ar": [
                    {
                        "id": 17672,
                        "name": "佐藤ひろ美",
                        "tns": [
                            "佐藤裕美"
                        ],
                        "alias": [
                            "さとう ひろみ",
                            "Sato Hiromi"
                        ],
                        "alia": [
                            "さとう ひろみ",
                            "Sato Hiromi"
                        ]
                    }
                ],
                "alia": [
                    "PCゲーム『月影のシミュラクル -解放の羽-』OPテーマ"
                ],
                "pop": 85,
                "st": 0,
                "rt": null,
                "fee": 0,
                "v": 12,
                "crbt": null,
                "cf": "",
                "al": {
                    "id": 35377102,
                    "name": "月影のシミュラクル -解放の羽- オリジナルサウンドトラック",
                    "picUrl": "http://p3.music.126.net/jUm6aclu8k5fNUdwEODz0w==/18598239185710248.jpg",
                    "tns": [],
                    "pic_str": "18598239185710248",
                    "pic": 18598239185710250
                },
                "dt": 276866,
                "h": {
                    "br": 320000,
                    "fid": 0,
                    "size": 11077007,
                    "vd": -79678,
                    "sr": 44100
                },
                "m": {
                    "br": 192000,
                    "fid": 0,
                    "size": 6646222,
                    "vd": -77200,
                    "sr": 44100
                },
                "l": {
                    "br": 128000,
                    "fid": 0,
                    "size": 4430829,
                    "vd": -76001,
                    "sr": 44100
                },
                "sq": {
                    "br": 1052522,
                    "fid": 0,
                    "size": 36426061,
                    "vd": -79658,
                    "sr": 44100
                },
                "hr": null,
                "a": null,
                "cd": "1",
                "no": 1,
                "rtUrl": null,
                "ftype": 0,
                "rtUrls": [],
                "djId": 0,
                "copyright": 0,
                "s_id": 0,
                "mark": 262144,
                "originCoverType": 0,
                "originSongSimpleData": null,
                "tagPicList": null,
                "resourceState": true,
                "version": 12,
                "songJumpInfo": null,
                "entertainmentTags": null,
                "single": 0,
                "noCopyrightRcmd": null,
                "rtype": 0,
                "rurl": null,
                "mst": 9,
                "cp": 663018,
                "mv": 0,
                "publishTime": 1485446400000,
                "tns": [
                    "自由的翅膀"
                ],
                "privilege": {
                    "id": 473403600,
                    "fee": 0,
                    "payed": 0,
                    "st": 0,
                    "pl": 320000,
                    "dl": 999000,
                    "sp": 7,
                    "cp": 1,
                    "subp": 1,
                    "cs": false,
                    "maxbr": 999000,
                    "fl": 320000,
                    "toast": false,
                    "flag": 256,
                    "preSell": false,
                    "playMaxbr": 999000,
                    "downloadMaxbr": 999000,
                    "maxBrLevel": "lossless",
                    "playMaxBrLevel": "lossless",
                    "downloadMaxBrLevel": "lossless",
                    "plLevel": "exhigh",
                    "dlLevel": "lossless",
                    "flLevel": "exhigh",
                    "rscl": null,
                    "freeTrialPrivilege": {
                        "resConsumable": false,
                        "userConsumable": false,
                        "listenType": null
                    },
                    "chargeInfoList": [
                        {
                            "rate": 128000,
                            "chargeUrl": null,
                            "chargeMessage": null,
                            "chargeType": 0
                        },
                        {
                            "rate": 192000,
                            "chargeUrl": null,
                            "chargeMessage": null,
                            "chargeType": 0
                        },
                        {
                            "rate": 320000,
                            "chargeUrl": null,
                            "chargeMessage": null,
                            "chargeType": 0
                        },
                        {
                            "rate": 999000,
                            "chargeUrl": null,
                            "chargeMessage": null,
                            "chargeType": 1
                        }
                    ]
                }
            },
            {
                "name": "自由の翅 (BEAST-Ⅵ Bootleg)",
                "id": 1946185953,
                "pst": 0,
                "t": 0,
                "ar": [
                    {
                        "id": 49024337,
                        "name": "Nero",
                        "tns": [],
                        "alias": []
                    }
                ],
                "alia": [],
                "pop": 5,
                "st": 0,
                "rt": "",
                "fee": 0,
                "v": 3,
                "crbt": null,
                "cf": "",
                "al": {
                    "id": 144732637,
                    "name": "HyperRave01",
                    "picUrl": "http://p3.music.126.net/C3YZ8fAg8TJ1pgTdlAbxnA==/109951167398067153.jpg",
                    "tns": [],
                    "pic_str": "109951167398067153",
                    "pic": 109951167398067150
                },
                "dt": 182987,
                "h": {
                    "br": 320000,
                    "fid": 0,
                    "size": 7321644,
                    "vd": -85382,
                    "sr": 44100
                },
                "m": {
                    "br": 192000,
                    "fid": 0,
                    "size": 4393004,
                    "vd": -83101,
                    "sr": 44100
                },
                "l": {
                    "br": 128000,
                    "fid": 0,
                    "size": 2928684,
                    "vd": -81941,
                    "sr": 44100
                },
                "sq": null,
                "hr": null,
                "a": null,
                "cd": "01",
                "no": 10,
                "rtUrl": null,
                "ftype": 0,
                "rtUrls": [],
                "djId": 0,
                "copyright": 0,
                "s_id": 0,
                "mark": 262144,
                "originCoverType": 0,
                "originSongSimpleData": null,
                "tagPicList": null,
                "resourceState": true,
                "version": 3,
                "songJumpInfo": null,
                "entertainmentTags": null,
                "single": 0,
                "noCopyrightRcmd": null,
                "rtype": 0,
                "rurl": null,
                "mst": 9,
                "cp": 2707442,
                "mv": 0,
                "publishTime": 0,
                "privilege": {
                    "id": 1946185953,
                    "fee": 0,
                    "payed": 0,
                    "st": 0,
                    "pl": 320000,
                    "dl": 320000,
                    "sp": 7,
                    "cp": 1,
                    "subp": 1,
                    "cs": false,
                    "maxbr": 320000,
                    "fl": 320000,
                    "toast": false,
                    "flag": 128,
                    "preSell": false,
                    "playMaxbr": 320000,
                    "downloadMaxbr": 320000,
                    "maxBrLevel": "exhigh",
                    "playMaxBrLevel": "exhigh",
                    "downloadMaxBrLevel": "exhigh",
                    "plLevel": "exhigh",
                    "dlLevel": "exhigh",
                    "flLevel": "exhigh",
                    "rscl": null,
                    "freeTrialPrivilege": {
                        "resConsumable": false,
                        "userConsumable": false,
                        "listenType": null
                    },
                    "chargeInfoList": [
                        {
                            "rate": 128000,
                            "chargeUrl": null,
                            "chargeMessage": null,
                            "chargeType": 0
                        },
                        {
                            "rate": 192000,
                            "chargeUrl": null,
                            "chargeMessage": null,
                            "chargeType": 0
                        },
                        {
                            "rate": 320000,
                            "chargeUrl": null,
                            "chargeMessage": null,
                            "chargeType": 0
                        },
                        {
                            "rate": 999000,
                            "chargeUrl": null,
                            "chargeMessage": null,
                            "chargeType": 1
                        }
                    ]
                }
            }
        ],
        "songCount": 2
    },
    "code": 200
}

這里簡單說明一下,code的回應狀態,可以根據這個來判斷請求是否成功,
result里面,songCount代表搜索結果有幾個,song里面是音樂的一些資訊,
name,id,ar是藝術家artist的意思,也就是歌手,al是所屬專輯,包括名稱,封面之類的,
h, l,m,sq分別代表音質的等級,h是極高,l是較高,m的標準,sq是無損,
privilege里面是一些音樂的音質資訊,包括可下載的最大音質,會員下載資訊等,chargeInfoList列出了各個音質下載所需的權限,

2、音樂資訊

(1)歌詞

點擊一個音樂進入播放界面,打開F12,篩選后一個一個鏈接尋找https://music.163.com/weapi/song/lyric?csrf_token=,此鏈接回傳歌詞資訊
歌詞

{
    "sgc": false,
    "sfy": false,
    "qfy": false,
    "transUser": {
        "id": 2204059,
        "status": 99,
        "demand": 1,
        "userid": 76837043,
        "nickname": "烈焰中舞動的火花",
        "uptime": 1493803368844
    },
    "lyricUser": {
        "id": 2204040,
        "status": 99,
        "demand": 0,
        "userid": 114415020,
        "nickname": "another_tonary",
        "uptime": 1493803368844
    },
    "lrc": {
        "version": 5,
        "lyric": "[00:00.000] 作詞 : 羽生みいな\n[00:00.515] 作曲 : Meis Clauson\n[00:01.30]自由の翅\n[00:02.78]月影のシミュラクル -解放の羽- 0P主題歌\n[00:10.45]\n[00:23.35]ここから見る景色は 何故どこか狹く悲しく ah\n[00:32.97]聲にならない聲で そう君を呼んでいたんだ ah\n[00:43.17]逃げられない蝶のように\n[00:48.00]最期を待つだけじゃないと\n[00:53.03]溫かい手 重ねた瞬間(とき)\n[00:58.09]差し込んだ光\n[01:02.03]絡みつくこの糸が 交わされた契約が\n[01:07.14]どれほど命 縛ろうとも\n[01:11.97]いつの日かこの翅(はね)を精一杯広げて\n[01:17.10]君が傍に居てくれるなら\n[01:22.24]きっと飛び立てるの あの空へと\n[01:48.09]仕方のないことだと 何故諦めようとしてた ah\n[01:57.94]紅く染まる暗闇から 抜け出せない気がして ah\n[02:08.16]それでもまだ君と生きたい\n[02:13.07]繫いだ手は震えるけど\n[02:18.05]熱い涙 溢れた瞬間(とき)\n[02:23.06]湧き上がる勇気\n[02:27.08]捕らわれた運命が 立ちはだかる試練が\n[02:32.11]どれほどこの身操ろうとも\n[02:37.00]立ち向かいたい 強く信じるの もっと強く\n[02:42.14]獨りじゃないと思えた 君となら\n[02:48.93]飛び立てるの あの空へと\n[02:53.53]忘れかけてた 遠い記憶 あの約束 思い出して\n[03:03.27]取り戻せるの 二人ならば 広い世界を\n[03:12.20]絡みつくこの糸が 交わされた契約が\n[03:17.08]どれほど命 縛ろうとも\n[03:22.01]いつの日かこの翅を 精一杯広げて\n[03:27.06]君が傍に居てくれるなら\n[03:32.00]きっと 光の向こう\n[03:37.11]捕らわれた運命が 立ちはだかる試練が\n[03:42.08]どれほどこの身操ろうとも\n[03:46.93]立ち向かいたい 強く信じるの もっと強く\n[03:52.09]獨りじゃないと思えた 君となら\n[03:58.82]飛び立てるの あの空へと\n"
    },
    "tlyric": {
        "version": 5,
        "lyric": "[by:所間]\n[ti:自由の翅]\n[ar:佐藤ひろ美]\n[al:月影のシミュラクル -解放の羽- 扯訓限定同梱 オリジナルサウンドトラック]\n[00:01.30]\n[00:02.78]\n[00:10.45]\n[00:23.35]這里所看到的景色 為何會感到如此狹小又悲傷 ah\n[00:32.97]以泣不成聲的聲音 不斷呼喊著你 ah\n[00:43.17]如同無法掙脫的蝴蝶一般\n[00:48.00]只能默默等候終焉的到來\n[00:53.03]溫暖的雙手重合的瞬間\n[00:58.09]感受到了照射的光芒\n[01:02.03]不管這糾纏不清的絲線與這被迫簽下的契約\n[01:07.14]究竟束縛了多少的生命\n[01:11.97]總有一天要用這雙翅膀 用盡全力展翅翱翔\n[01:17.10]只要你能夠陪伴在我身邊\n[01:22.24]一定就能夠展翅高飛 向著那片天空\n[01:48.09]為何要說著“這是無可奈何的事情”而準備去放棄一切呢 ah\n[01:57.94]就算覺得無法從這漸漸染紅的黑暗中逃脫出去 ah\n[02:08.16]即便如此仍舊想要與你一同活下去\n[02:13.07]雖然緊牽著的手止不住顫抖\n[02:18.05]溫熱的淚水 滿溢的瞬間\n[02:23.06]心中所涌出的勇氣\n[02:27.08]不管這被囚禁的命運與這艱辛的試煉\n[02:32.11]會讓這幅身軀會承受多少傷害\n[02:37.00]就算如此也想要奮發向上 不斷堅信著 變得更加堅強\n[02:42.14]與你在一起的話 就不會感到孤獨\n[02:48.93]向著那片天空展翅高飛\n[02:53.53]從將要遺忘的記憶中找回了那個約定\n[03:03.27]我們一起的話 就能奪回那個寬廣的世界\n[03:12.20]不管這糾纏不清的絲線與這被迫簽下的契約\n[03:17.08]究竟束縛了多少的生命\n[03:22.01]總有一天要用這雙翅膀 用盡全力展翅翱翔\n[03:27.06]只要你能夠陪伴在我身邊\n[03:32.00]肯定就在那光芒的彼岸\n[03:37.11]不管這被囚禁的命運與這艱辛的試煉\n[03:42.08]會讓這幅身軀會承受多少傷害\n[03:46.93]就算如此也想要奮發向上 不斷堅信著 變得更加堅強\n[03:52.09]與你在一起的話 就不會感到孤獨\n[03:58.82]向著那片天空展翅高飛"
    },
    "code": 200
}

其中transUser為歌詞貢獻者,lyricUser為歌詞翻譯貢獻者,lrc里有原版歌詞,tlyric里有歌詞翻譯,
決議程序和上面一樣,除錯頁面找到d的值即可,

d = '{"id":"473403600","lv":-1,"tv":-1,"csrf_token":""}'

id為音樂id,可更改,

# 歌詞檔案
lyric_msg = '{"id":"427419615","lv":-1,"tv":-1,"csrf_token":""}'
lyric_url = 'https://music.163.com/weapi/song/lyric?csrf_token='
print(get_data(lyric_msg, lyric_url))

(2)評論

https://music.163.com/weapi/comment/resource/comments/get?csrf_token=回傳用戶評論資訊,目前還不需要,不使用,
評論

(3)音樂資訊

點擊藍色的播放按鈕,發現由多出了一些鏈接,一個一個找下來,
鏈接
https://music.163.com/weapi/v3/song/detail?csrf_token=回傳音樂的詳細資訊,
注意,這里除錯時需要一點技巧,重繪頁面,首先在原始碼里打個斷點
斷點
點擊播放,注意必須是藍色的那個播放按鈕
在這里插入圖片描述
然后除錯獲得api地址https://music.163.com/weapi/v3/song/detail和d
繼續除錯可獲得音樂的下載地址,
地址

# 音樂詳細資訊,包含了音質等級和可下載權限
detail_msg = r'{"id":"473403600","c":"[{\"id\":\"473403600\"}]","csrf_token":""}'
detail_url = 'https://music.163.com/weapi/v3/song/detail?csrf_token='
print(get_data(detail_msg, detail_url))

注意msg要加上r,id為音樂id,可更改
其實沒有這個也行,在搜索時,回傳的資料里就有音樂的詳細資訊了,

(4)下載鏈接

https://music.163.com/weapi/song/enhance/player/url/v1?csrf_token=回傳的是下載鏈接,
下載地址
除錯找到d,

# 音樂下載地址,level代表音質等級,encodeType代表編碼型別,flac可存盤無損音質,目前無法下載無損音樂
# 音質 standard標準 higher較高 exhigh極高 lossless無損 hires
# 編碼型別 aac flac
song_msg = '{"ids":"[473403600]","level":"lossless","encodeType":"flac","csrf_token":""}'
song_url = 'https://music.163.com/weapi/song/enhance/player/url/v1?csrf_token='
print(get_data(song_msg, song_url))

ids里面是音樂id,既然是一個陣列,那想必可以多加幾個音樂id,回傳多個下載地址,level為音質水平,分四個等級,standard代表標準,higher代表較高,exhigh代表極高,lossless代表無損,還有hires,其中lossless和hires都無法下載,不知道加上有會員權限的csrf_token能不能下載,

3、音樂播放器外鏈

在網頁版有一個功能叫生成外鏈播放器,點擊一下
外鏈播放器
它會叫你嵌入一段代碼
嵌入
我們將src里面的內容復制出來,添加上頭部組成https://music.163.com/outchain/player?type=2&id=473403600&auto=1&height=66
你可以嵌入自己的網站中去(如果你的位置支持嵌入ifram的話),也可以自己寫一個前端播放器,然后爬音樂資訊,將資料放進去,
打開開發者工具,這里也有兩個可用的api介面
一個是音樂詳細資訊https://music.163.com/weapi/song/detail
詳情
另外一個是音樂下載地址https://music.163.com/weapi/song/enhance/player/url
在這里插入圖片描述
除錯頁面獲取d

# # 通過外鏈播放器獲取決議的鏈接
# # 音樂下載地址,br代表音質,依舊無法下載無損音樂
# br四個等級 標準128000 較高192000 極高320000 無損999000
song_msg = '{"ids":"[473403600]","br":1052522,"csrf_token":""}'
song_url = 'https://music.163.com/weapi/song/enhance/player/url'
print(get_data(song_msg, song_url))
# 
# # 音樂詳情,有更加詳細的資訊
detail_msg = r'{"id":"473403600","ids":"[\"473403600\"]","limit":10000,"offset":0,"csrf_token":""}'
detail_url = 'https://music.163.com/weapi/song/detail'
print(get_data(detail_msg, detail_url))

回傳的資料和前面的差不多,稍微有點出入,

三、其他api

還記得上面提到過的一段代碼嗎?

Y8Q = Y8Q.replace("api", "weapi");

這段代碼將鏈接里的api換成了weapi,如果我們用原來的鏈接會怎么樣?
以歌詞檔案的api為例
在這里插入圖片描述
補上前綴得到https://music.163.com/api/song/lyric,當然,現在這個鏈接還用不了,需要傳遞引數,
展開query,發現里面一些關于音樂的引數,帶入到鏈接里去,https://music.163.com/api/song/lyric?id=473403600&lv=-1&tv=-1
此請求為get請求,自己獲取回傳資料,
在這里插入圖片描述
有沒有發現這個lv=-1,tv=-1這么像d里面的引數?

d = '{"id":"427419615","lv":-1,"tv":-1,"csrf_token":""}'

對比一下就知道了,
在這里插入圖片描述
幾乎一致,通過更改引數的值,發現回傳的結果,可以知道各個引數的含義,
其他api類似,如果你不想模擬js的加密程序,可以使用這些api直接獲取到資料,
更多api介面請查看介面檔案,注意如果要下載會員音樂和無損音樂(如果有的話)的話,要使用具有會員權限的賬號cookie,post請求放cookies里,get請求放headers里,

四、最終實作代碼

weapi介面的代碼實作

"""
webapi介面
搜索結果:https://music.163.com/weapi/cloudsearch/get/web?csrf_token=(post)
評論:https://music.163.com/weapi/comment/resource/comments/get?csrf_token=
歌詞:https://music.163.com/weapi/song/lyric?csrf_token=
詳情(包括音質):https://music.163.com/weapi/v3/song/detail?csrf_token=
歌曲下載:https://music.163.com/weapi/song/enhance/player/url/v1?csrf_token=

iw233網站決議鏈接
https://iw233.cn/music/?name=コトダマ紬ぐ未來&type=netease

外鏈:http://music.163.com/song/media/outer/url?id=534544522.mp3

音樂外鏈播放器:https://music.163.com/outchain/player?type=2&id=473403600&auto=1&height=66
"""
import base64
import codecs
import json
import math
import random

import requests
from Crypto.Cipher import AES
from urllib3.exceptions import InsecureRequestWarning

requests.packages.urllib3.disable_warnings(InsecureRequestWarning)

'''

var bKB3x = window.asrsea(JSON.stringify(i3x), buU1x(["流淚", "強"]), buU1x(Rg7Z.md), buU1x(["愛心", "女孩", "驚恐", "大笑"]));
            e3x.data = https://www.cnblogs.com/pikeduo/p/j3x.cr3x({
                params: bKB3x.encText,
                encSecKey: bKB3x.encSecKey
            })


    window.asrsea = d,

    d: {"hlpretag":"<span >","hlposttag":"</span>","s":"コトダマ紬ぐ未來","type":"1","offset":"0","total":"true","limit":"30","csrf_token":""}
    e:010001
    f:00e0b509f6259df8642dbc35662901477df22677ec152b5ff68ace615bb7b725152b3ab17a876aea8a5aa76d2e417629ec4ee341f56135fccf695280104e0312ecbda92557c93870114af6c9d05c4f7f0c3685b7a46bee255932575cce10b424d813cfe4875d3e82047b97ddef52741d546b8e289dc6935b3ece0462db0a22b8e7
    g:0CoJUm6Qyw8W8jud
        function d(d, e, f, g) {
        var h = {}
          , i = a(16);
        return h.encText = b(d, g),
        h.encText = b(h.encText, i),
        h.encSecKey = c(i, e, f),
        h
    }

        function a(a) {
        var d, e, b = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789", c = "";
        for (d = 0; a > d; d += 1)
            e = Math.random() * b.length,
            e = Math.floor(e),
            c += b.charAt(e);
        return c
    }
    function b(a, b) {
        var c = CryptoJS.enc.Utf8.parse(b)
          , d = CryptoJS.enc.Utf8.parse("0102030405060708")
          , e = CryptoJS.enc.Utf8.parse(a)
          , f = CryptoJS.AES.encrypt(e, c, {
            iv: d,
            mode: CryptoJS.mode.CBC
        });
        return f.toString()
    }
    function c(a, b, c) {
        var d, e;
        return setMaxDigits(131),
        d = new RSAKeyPair(b,"",c),
        e = encryptedString(d, a)
    }
'''

class wangyiyun:
    def __init__(self):
        self.e = '010001'
        self.f = '00e0b509f6259df8642dbc35662901477df22677ec152b5ff68ace615bb7b725152b3ab17a876aea8a5aa76d2e417629ec4ee341f56135fccf695280104e0312ecbda92557c93870114af6c9d05c4f7f0c3685b7a46bee255932575cce10b424d813cfe4875d3e82047b97ddef52741d546b8e289dc6935b3ece0462db0a22b8e7'
        self.g = '0CoJUm6Qyw8W8jud'

    # 獲取一個隨意字串,length是字串長度
    def generate_str(self, lenght):
        str = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
        res = ''
        for i in range(lenght):
            index = random.random() * len(str)  # 獲取一個字串長度的亂數
            index = math.floor(index)  # 向下取整
            res = res + str[index]  # 累加成一個隨機字串
        return res

    # AES加密獲得params
    def AES_encrypt(self, text, key):
        iv = '0102030405060708'.encode('utf-8')  # iv偏移量
        text = text.encode('utf-8')  # 將明文轉換為utf-8格式
        pad = 16 - len(text) % 16
        text = text + (pad * chr(pad)).encode('utf-8')  # 明文需要轉成二進制,且可以被16整除
        key = key.encode('utf-8')  # 將密鑰轉換為utf-8格式
        encryptor = AES.new(key, AES.MODE_CBC, iv)  # 創建一個AES物件
        encrypt_text = encryptor.encrypt(text)  # 加密
        encrypt_text = base64.b64encode(encrypt_text)  # base4編碼轉換為byte字串
        return encrypt_text.decode('utf-8')

    # RSA加密獲得encSeckey
    def RSA_encrypt(self, str, key, f):
        str = str[::-1]  # 隨機字串逆序排列
        str = bytes(str, 'utf-8')  # 將隨機字串轉換為byte型別的資料
        sec_key = int(codecs.encode(str, encoding='hex'), 16) ** int(key, 16) % int(f, 16)  # RSA加密
        return format(sec_key, 'x').zfill(256)  # RSA加密后字串長度為256,不足的補x

    # 獲取引數
    def get_params(self, d, e, f, g):
        i = self.generate_str(16)    # 生成一個16位的隨機字串
        # i = 'aO6mqZksdJbqUygP'
        encText = self.AES_encrypt(d, g)
        # print(encText)    # 列印第一次加密的params,用于測驗d正確
        params = self.AES_encrypt(encText, i)  # AES加密兩次后獲得params
        encSecKey = self.RSA_encrypt(i, e, f)  # RSA加密后獲得encSecKey
        return params, encSecKey

    # 傳入msg和url,獲取回傳的json資料
    def get_data(self, msg, url):
        encText, encSecKey = self.get_params(msg, self.e, self.f, self.g)   # 獲取引數
        params = {
            "params": encText,
            "encSecKey": encSecKey
        }
        re = requests.post(url=url, params=params, verify=False)    # 向服務器發送請求
        return re.json()    #回傳結果

    # 回傳搜索資料
    def get_search_data(self, s='', type=1, offset=0, total='true', limit=30, csrf_token=''):
        msg = r'{"hlpretag":"<span class=\"s-fc7\">","hlposttag":"</span>",' + f'"s":"{s}","type":"{type}","offset":"{offset}","total":"{total}","limit":"{limit}","csrf_token":"{csrf_token}"' + '}'
        url = f'https://music.163.com/weapi/cloudsearch/get/web?csrf_token={csrf_token}'
        return self.get_data(msg, url)

    # 回傳歌詞資料
    def get_lyric_data(self, id, lv=-1, tv=-1, csrf_token=''):
        msg = '{' + f'"id":"{id}","lv":"{lv}","tv":"{tv}","csrf_token":"{csrf_token}"' + '}'
        url = f'https://music.163.com/weapi/song/lyric?csrf_token={csrf_token}'
        return self.get_data(msg, url)

    # 回傳音樂詳情,包含了音質等級和可下載權限
    def get_detail_data(self, id, csrf_token=''):
        msg = '{' + f'"id":"{id}",' + r'"c":"[{\"id\":\"' + str(id) + r'\"}]",' + f'"csrf_token":"{csrf_token}"' + '}'
        url = f'https://music.163.com/weapi/v3/song/detail?csrf_token={csrf_token}'
        return  self.get_data(msg, url)

    # 回傳下載資料,level代表音質等級,encodeType代表編碼型別,flac可存盤無損音質,目前無法下載無損音樂
    # # 音質 standard標準 higher較高 exhigh極高 lossless無損 hires
    # # 編碼型別 aac flac
    def get_download_data(self, id, level='exhigh', encodeType='flac', csrf_token=''):
        msg = '{' + f'"ids":"{id}","level":"{level}","encodeType":"{encodeType}","csrf_token":"{csrf_token}"' + '}'
        url = f'https://music.163.com/weapi/song/enhance/player/url/v1?csrf_token={csrf_token}'
        return self.get_data(msg, url)

    # 通過播放器外鏈的方式回傳的音樂詳情資料
    def get_detail_outdata(self, id, limit=10000, offset=0, csrf_token=''):
        msg = '{' + f'"id":"{id}",' + r'"ids":"[\"' + str(id) + r'\"]",' + f'"limit":{limit},"offset":{offset},"csrf_token":"{csrf_token}"' + '}'
        url = 'https://music.163.com/weapi/song/detail'
        return self.get_data(msg, url)

    # 通過播放器外鏈的方式回傳的音樂下載資料
    # br代表音質,四個等級 標準128000 較高192000 極高320000 無損999000
    def get_download_outdata(self, id, br=320000, csrf_token=''):
        msg = '{' + f'"ids":"{id}","br":{br},"csrf_token":"{csrf_token}"' + '}'
        url = 'https://music.163.com/weapi/song/enhance/player/url'
        return self.get_data(msg, url)

api介面的代碼實作

'''
api介面
'''

import json
import requests
from urllib3.exceptions import InsecureRequestWarning

requests.packages.urllib3.disable_warnings(InsecureRequestWarning)


class wangyiyun:
    # 獲取資料
    def get_data(self, url, data):
        re = requests.post(url=url, data=https://www.cnblogs.com/pikeduo/p/data, verify=False)
        return re.json()
    # 回傳搜索資料
    def get_search_data(self, s='', type=1, offset=0, total='true', limit=30, csrf_token=''):
        url = 'https://music.163.com/api/cloudsearch/get/web'
        data = https://www.cnblogs.com/pikeduo/p/{'hlpretag': '<span >',
            'hlposttag': '</span>',
            's': s,
            'type': type,
            'offset': offset,
            'total': total,
            'limit': limit,
            'csrf_token': csrf_token
        }
        return self.get_data(url, data)

    # 回傳歌詞資料
    def get_lyric_data(self, id, lv=-1, tv=-1, csrf_token=''):
        url = 'https://music.163.com/api/song/lyric'
        data = https://www.cnblogs.com/pikeduo/p/{'id': id,
            'lv': lv,
            'tv': tv,
            'csrf_token': csrf_token
        }
        return self.get_data(url, data)

    # 回傳音樂詳情,包含了音質等級和可下載權限
    def get_detail_data(self, id, csrf_token=''):
        url = 'https://music.163.com/api/v3/song/detail'
        c = '[{' + f'"id":"{id}"' + '}]'
        data = https://www.cnblogs.com/pikeduo/p/{'id': id,
            'c': c,
            'csrf_token': csrf_token
        }
        return  self.get_data(url, data)

    # 回傳下載資料,level代表音質等級,encodeType代表編碼型別,flac可存盤無損音質,目前無法下載無損音樂
    # # 音質 standard標準 higher較高 exhigh極高 lossless無損 hires
    # # 編碼型別 aac flac
    def get_download_data(self, id, level='exhigh', encodeType='flac', csrf_token=''):
        url = 'https://music.163.com//api/song/enhance/player/url/v1'
        data = https://www.cnblogs.com/pikeduo/p/{'encodeType': encodeType,
            'ids': str(id),
            'level': level,
            'csrf_token': csrf_token
        }
        return self.get_data(url, data)

    # 通過播放器外鏈的方式回傳的音樂詳情資料
    def get_detail_outdata(self, id, limit=10000, offset=0, csrf_token=''):
        url = 'https://music.163.com/api/song/detail'
        data = https://www.cnblogs.com/pikeduo/p/{'id': id,
            'ids': f'[{str(id)}]',
            'limit': limit,
            'offset': offset,
            'csrf_token': csrf_token
        }
        return self.get_data(url, data)

    # 通過播放器外鏈的方式回傳的音樂下載資料
    # br代表音質,四個等級 標準128000 較高192000 極高320000 無損999000
    def get_download_outdata(self, id, br=320000, csrf_token=''):
        url = 'https://music.163.com/api/song/enhance/player/url'
        data = https://www.cnblogs.com/pikeduo/p/{'br': br,
            'ids': str(id),
            'csrf_token': csrf_token
        }
        return self.get_data(url, data)

五、總結

全部程序爬取下來,發現網易云對資料的加密方式還是挺單調的,只要弄懂了原理就好辦了,基本上都一致,
后續考慮做個音樂播放器,添加網易云,QQ,酷狗,百度等源,等做好了再發個教程和專案地址,不過感覺用處也不大,畢竟不能下載vip音樂和無損音樂,就當做是學習了,
小伙伴們有什么不懂的地方可以私信我,也可以在評論區留言,

轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/539304.html

標籤:Python

上一篇:day33-JSON&Ajax01

下一篇:pdf和圖片的處理一記

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 【C++】Microsoft C++、C 和匯編程式檔案

    ......

    uj5u.com 2020-09-10 00:57:23 more
  • 例外宣告

    相比于斷言適用于排除邏輯上不可能存在的狀態,例外通常是用于邏輯上可能發生的錯誤。 例外宣告 Item 1:當函式不可能拋出例外或不能接受拋出例外時,使用noexcept 理由 如果不打算拋出例外的話,程式就會認為無法處理這種錯誤,并且應當盡早終止,如此可以有效地阻止例外的傳播與擴散。 示例 //不可 ......

    uj5u.com 2020-09-10 00:57:27 more
  • Codeforces 1400E Clear the Multiset(貪心 + 分治)

    鏈接:https://codeforces.com/problemset/problem/1400/E 來源:Codeforces 思路:給你一個陣列,現在你可以進行兩種操作,操作1:將一段沒有 0 的區間進行減一的操作,操作2:將 i 位置上的元素歸零。最終問:將這個陣列的全部元素歸零后操作的最少 ......

    uj5u.com 2020-09-10 00:57:30 more
  • UVA11610 【Reverse Prime】

    本人看到此題沒有翻譯,就附帶了一個自己的翻譯版本 思考 這一題,它的第一個要求是找出所有 $7$ 位反向質數及其質因數的個數。 我們應該需要質數篩篩選1~$10^{7}$的所有數,這里就不慢慢介紹了。但是,重讀題,我們突然發現反向質數都是 $7$ 位,而將它反過來后的數字卻是 $6$ 位數,這就說明 ......

    uj5u.com 2020-09-10 00:57:36 more
  • 統計區間素數數量

    1 #pragma GCC optimize(2) 2 #include <bits/stdc++.h> 3 using namespace std; 4 bool isprime[1000000010]; 5 vector<int> prime; 6 inline int getlist(int ......

    uj5u.com 2020-09-10 00:57:47 more
  • C/C++編程筆記:C++中的 const 變數詳解,教你正確認識const用法

    1、C中的const 1、區域const變數存放在堆疊區中,會分配記憶體(也就是說可以通過地址間接修改變數的值)。測驗代碼如下: 運行結果: 2、全域const變數存放在只讀資料段(不能通過地址修改,會發生寫入錯誤), 默認為外部聯編,可以給其他源檔案使用(需要用extern關鍵字修飾) 運行結果: ......

    uj5u.com 2020-09-10 00:58:04 more
  • 【C++犯錯記錄】VS2019 MFC添加資源不懂如何修改資源宏ID

    1. 首先在資源視圖中,添加資源 2. 點擊新添加的資源,復制自動生成的ID 3. 在解決方案資源管理器中找到Resource.h檔案,編輯,使用整個專案搜索和替換的方式快速替換 宏宣告 4. Ctrl+Shift+F 全域搜索,點擊查找全部,然后逐個替換 5. 為什么使用搜索替換而不使用屬性視窗直 ......

    uj5u.com 2020-09-10 00:59:11 more
  • 【C++犯錯記錄】VS2019 MFC不懂的批量添加資源

    1. 打開資源頭檔案Resource.h,在其中預先定義好宏 ID(不清楚其實ID值應該設定多少,可以先新建一個相同的資源項,再在這個資源的ID值的基礎上遞增即可) 2. 在資源視圖中選中專案資源,按F7編輯資源檔案,按 ID 型別 相對路徑的形式添加 資源。(別忘了先把檔案拷貝到專案中的res檔案 ......

    uj5u.com 2020-09-10 01:00:19 more
  • C/C++編程筆記:關于C++的參考型別,專供新手入門使用

    今天要講的是C++中我最喜歡的一個用法——參考,也叫別名。 參考就是給一個變數名取一個變數名,方便我們間接地使用這個變數。我們可以給一個變數創建N個參考,這N + 1個變數共享了同一塊記憶體區域。(參考型別的變數會占用記憶體空間,占用的記憶體空間的大小和指標型別的大小是相同的。雖然參考是一個物件的別名,但 ......

    uj5u.com 2020-09-10 01:00:22 more
  • 【C/C++編程筆記】從頭開始學習C ++:初學者完整指南

    眾所周知,C ++的學習曲線陡峭,但是花時間學習這種語言將為您的職業帶來奇跡,并使您與其他開發人員區分開。您會更輕松地學習新語言,形成真正的解決問題的技能,并在編程的基礎上打下堅實的基礎。 C ++將幫助您養成良好的編程習慣(即清晰一致的編碼風格,在撰寫代碼時注釋代碼,并限制類內部的可見性),并且由 ......

    uj5u.com 2020-09-10 01:00:41 more
最新发布
  • Rust中的智能指標:Box<T> Rc<T> Arc<T> Cell<T> RefCell<T> Weak

    Rust中的智能指標是什么 智能指標(smart pointers)是一類資料結構,是擁有資料所有權和額外功能的指標。是指標的進一步發展 指標(pointer)是一個包含記憶體地址的變數的通用概念。這個地址參考,或 ” 指向”(points at)一些其 他資料 。參考以 & 符號為標志并借用了他們所 ......

    uj5u.com 2023-04-20 07:24:10 more
  • Java的值傳遞和參考傳遞

    值傳遞不會改變本身,參考傳遞(如果傳遞的值需要實體化到堆里)如果發生修改了會改變本身。 1.基本資料型別都是值傳遞 package com.example.basic; public class Test { public static void main(String[] args) { int ......

    uj5u.com 2023-04-20 07:24:04 more
  • [2]SpinalHDL教程——Scala簡單入門

    第一個 Scala 程式 shell里面輸入 $ scala scala> 1 + 1 res0: Int = 2 scala> println("Hello World!") Hello World! 檔案形式 object HelloWorld { /* 這是我的第一個 Scala 程式 * 以 ......

    uj5u.com 2023-04-20 07:23:58 more
  • 理解函式指標和回呼函式

    理解 函式指標 指向函式的指標。比如: 理解函式指標的偽代碼 void (*p)(int type, char *data); // 定義一個函式指標p void func(int type, char *data); // 宣告一個函式func p = func; // 將指標p指向函式func ......

    uj5u.com 2023-04-20 07:23:52 more
  • Django筆記二十五之資料庫函式之日期函式

    本文首發于公眾號:Hunter后端 原文鏈接:Django筆記二十五之資料庫函式之日期函式 日期函式主要介紹兩個大類,Extract() 和 Trunc() Extract() 函式作用是提取日期,比如我們可以提取一個日期欄位的年份,月份,日等資料 Trunc() 的作用則是截取,比如 2022-0 ......

    uj5u.com 2023-04-20 07:23:45 more
  • 一天吃透JVM面試八股文

    什么是JVM? JVM,全稱Java Virtual Machine(Java虛擬機),是通過在實際的計算機上仿真模擬各種計算機功能來實作的。由一套位元組碼指令集、一組暫存器、一個堆疊、一個垃圾回收堆和一個存盤方法域等組成。JVM屏蔽了與作業系統平臺相關的資訊,使得Java程式只需要生成在Java虛擬機 ......

    uj5u.com 2023-04-20 07:23:31 more
  • 使用Java接入小程式訂閱訊息!

    更新完微信服務號的模板訊息之后,我又趕緊把微信小程式的訂閱訊息給實作了!之前我一直以為微信小程式也是要企業才能申請,沒想到小程式個人就能申請。 訊息推送平臺🔥推送下發【郵件】【短信】【微信服務號】【微信小程式】【企業微信】【釘釘】等訊息型別。 https://gitee.com/zhongfuch ......

    uj5u.com 2023-04-20 07:22:59 more
  • java -- 緩沖流、轉換流、序列化流

    緩沖流 緩沖流, 也叫高效流, 按照資料型別分類: 位元組緩沖流:BufferedInputStream,BufferedOutputStream 字符緩沖流:BufferedReader,BufferedWriter 緩沖流的基本原理,是在創建流物件時,會創建一個內置的默認大小的緩沖區陣列,通過緩沖 ......

    uj5u.com 2023-04-20 07:22:49 more
  • Java-SpringBoot-Range請求頭設定實作視頻分段傳輸

    老實說,人太懶了,現在基本都不喜歡寫筆記了,但是網上有關Range請求頭的文章都太水了 下面是抄的一段StackOverflow的代碼...自己大修改過的,寫的注釋挺全的,應該直接看得懂,就不解釋了 寫的不好...只是希望能給視頻網站開發的新手一點點幫助吧. 業務場景:視頻分段傳輸、視頻多段傳輸(理 ......

    uj5u.com 2023-04-20 07:22:42 more
  • Windows 10開發教程_編程入門自學教程_菜鳥教程-免費教程分享

    教程簡介 Windows 10開發入門教程 - 從簡單的步驟了解Windows 10開發,從基本到高級概念,包括簡介,UWP,第一個應用程式,商店,XAML控制元件,資料系結,XAML性能,自適應設計,自適應UI,自適應代碼,檔案管理,SQLite資料庫,應用程式到應用程式通信,應用程式本地化,應用程式 ......

    uj5u.com 2023-04-20 07:22:35 more