网易云音乐API分析

2018/08/01 python

网易云音乐官方API有好几个,而且请求参数都用到了AES加密,这里是一些简单总结,包括怎么去分析不同的API请求参数的加密过程,也汇集了一些解析贴


简要分析

关于weapi

weapi就是网易云音乐网页版用的API

如图所示,关于某首歌的热门评论:

请求链接:

https://music.163.com/weapi/v1/resource/comments/R_SO_4_27759600?csrf_token=

请求方式为POST,重点在请求参数Form Data:

params: ijv7HcA5CRxI/lWUPv5o2jvLmv6JPYcA1HLDDS7PZwaIUzuybQTrpCSuPF8+o18gIadNpC4NA4zzTbAlUYTZ9lBGEo3JhkiDGwELaPrt52KjXm0j62P80s389dYCDxQD/sRGAzWS0rHueCg99+PreKnUVH5Jc3BRPBohqVjxWCB04SFqOOqzstoQaFanUIal
encSecKey: dfaac2123ed10285b18d876c3355933aea8f0cc74865f6f7e98f996dbb4aacb90c0c84821eada73a84c8da2ef20ca184da4b4d26ac5c92ec657f0eef9794038e28e9419710c65a151855426f0bdef5318cafc2c500e0e6dfbe901d2ccb8f9f002640cf7023d455716727026c3a09aaa7b8b1d3f69608c767466eb18d0bd25aac

两个请求参数都被加密了,我们要想获得这个请求链接返回的数据,就需要在本地模拟这个加密过程,要想分析其加密过程,可以用Fiddler等软件进行抓包,如文末帖子里面的操作,大致可以知道,weapi的请求参数params进行了两次的AES加密

关于eapi

eapi就是目前网易云音乐PC版本、Android版本用到的API

eapi分析过程同上,eapi的请求参数params进行了一次的AES加密

关于weapi请求参数

比如请求评论的类型,评论页数,等等请求参数,都可以通过fiddler抓包分析来获取,每一种请求链接一般都有不同的请求参数,可以参考NeteaseCloudMusicAPI项目里的module模块

在我的netease_music_cat项目的netease模块 first_param.py,也实现了一小部分请求参数

weapi和eapi的python模拟加密

在我的netease_music_cat项目的netease模块,实现了weapi和eapi的加密过程

# !/usr/bin/env python
# -*- coding: utf-8 -*-

# author: humingk
# ----------------------
import hashlib
from binascii import b2a_hex
import config
import base64
from Crypto.Cipher import AES
from my_tools.logger_tool import loggler_tool

logger = loggler_tool()


class form_data:
    """
    AES加密请求参数类
        利用fiddler分析网易云音乐中的core.js对请求表单参数的加密过程
        此模块模拟对请求表单参数的两次AES加密

    """

    def __get_weapi_params(self, first_param):
        """
        获取weapi Form Data中的parms(通过两次AES加密)

        :param first_param: 第一个参数字典(由first_param获取)
        :return: 两次加密后的params
        """
        params = self.__AES_encrypt(mode=AES.MODE_CBC, param=first_param, key=config.first_key, iv=config.iv)
        params = str(base64.b64encode(params))[2:-1]
        params = self.__AES_encrypt(mode=AES.MODE_CBC, param=params, key=config.second_key, iv=config.iv)
        return str(base64.b64encode(params))[2:-1]

    def __get_eapi_params(self, eapi_url, first_param):
        """
        获取eapi Form Data中的params(通过一次AES加密)

        :param eapi_url: 请求url 字符串
        :param first_param:  第一个参数字典 字符串
        :return: 一次加密后的params
        """
        first_param = first_param.encode()
        eapi_url = eapi_url.encode()
        params = b'nobody' + eapi_url + b'use' + first_param + b'md5forencrypt'
        md5 = hashlib.md5()
        md5.update(params)
        params = eapi_url + b'-36cd479b6b5-' + first_param + b'-36cd479b6b5-' + md5.hexdigest().encode()
        pad = 16 - len(params) % 16
        params = params + bytearray([pad] * pad)
        crypt = AES.new(config.eapikey, AES.MODE_ECB)
        params = crypt.encrypt(params)
        return b2a_hex(params).upper()

    def __get_encSecKey(self):
        """
        获取Form Data中的encSeckey(已配合param加密)

        :return: encSecKey
        """
        return config.encSecKey

    def __AES_encrypt(self, mode, param, key, iv):
        """
        AES加密过程

        :param mode: AES模式
        :param param: AES加密对象
        :param key: AES加密秘钥
        :param iv: AES秘钥偏移量
        :return: AES加密结果
        """
        # 这里不能使用len(param),因为此处长度指标不能用中文,应该先转化为unicode
        pad = 16 - len(param.encode()) % 16
        param = param + pad * chr(pad)
        encryptor = AES.new(key, mode, iv)
        encrypt_text = encryptor.encrypt(param)
        return encrypt_text

    def get_form_data(self, first_param, api_type=config.api_weapi, eapi_url=None):
        """
        获取请求参数

        :param first_param: 第一个请求字典
        :param api_type: api类型
        :param eapi_url: eapi的请求链接
        :return:
        """
        if api_type == config.api_weapi:
            return {
                "params": self.__get_weapi_params(first_param),
                "encSecKey": self.__get_encSecKey()
            }
        elif api_type == config.api_eapi:
            return {
                "params": self.__get_eapi_params(eapi_url=eapi_url, first_param=first_param)
            }
        else:
            return None

其他的还有LinuxApi等等

相关帖子汇总

API服务提供

关于weapi

关于eapi

目前只有eapi能够获取到所有的热门评论,weapi只能获取第一页的热门评论