JS逆向-某易云音乐下载器

news2025/5/22 2:49:58

文章目录

  • 介绍
  • 下载链接
  • Robots文件
  • 搜索功能
  • JS逆向
      • **函数a:生成随机字符串**
      • **函数b:AES-CBC加密**
      • **函数c:RSA公钥加密**
  • 歌曲下载
  • 总结

介绍

在某易云音乐中,很多歌曲听是免费的,但下载需要VIP,此程序旨在“可听”则可下,只要能够听的歌,都可以下载下来。

而此篇博客则着重讲此代码是如何编写的,如果只想要下载工具,请自行查看下载链接。

注意,此程序仅做学习参考,任何违法行为与作者无关。

下载链接

GitHub:https://github.com/13337356453/163Music
CSDN:https://download.csdn.net/download/realmels/90855703

Robots文件

养成好习惯,爬虫之前,先看robots文件。
在这里插入图片描述
禁止爬取

/prime/m/gift-receive

文件,刚好,我们这次不爬取此文件。

搜索功能

在某易云音乐中提供了搜索功能,可以通过关键词搜索歌曲。
在这里插入图片描述
而我们的程序需要通过用户关键词来搜索内容,因此需要首先从搜索功能开始爬取。

在这里通过歌曲abc为例,进行搜索功能的分析与实现。

在搜索栏输入关键字abc,按下回车键得到搜索结果。
在这里插入图片描述
按下F12进入开发者工具,选中Network,点击XHR查看Ajax的请求包

刷新界面,出现了很多请求包
在这里插入图片描述
逐个查看请求包的Response,寻找搜索歌曲的请求包。

在路径为

/weapi/cloudsearch/get/web

的请求包中,我们可以看到歌曲的搜索信息。

在这里插入图片描述
可以确定这个就是搜索歌曲的请求包。

接下来分析请求参数。

在这里插入图片描述
这是一个POST请求,请求的URL为:https://music.163.com/weapi/cloudsearch/get/web
,POST的参数如下:

params=NM406wjNCdicjbT3ZSuA7X6ICkCBzfN6rA1KAeZR4Pmpv6VhTT8jKqR/ZRpGitFCp77TipjrdwPahGWiMjGf2cLrREp5Gadgeseo9l9+IMfrwG/JmgDfW9pLZaRIagi+MSESFlTJnlH3vJI7YbqwWPjgfbyzBX0sgw4l3IXxfuamFggnztM3DlEhB1uCBUPqEpDFlsMW8pPdEPtaJS47Y2DMXBY/SJVOa8Y3nD/rjP2lhw5sXaZ3qs5LEwmYKiriEAaZ6fRmKJb4Vn6Ay6ELCBWL74DqjF4BPh8GsEPicXdah/nR0BPoM+suZbKCICMK
encSecKey=8b987300e7f1a5e657c7a69d3c9ff9a4d9ab1c3dc1b2328df473600103b1fe18aa4898008ac9d03074c2cb560543ac22740c4c0c1f7d2c14535f938344a999224733d97e75ffdc26dc2a7ac9afff302f127b29ee762ce02061b9ce89ad2b9006938ed0e62667bfbac656b12adbe951ccff0d02dacd27fb94de7473fa933043ec

别的都容易理解,这两个参数是什么呢?

我们从JS文件中找答案。

JS逆向

在这里先科普基本知识。一个网站,分为前端和后端,前端负责展现好看的页面,后端负责处理繁杂的数据。用户通过前端界面,将数据发送到后端,后端代码处理数据,将相应的结果发送到前端,呈现在用户眼前。这个过程通过request/response,实现。因此,前端所有奇形怪状的参数,都可以在前端代码中找到蛛丝马迹。毕竟,后端代码是不可见的,而前端代码是公开的。

首先需要明白paramsencSecKey这两个参数是如何产生的,这里我们复制encSecKey,在开发者工具中选中Sources,按下Ctrl+Shift+F进行全局搜索。
在这里插入图片描述
在这里我们逐个进行分析,首先打开这个core开头的JS文件,顾名思义,core就是核心,因此最可能有我们想要的数据。

打开文件后按下Ctrl+F进行文件内搜索,搜索encSecKey,查询到三个结果。
在这里插入图片描述
我们分别进行分析。

在第二次出现的地方,我们需要的两个参数:paramsencsecKey同时出现
在这里插入图片描述
怀疑这里有参数的生成逻辑,打一个断点。
在这里插入图片描述
刷新页面,程序运行到断点处停止。
在这里插入图片描述
可以看到,paramsencsecKey这两个参数分别是bYE4I这个对象的两个属性。而bYE4I这个对象是由window.asrsea这个函数生成的。

我们把光标放到window.asrsea这个函数上,查看这个函数的真实面目。
在这里插入图片描述
跳转到函数d
在这里插入图片描述
我们可以看到,函数d中调用了函数a,b,c。正好这三个参数都在d函数的附近,我们一起复制过来查看。

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)
    }
    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函数中打上断点,分析d函数的参数
在这里插入图片描述
一直点击上方的下一步,同时眼睛观察着右边的实时参数值。
在这里插入图片描述
在这里插入图片描述
多走几步,我们会发现函数d接受的四个参数中,efg都是固定值,值如下:

e=010001
f=00e0b509f6259df8642dbc35662901477df22677ec152b5ff68ace615bb7b725152b3ab17a876aea8a5aa76d2e417629ec4ee341f56135fccf695280104e0312ecbda92557c93870114af6c9d05c4f7f0c3685b7a46bee255932575cce10b424d813cfe4875d3e82047b97ddef52741d546b8e289dc6935b3ece0462db0a22b8e7
g=0CoJUm6Qyw8W8jud

参数d的值却一直在变化。

我们一直点下一步的按钮,同时盯着参数,点着点着,会发现右侧的d参数发生变化,大多数时候都是csrf_token的值,我们不用管,但偶尔会出现别的内容,而大概点了几十次后,参数会变成:

d="{\"hlpretag\":\"<span class=\\\"s-fc7\\\">\",\"hlposttag\":\"</span>\",\"s\":\"abc\",\"type\":\"1\",\"offset\":\"0\",\"total\":\"true\",\"limit\":\"30\",\"csrf_token\":\"\"}"

在其中,出现了我们的关键词abc,以及出现歌曲的数量30

确定d函数就是生成参数的逻辑。

接下来仔细分析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
    }

首先定义了一个空对象h,然后通过a函数生成内容,赋值给i,接下来通过b函数生成第一个encText,赋给h的属性,接受参数dg,再用b函数二次处理encText,参数为第一次处理的encTexti,这就是提交时所需的params;同时用c函数生成encsecKey,接受参数ief

接下来我们逐个分析abc函数

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)
    }

人与动物的区别就是人会使用工具,我们直接问deepseek

在这里插入图片描述
在这里插入图片描述
这是deepseek的回答。


函数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
}
  • 功能:生成指定长度的随机字符串,字符集包括大小写字母和数字。
  • 参数a 控制字符串长度。
  • 实现细节
    • 循环 a 次,每次从字符池 b 中随机选取一个字符。
    • 使用 Math.random() 生成随机索引,可能不够安全(非密码学安全随机数)。
  • 安全问题
    • Math.random() 在浏览器中可能被预测,建议改用 crypto.getRandomValues()
  • 用途:生成临时密钥、Nonce 或盐值。

函数b:AES-CBC加密

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()
}
  • 功能:使用 AES-CBC 模式加密数据,密钥由参数提供。
  • 参数
    • a:明文数据(UTF-8 字符串)。
    • b:加密密钥(UTF-8 字符串)。
  • 实现细节
    • 固定初始化向量(IV)0102030405060708
    • 使用 CryptoJS 库进行加密,返回 Base64 编码的密文。
  • 安全问题
    • 固定IV:CBC 模式要求每次加密使用随机 IV,否则可能导致明文模式泄露。
    • 密钥建议使用二进制格式(如 PBKDF2 派生),而非直接字符串。
  • 用途:加密传输敏感数据。

函数c:RSA公钥加密

function c(a, b, c) {
    var d, e;
    return setMaxDigits(131),
    d = new RSAKeyPair(b,"",c),
    e = encryptedString(d, a)
}
  • 功能:使用 RSA 公钥加密数据。
  • 参数
    • a:明文数据(字符串或数值)。
    • b:公钥指数(通常为 "10001" 十六进制)。
    • c:RSA 模数(n)。
  • 实现细节
    • setMaxDigits(131) 设置大整数位数(支持最大 131 位,对应 1024 位 RSA)。
    • RSAKeyPair 仅使用公钥指数和模数,私钥留空,表明这是公钥加密。
    • encryptedString 可能是自定义函数,实现 RSA 加密逻辑。
  • 安全问题
    • 需确认是否使用正确填充(如 PKCS#1 v1.5 或 OAEP),默认可能不安全。
  • 用途:加密对称密钥(如 AES 密钥)以便安全传输。

知道了函数的用途,我们开始编写Python代码。

写一个工具类,分别实现函数abcd的功能,名字为JS,定义四个静态方法:

import base64
import random
from binascii import hexlify

from Crypto.Cipher import AES
from Crypto.Util.Padding import pad


class JS:
    @staticmethod
    def d(d,e="010001",f="00e0b509f6259df8642dbc35662901477df22677ec152b5ff68ace615bb7b725152b3ab17a876aea8a5aa76d2e417629ec4ee341f56135fccf695280104e0312ecbda92557c93870114af6c9d05c4f7f0c3685b7a46bee255932575cce10b424d813cfe4875d3e82047b97ddef52741d546b8e289dc6935b3ece0462db0a22b8e7",g="0CoJUm6Qyw8W8jud"):
        '''生成数据'''
        i=JS.a(16)
        h_encText=JS.b(d,g)
        encText=JS.b(h_encText,i)
        encSecKey=JS.c(i,e,f)
        return encText,encSecKey
    @staticmethod
    def a(a):
        '''生成16位随机字符'''
        b="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
        c=""
        for i in range(a):
            e = random.randint(0, len(b) - 1)
            c+=b[e]
        return c
    @staticmethod
    def b(a,b):
        '''AES加密'''
        key = b.encode('utf-8')
        iv = "0102030405060708".encode('utf-8')
        data = a.encode('utf-8')
        cipher = AES.new(key, AES.MODE_CBC, iv)
        encrypted = cipher.encrypt(pad(data, AES.block_size))
        return base64.b64encode(encrypted).decode('utf-8')
    @staticmethod
    def c(a,b,c):
        '''RSA加密'''
        a = a[::-1]
        result = pow(int(hexlify(a.encode()), 16), int(b, 16), int(c, 16))
        return format(result, 'x').zfill(131)

至于这几个函数是如何改写的。。其实很简单。
在这里插入图片描述
接下来的步骤就很简单了,编写搜索歌曲功能代码:

import requests
from urllib.parse import quote
from json import loads

from JS import JS

requests.packages.urllib3.disable_warnings()
class Searcher:
    url="https://music.163.com/weapi/cloudsearch/get/web?csrf_token="
    headers={
        'Host': 'music.163.com',
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/136.0.0.0 Safari/537.36',
        'Referer': 'https://music.163.com/search/',
        'Content-Type': 'application/x-www-form-urlencoded',
    }
    jsonStr="{\"hlpretag\":\"<span class=\\\"s-fc7\\\">\",\"hlposttag\":\"</span>\",\"s\":\"%s\",\"type\":\"1\",\"offset\":\"0\",\"total\":\"true\",\"limit\":\"%d\",\"csrf_token\":\"\"}"
    def __init__(self,keyword,proxies={},number=1,cookie=""):
        self.keyword=keyword
        self.proxies=proxies
        self.number=number
        self.headers['Cookie']=cookie

    def getData(self):
        s=self.jsonStr%(self.keyword,self.number)
        params,encSecKey=JS.d(s)
        data=f"""params={quote(params)}&encSecKey={quote(encSecKey)}"""
        try:
            r=requests.post(self.url,headers=self.headers,data=data,verify=False,timeout=10,proxies=self.proxies)
            if r.status_code==200:
                result=loads(r.content.decode())
                return result['result']['songs']
            return None
        except Exception as e:
            print(f'[!] {e}')
            return None

注意注意,这里有个非常坑的点,就是在传输data中,数据还要进行URL加密,否则就会报400,参数错误。这个点害了我一晚上。

我们成功实现了通过关键字搜索功能,获取到歌曲的idname,这两个参数至关重要

歌曲下载

选中一首歌,我们打开详细页。
在这里插入图片描述
打开开发者工具抓包,同时点击播放按钮
在这里插入图片描述
抓取到数据包后,我们逐个分析。
在这里插入图片描述
锁定到

/weapi/song/enhance/player/url/v1

这个请求包,在请求包中,我们可以看到歌曲的下载链接。
在这里插入图片描述
我们把这个链接复制到浏览器打开,成功播放了歌曲,确定就是下载链接。

在这里插入图片描述
接下来我们分析这个请求包的参数。
在这里插入图片描述
熟悉的paramsencsecKey,我们之前已经分析过了

使用同样方法打好断点,再次点击播放按钮,查看传入的参数。

在这里插入图片描述
这里出现了歌曲id,原始的参数为:

d="{\"ids\":\"[33004804]\",\"level\":\"exhigh\",\"encodeType\":\"aac\",\"csrf_token\":\"\"}"

于是我们可以编写相关Python代码:

from json import loads
from urllib.parse import quote

import requests

from JS import JS

requests.packages.urllib3.disable_warnings()
class Musicer:
    url='https://music.163.com/weapi/song/enhance/player/url/v1?csrf_token='
    headers={
        'Host': 'music.163.com',
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/136.0.0.0 Safari/537.36',
        'Referer': 'https://music.163.com/search/',
        'Content-Type': 'application/x-www-form-urlencoded',
    }
    jsonStr='{"ids":"[%s]","level":"exhigh","encodeType":"aac","csrf_token":""}'
    def __init__(self,musicid,cookie,name,proxies={}):
        self.musicid=str(musicid)
        self.headers['Cookie']=cookie
        self.proxies=proxies
        self.name=name

    def getDownloadUrl(self):
        s=self.jsonStr%self.musicid
        params,encSecKey=JS.d(s)
        data=f"""params={quote(params)}&encSecKey={quote(encSecKey)}"""
        try:
            r=requests.post(self.url,headers=self.headers,data=data,verify=False,timeout=10,proxies=self.proxies)
            if r.status_code==200:
                return loads(r.content.decode())['data'][0]['url']
            return None
        except Exception as e:
            print(e)
            return None
    def download(self):
        url=self.getDownloadUrl()
        try:
            r=requests.get(url,proxies=self.proxies,verify=False,timeout=10)
            if r.status_code==200:
                with open(f'{self.name}.mp3','wb') as f:
                    f.write(r.content)
                    print(f"[+] {self.name}.mp3 已保存")
        except Exception as e:
            print(f'[!] {e}')

至此,本项目全部完成。

总结

本博客仅做学习参考,任何违法犯罪行为与本文作者无关。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2381942.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

服务器的基础知识

什么是服务器 配置牛、运行稳、价格感人的高级计算机&#xff0c;家用电脑不能比拟的。 服务器的组成&#xff1a;电源、raid卡、网卡、内存、cpu、主板、风扇、硬盘。 服务器的分类 按计算能力分类 超级计算机 小型机AIX x86服务器&#xff08;服务器cpu架构&#xff09; …

Python连接redis

第一步安装redis Releases microsoftarchive/redis 安装时勾上所有能勾上的选项下一步即可 在CMD中pip install redis 安装redis pip install redis -i https://pypi.tuna.tsinghua.edu.cn/simple 配置redis 在redis安装目录下找到 修改 line 57 bind 0.0.0.0 line…

使用exceljs将excel文件转化为html预览最佳实践(完整源码)

前言 在企业应用中&#xff0c;我们时常会遇到需要上传并展示 Excel 文件的需求&#xff0c;以实现文件内容的在线预览。经过一番探索与尝试&#xff0c;笔者最终借助 exceljs 这一库成功实现了该功能。本文将以 Vue 3 为例&#xff0c;演示如何实现该功能&#xff0c;代码示例…

前端面经12 函数柯里化

<script>function sum(num){return function(num2){return numnum2}}console.log(sum(1)(2))</script>面试考察 只要参数够了 达到某个数量就输出 <script>let nums[]function sum(...args){nums.push(...args)if(nums.length>5){const out (nums.slice…

告别蜘蛛池!PHP 打造你的网站专属蜘蛛导航仪

在网站优化的赛道上&#xff0c;吸引搜索引擎蜘蛛来访一直是站长和开发者关注的重点。以往借助蜘蛛池、软件等工具引蜘蛛&#xff0c;不仅存在成本高、易违规的风险&#xff0c;效果也参差不齐。现在&#xff0c;有一种更高效、更安全的方式 —— 利用 PHP 代码&#xff0c;无需…

ubuntu kubeasz 部署高可用k8s 集群

ubuntu kubeasz 部署高可用k8s 集群 测试环境主机列表软件清单kubeasz 部署高可用 kubernetes配置源配置host文件安装 ansible 并进行 ssh 免密登录:下载 kubeasz 项⽬及组件部署集群部署各组件开始安装修改 config 配置文件增加 master 节点增加 kube_node 节点登录dashboard…

芯驰科技与安波福联合举办技术研讨会,深化智能汽车领域合作交流

5月15日&#xff0c;芯驰科技与全球移动出行技术解决方案供应商安波福&#xff08;Aptiv&#xff09;在上海联合举办以“芯智融合&#xff0c;共赢未来”为主题的技术研讨会。会上&#xff0c;双方聚焦智能座舱与智能车控的发展趋势&#xff0c;展开深入交流与探讨&#xff0c;…

【论文#目标检测】End-to-End Object Detection with Transformers

目录 摘要1.引言2.相关工作2.1 集合预测2.2 Transformer和并行解码2.3 目标检测 3.DETR模型3.1 目标检测集合预测损失3.2 DETR架构 4.实验4.1 与Faster R-CNN的比较4.2 消融研究4.3 分析4.4 DETR用于全景分割 5.结论6.致谢 Author: Nicolas Carion, Francisco Massa, Gabriel S…

Elasticsearch 深入分析三种分页查询【Elasticsearch 深度分页】

前言&#xff1a; 在前面的 Elasticsearch 系列文章中&#xff0c;分享了 Elasticsearch 的各种查询&#xff0c;分页查询也分享过&#xff0c;本篇将再次对 Elasticsearch 分页查询进行专题分析&#xff0c;“深度分页” 这个名词对于我们来说是一个非常常见的业务场景&#…

DAY29 超大力王爱学Python

知识点回顾 类的装饰器装饰器思想的进一步理解&#xff1a;外部修改、动态类方法的定义&#xff1a;内部定义和外部定义 作业&#xff1a;复习类和函数的知识点&#xff0c;写下自己过去29天的学习心得&#xff0c;如对函数和类的理解&#xff0c;对python这门工具的理解等&…

Ubuntu 远程桌面配置指南

概述: 本文主要介绍在Ubuntu 22.04中通过VNC实现远程连接的方法。首先需安装图形化界面和VNC工具x11vnc,设置开机启动服务;然后在Windows客户端用VNC Viewer通过局域网IP和端口5900连接。 总结: 一、VNC配置与安装 安装图形化界面 在Ubuntu 22.04中需先安装: sudo apt …

推扫式高光谱相机VIX-N230重磅发布——开启精准成像新时代

随着各行业对高光谱成像技术需求的持续增长&#xff0c;市场对于高分辨率、高灵敏度以及快速成像的高光谱相机的需求愈发迫切。中达瑞和凭借多年的行业经验和技术积累&#xff0c;敏锐捕捉到这一市场趋势&#xff0c;正式推出全新一代推扫式可见光近红外高光谱相机——VIX-N230…

Parsec解决PnP连接失败的问题

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、准备环境二、DMZ三、端口映射1.Parsec设置固定端口2.路由器设置端口转发3.重启被控端Parsec四、多少一句1.有光猫管理员账号2.没有光猫管理员账号总结 前言…

软件I2C

软件I2C 注意&#xff1a; SDA&#xff08;串行数据线&#xff09;和SCL&#xff08;串行时钟线&#xff09;都是双向I/O线&#xff0c;接口电路为开漏输出。需通过上拉电阻接电源VCC。 软件I2C说明 说明&#xff0c;有的单片机没有硬件I2C的功能&#xff0c;或者因为电路设计…

Brooks Polycold快速循环水蒸气冷冻泵客户使用手含电路图,适用于真空室应用

Brooks Polycold快速循环水蒸气冷冻泵客户使用手含电路图&#xff0c;适用于真空室应用

关于在Unity项目中使用Post Processing插件打包到web端出现的问题

关于在Unity项目中使用Post Processing插件打包到web端出现的问题 解决方法&#xff1a;是不激活摄像机上的Post Processing有关组件&#xff0c;拉低场景中的Directional Light平行光的强度进行web端打包。 &#xff08;烘焙灯光时是可以激活。&#xff09; web端支持这个Pos…

Prompt Tuning:高效微调大模型的新利器

Prompt Tuning(提示调优)是什么 Prompt Tuning(提示调优) 是大模型参数高效微调(Parameter-Efficient Fine-Tuning, PEFT)的重要技术之一,其核心思想是通过优化 连续的提示向量(而非整个模型参数)来适配特定任务。以下是关于 Prompt Tuning 的详细解析: 一、核心概念…

C++初阶-vector的底层

目录 1.序言 2.std::sort(了解) 3.vector的底层 3.1讲解 3.2构造函数 3.3push_back函数 3.4begin()和end()函数 3.5capacity()和size()函数和max_size函数 3.5.1size&#xff08;&#xff09;函数 为什么这样写&#xff1f; 底层原理 3.5.2max_size()函数 为什么这…

C语言指针深入详解(五):回调函数、qsort函数

目录 一、回调函数 1、使用回调函数改造前 2、使用回到函数改造后 二、qsort使用举例 1、使用qsort函数排序整型数据 2、使用qsort排序结构数据 三、qsort函数模拟实现 结语 &#x1f525;个人主页&#xff1a;艾莉丝努力练剑 &#x1f353;专栏传送门&#xff1a;《…

数据结构进阶:AVL树与红黑树

目录 前言 AVL树 定义 结构 插入 AVL树插入的大致过程 更新平衡因子 旋转 右单旋 左单旋 左右双旋 右左双旋 实现 红黑树 定义 性质 结构 插入 实现 总结 前言 在学习了二叉搜索树之后&#xff0c;我们了解到其有个致命缺陷——当树的形状呈现出一边倒…