Python扫码登录保存和验证cookies值——网易云音乐篇(九)

一、打开二维码扫码登录页面,找到二维码图片链接

  • 本篇幅度比较长,有点难度,不过经过小编反复测试,终于找到规律完成扫码操作和获取登录cookie信息。稍微加了一些图片来说明吧。
  • 先找到生成二维码的链接,打开登录页面:https://music.163.com/#/login
  • 点击二维码右键鼠标,选择检查选项,每个浏览器不一样,小编用的是谷歌浏览器,如下图:
    在这里插入图片描述
  • 找到了二维码链接:http://music.163.com/login?codekey=9582af31-7ac9-4863-a83b-0764b11e08e3
  • 发现变量参数codekey,下一步就去获取
    在这里插入图片描述
    在这里插入图片描述
  • 通过:https://music.163.com/weapi/login/qrcode/unikey?csrf_token=。这个可以获取unikey值

二、破解获取params和encSecKey值

  • 上面我们知道了获取unikey值的链接,但是访问此链接必须要有两个参数params和encSecKey值
    在这里插入图片描述
  • 要获取这两个参数,小编花费不少时间~
  • 搜索其中一个参数encSecKey,双击下面链接
    在这里插入图片描述
  • 优化js代码,CTRL+F继续进行搜索encSecKey值,找到这两个值生成方式
    在这里插入图片描述
    在这里插入图片描述
  • 通过上面代码可以看出我们需要知道bWv7o的值,这个值由下面这个四组合的
(JSON.stringify(i6c), bsK7D(["流泪", "强"]), bsK7D(XR0x.md), bsK7D(["爱心", "女孩", "惊恐", "大笑"])
  • 首先获取i6c的值,通过断点可以知道
    在这里插入图片描述
i6c: {key: "1b601cd2-960c-4ee5-b1c6-e90dcc5a3f59", type: "1", csrf_token: ""}
  • 再者**bsK7D([“流泪”, “强”])**的值,同上面方法,点击红色块跳另一个地方,进行断点运行
    在这里插入图片描述
    在这里插入图片描述
Return value: "010001"
  • 然后**bsK7D(XR0x.md)**的值,再点击运行,就可以出现Return value值
    在这里插入图片描述
Return value: "00e0b509f6259df8642dbc35662901477df22677ec152b5ff68ace615bb7b725152b3ab17a876aea8a5aa76d2e417629ec4ee341f56135fccf695280104e0312ecbda92557c93870114af6c9d05c4f7f0c3685b7a46bee255932575cce10b424d813cfe4875d3e82047b97ddef52741d546b8e289dc6935b3ece0462db0a22b8e7"
  • 最后一个值bsK7D([“爱心”, “女孩”, “惊恐”, “大笑”]
    在这里插入图片描述
Return value: "0CoJUm6Qyw8W8jud"
  • 四个值获取到了
  • i6c: {key: “1b601cd2-960c-4ee5-b1c6-e90dcc5a3f59”, type: “1”, csrf_token: “”}
  • Return value: "010001"
  • Return value: "00e0b509f6259df8642dbc35662901477df22677ec152b5ff68ace615bb7b725152b3ab17a876aea8a5aa76d2e417629ec4ee341f56135fccf695280104e0312ecbda92557c93870114af6c9d05c4f7f0c3685b7a46bee255932575cce10b424d813cfe4875d3e82047b97ddef52741d546b8e289dc6935b3ece0462db0a22b8e7"
  • Return value:"0CoJUm6Qyw8W8jud"
  • key值是变量值,其他值都是固定值,下面在vipcashier.umd.js中找到获取key值的方法,就是时间加随机加固定值组合而成
        function S() {
            var e = (new Date).getTime();
            return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function(t) {
                var n = (e + 16 * Math.random()) % 16 | 0;
                return e = Math.floor(e / 16),
                ("x" === t ? n : 7 & n | 8).toString(16)
            })
        }

-上面四个参数获取到了,还有一个大包围window.asrsea

var bWv7o = window.asrsea(JSON.stringify(i6c), bsK7D(["流泪", "强"]), bsK7D(XR0x.md), bsK7D(["爱心", "女孩", "惊恐", "大笑"]));

在这里插入图片描述
在这里插入图片描述

  • d,e,f,g就是上面那四个参数的按步骤获取的参数,这里断点运行之后直接就可以看到
d: "{"key":"1b601cd2-960c-4ee5-b1c6-e90dcc5a3f59","type":"1","csrf_token":""}"
e: "010001"
f: "00e0b509f6259df8642dbc35662901477df22677ec152b5ff68ace615bb7b725152b3ab17a876aea8a5aa76d2e417629ec4ee341f56135fccf695280104e0312ecbda92557c93870114af6c9d05c4f7f0c3685b7a46bee255932575cce10b424d813cfe4875d3e82047b97ddef52741d546b8e289dc6935b3ece0462db0a22b8e7"
g: "0CoJUm6Qyw8W8jud"
  • 通过上面这段代码可以看出,这里又继续加密了

进一步化简:
i = a(16)
h.encText = b(d, g)
h.encText = b(h.encText, i)
h.encSecKey = c(i, e, f)
大概就这个意思
encText = b(b(d, g), i)
encSecKey = c(i, e, f)

  • d, e, f, g的值我们获取到了,下面首先获取 i
  • 获取 i 值:i = a(16),同上方法进行寻找

在这里插入图片描述
在这里插入图片描述

  • i值获取方式,将i = a(16)带入该函数单独随机生成,没其他函数参与
    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
    }

可以看出这就是由这段代码随机生成的16位值

  • 接着就是b(b(d, g), i),这个函数AES加密的

在这里插入图片描述
在这里插入图片描述

  • 从上面可以看出iv=0102030405060708,这是偏移量,固定值
iv:0102030405060708
  • 将a,b值带入函数中生成新的参数,是b(d, g),变成b(a, b),其实就是a就是d值,g就是b值
d: "{"key":"1b601cd2-960c-4ee5-b1c6-e90dcc5a3f59","type":"1","csrf_token":""}"
g: "0CoJUm6Qyw8W8jud"
  • 将b(b(d, g), i)函数代码转换成python代码,b函数加密了两次
def keys(key):
    while len(key) % 16 != 0:
        key += '\0'
    return str.encode(key)
#AES解密
def AES_aes(t, key, iv):
    p = lambda s: s + (AES.block_size - len(s) % AES.block_size) * chr(AES.block_size - len(s) % AES.block_size)
    encrypt = str(base64.encodebytes(AES.new(keys(key), AES.MODE_CBC,keys(iv)).encrypt(str.encode(p(t)))), encoding='utf-8')
    return encrypt
#将上面参数d, g, iv带入其中就行
def params(): #b(b(d, g), i)和下面是不是很相似
    return AES_aes(AES_aes(d, g, iv), i, iv)   #g 和 i 都是key代替
  • 再接着就是:encSecKey = c(i, e, f),同上办法,其实就在上面这段

在这里插入图片描述
在这里插入图片描述

  • 这个比较简单,加了一次密是RSA加密,把它转换成python代码
  • 把 i, e, f 参数带入其运行就行
def RSA_rsa(i, e, f):
    return format(int(codecs.encode(i[::-1].encode('utf-8'), 'hex_codec'), 16) ** int(e, 16) % int(f, 16), 'x').zfill(256)
    
def encSecKey():
    return RSA_rsa(i, e, f)
  • 到此这两个参数值就算出来了
def params():
    return AES_aes(AES_aes(d, g, iv), i, iv) 
def encSecKey():
    return RSA_rsa(i, e, f)

注意:from Crypto.Cipher import AES

  • 小编再使用crypto库的时候,老是显示错误,最后再网上找到解决办法

解决方法:
安装crypto后,将\venv\Lib\site-packages\crypto的crypto文件夹名首字母c改成大写C就可以了

三、进行扫码操作,找到确认链接

  • 上面我们找到生成二维码的链接和params、encSecKey两个参数,下面就进行访问
#访问生成二维码网址的d值是这样的,其他不变,下面的确认网址的d值有所不一样
#key上面已经说了,这里的key值是随机生成的
d = str({'key': key, 'type': "1", 'csrf_token': ""})
def params():
    return AES_aes(AES_aes(d, g, iv), i, iv) 
def encSecKey():
    return RSA_rsa(i, e, f)
getlogin = session.post('https://music.163.com/weapi/login/qrcode/unikey?csrf_token=', data={'params': params(), 'encSecKey': encSecKey()}, headers=headers).json()
#输出getlogin = {'code': 200, 'unikey': '3d99……'}
  • 通过识别登录页面二维码的隐藏文字信息,是这样的规则

https://music.163.com/login?codekey=52f86249-fa48-4914-a324-0df788108e05&refer=scan

  • 所以codekey就是unikey值,接着就是把unikey带入该网址用python的qrcode库转换成二维码进行扫码操作
pngurl = 'https://music.163.com/login?codekey='+getlogin['unikey']+'&refer=scan'
qr = qrcode.QRCode()
qr.add_data(pngurl)
img = qr.make_image()
# 缓存的好处就是不需要保存本地
a = BytesIO()
img.save(a, 'png')
png = a.getvalue()
a.close()
# 打开二维码进行扫码操作
t = showpng(png)
t.start()
  • 接着找到确认网址,进行测试登录,确认网址一般输出都是:等待扫码、未扫码等字样,比较好找,下面找到以后进行登录测试结果
tokenurl = 'https://music.163.com/weapi/login/qrcode/client/login?csrf_token='
while 1:
    u = str({'key': getlogin['unikey'], 'type': "1", 'csrf_token': ""})
    qrcodedata = session.post(tokenurl, data={'params': AES_aes(AES_aes(u, g, iv), i, iv), 'encSecKey': encSecKey()}, headers=headers).json()
    if '801' in str(qrcodedata['code']):
        print('二维码未失效,请扫码!')
    elif '802' in str(qrcodedata['code']):
        print('已扫码,请确认!')
    elif '803' in str(qrcodedata['data']['code']):
        print('已确认,登入成功!')
        break
    else:
        print('其他:', qrcodedata)
    time.sleep(2)
  • 上面小编说过,生成二维码网址和确认网址的d值不一样,这我用u来代替,这里小编猜测了很久,测试很久,才成功的。其实也很简单,就是这里的key不是随机的了,而是unikey值,最后成功登录。
  • 登录成功之后就获取了cookie,没其他网址链接了。

四、判断cookie是否有效

  • 这里小编页研究了很久,发现登录以后每个网址后面都带有

csrf_token=53b3e139d912……

在这里插入图片描述

  • csrf_token值就是登录以后获取的cookie值,小编就将csrf_token值从cookie提取出来,放入网址进行判断
def islogin(session):
    try:
        session.cookies.load(ignore_discard=True)
    except Exception:
        pass
        #从cookie提取csrf_token值
    csrf_token = session.cookies.get('__csrf')
    c = str({'csrf_token': csrf_token})
    try:
        loginurl = session.post('https://music.163.com/weapi/w/nuser/account/get?csrf_token={}'.format(csrf_token), data={'params': AES_aes(AES_aes(c, g, iv), i, iv), 'encSecKey': encSecKey()}, headers=headers).json()
        if '200' in str(loginurl['code']):
            print('Cookies值有效:',loginurl['profile']['nickname'],',已登录!')
            return session, True
        else:
            print('Cookies值已经失效,请重新扫码登录!')
            return session, False
    except:
        print('Cookies值已经失效,请重新扫码登录!')
        return session, False
  • 该网址的d也所变化,小编用c来代替,就放csrf_token值了,上面几个访问链接不需要放。如果还没登录,cookie就没有,就提不了csrf_token值,就报错不运行了,所以小编就使用tuy双判断进行,测试成功!

五、其他文件

  • 要运行本篇的完整代码,就需要这两个文件agent.py和jsdm.js,同下面完整代码文件放同一根目录就行。
  • agent.py文件完整代码。这里除了和这篇有关,前几篇文章运行也需要这两个文件
# -*- coding: UTF-8 -*-
import random
import random
import execjs
agent = [
    "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2228.0 Safari/537.36",
    "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2227.1 Safari/537.36",
    #可以多添加几个   
]

# 获取浏览器认证头
def get_user_agents():
    return random.choice(agent)
# 读取js
def djs(js):
    f = open(js, 'r', encoding='utf-8')
    jst = ''
    while True:
        readline = f.readline()
        if readline:
            jst += readline
        else:
            break
    return jst
def getjs():
    return djs('jsdm.js')

# 获取ptqrtoken
def ptqrtoken(qrsign):
    # 加载js
    execjs_execjs = execjs.compile(getjs())
    return execjs_execjs.call('hash33', qrsign)
# 获取UI
def guid():
    # 加载js
    execjs_execjs = execjs.compile(getjs())
    return execjs_execjs.call('guid')
# 获取g_tk
def get_g_tk(p_skey):
    # 加载js
    execjs_execjs = execjs.compile(getjs())
    return execjs_execjs.call('getToken', p_skey)
# 获取i
def S():
    # 加载js
    execjs_execjs = execjs.compile(getjs())
    return execjs_execjs.call('S')
# 获取key
def a():
    # 加载js
    execjs_execjs = execjs.compile(getjs())
    return execjs_execjs.call('a', 16)
  • jsdm.js 文件完整代码
function hash33(t) {
    for (var e = 0, i = 0, n = t.length; i < n; ++i)
        e += (e << 5) + t.charCodeAt(i);
    return 2147483647 & e
}

function getToken(p_skey) {
    var str = p_skey || '',
        hash = 5381;
    for (var i = 0, len = str.length; i < len; ++i) {
        hash += (hash << 5) + str.charCodeAt(i);
    }
    return hash & 0x7fffffff;
}

function guid() {
    return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
        var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8);
        return v.toString(16);
    }).toUpperCase();
};


function S() {
    var e = (new Date).getTime();
    return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function(t) {
        var n = (e + 16 * Math.random()) % 16 | 0;
        return e = Math.floor(e / 16),
            ("x" === t ? n : 7 & n | 8).toString(16)
    })
}


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
}

六、保存cookies值并进行验证完整代码

完整代码

# -*- coding: utf-8 -*-
import base64
import codecs
import pickle
from Crypto.Cipher import AES     
#解决方法:安装crypto后,将\venv\Lib\site-packages\crypto的crypto文件夹名首字母c改成大写C就可以了
import qrcode
import agent
from threading import Thread
import time
import requests
from io import BytesIO
from PIL import Image
import os
requests.packages.urllib3.disable_warnings()
headers = {'User-Agent': agent.get_user_agents(),'Referer':'https://music.163.com/'}


class showpng(Thread):
    def __init__(self, data):
        Thread.__init__(self)
        self.data = data

    def run(self):
        img = Image.open(BytesIO(self.data))
        img.show()

#解密params和encSecKey值
def keys(key):
    while len(key) % 16 != 0:
        key += '\0'
    return str.encode(key)

def AES_aes(t, key, iv):
    p = lambda s: s + (AES.block_size - len(s) % AES.block_size) * chr(AES.block_size - len(s) % AES.block_size)
    encrypt = str(base64.encodebytes(AES.new(keys(key), AES.MODE_CBC,keys(iv)).encrypt(str.encode(p(t)))), encoding='utf-8')
    return encrypt

def RSA_rsa(i, e, f):
    return format(int(codecs.encode(i[::-1].encode('utf-8'), 'hex_codec'), 16) ** int(e, 16) % int(f, 16), 'x').zfill(256)

#获取的参数
key = agent.S() # i6c的值

d = str({'key': key, 'type': "1", 'csrf_token': ""})
e = "010001"# (["流泪", "强"])的值
f = "00e0b509f6259df8642dbc35662901477df22677ec152b5ff68ace615bb7b725152b3ab17a876aea8a5aa76d2e417629ec4ee341f56135fccf695280104e0312ecbda92557c93870114af6c9d05c4f7f0c3685b7a46bee255932575cce10b424d813cfe4875d3e82047b97ddef52741d546b8e289dc6935b3ece0462db0a22b8e7"
g = "0CoJUm6Qyw8W8jud" # (["爱心", "女孩", "惊恐", "大笑"])的值

iv = "0102030405060708"  # 偏移量
i = agent.a()  # 随机生成长度为16的字符串

def params():
    return AES_aes(AES_aes(d, g, iv), i, iv)   #g 和 i 都是key代替

def encSecKey():
    return RSA_rsa(i, e, f)

#判断cookie是否有效
def islogin(session):
    try:
        session.cookies.load(ignore_discard=True)
    except Exception:
        pass
    csrf_token = session.cookies.get('__csrf')
    c = str({'csrf_token': csrf_token})
    try:
        loginurl = session.post('https://music.163.com/weapi/w/nuser/account/get?csrf_token={}'.format(csrf_token), data={'params': AES_aes(AES_aes(c, g, iv), i, iv), 'encSecKey': encSecKey()}, headers=headers).json()
        if '200' in str(loginurl['code']):
            print('Cookies值有效:',loginurl['profile']['nickname'],',已登录!')
            return session, True
        else:
            print('Cookies值已经失效,请重新扫码登录!')
            return session, False
    except:
        print('Cookies值已经失效,请重新扫码登录!')
        return session, False

#登录扫码保存cookie
def wyylogin():
    # 写入
    session = requests.session()
    if not os.path.exists('wyycookies.cookie'):
        with open('wyycookies.cookie', 'wb') as f:
            pickle.dump(session.cookies, f)
    # 读取
    session.cookies = pickle.load(open('wyycookies.cookie', 'rb'))
    session, status = islogin(session)
    if not status:
        getlogin = session.post('https://music.163.com/weapi/login/qrcode/unikey?csrf_token=', data={'params': params(), 'encSecKey': encSecKey()}, headers=headers).json()
        pngurl = 'https://music.163.com/login?codekey=' + getlogin['unikey'] + '&refer=scan'
        qr = qrcode.QRCode()
        qr.add_data(pngurl)
        img = qr.make_image()
        # 缓存的好处就是不需要保存本地
        a = BytesIO()
        img.save(a, 'png')
        png = a.getvalue()
        a.close()
        # 打开二维码进行扫码操作
        t = showpng(png)
        t.start()
        tokenurl = 'https://music.163.com/weapi/login/qrcode/client/login?csrf_token='
        while 1:
            u = str({'key': getlogin['unikey'], 'type': "1", 'csrf_token': ""})
            qrcodedata = session.post(tokenurl, data={'params': AES_aes(AES_aes(u, g, iv), i, iv), 'encSecKey': encSecKey()}, headers=headers).json()
            if '801' in str(qrcodedata['code']):
                print('二维码未失效,请扫码!')
            elif '802' in str(qrcodedata['code']):
                print('已扫码,请确认!')
            elif '803' in str(qrcodedata['code']):
                print('已确认,登入成功!')
                break
            else:
                print('其他:', qrcodedata)
            time.sleep(2)
        with open('wyycookies.cookie', 'wb') as f:
            pickle.dump(session.cookies, f)
    return session
    
if __name__ == '__main__':
    wyylogin()
  • 网易云音乐每个链接网址访问都需要params和encSecKey值这两个值,所以获取MP3地址也需要这两个值,只是访问链接和D值参数不一样,后期小编会开设采集数据专栏包括音乐视频等数据!

七 、 更多文章

  1. 抖音篇(一)
  2. 快手篇(二)
  3. 微视篇(三)
  4. 微信公众号篇(四)
  5. 微博篇(五))
  6. B站篇(六))
  7. CSDN篇(八))
  • 后期小编将开设登录后批量采集各平台数据(点赞、播放量、评论、图片、视频、音乐等)专栏文章!记得关注哟!😜
  • 如果文章能帮到您,愿意给小编点个 👍 吗,么么哒~😘 (●’◡’●)
评论 13
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值