牙叔教程 简单易懂
之前发了一篇教程 百度网盘5MB每秒-你本来就很快
浏览量还可以, 所以写个续集,
不懂原理的可以看之前的教程,
上一篇教程中采用的方法是扫描二维码授权, 今天我们用另外一种方法授权;
我们封装一下方法, 使用的语言是
- Node.js
其他语言可以使用 ChatGPT4 , 把Nodejs修改为其他语言
最后整合的代码中, 有下载进度, 就是我用 ChatGPT4 优化的.
步骤
一共有两步
- 获取用户授权
- 下载文件
授权模式介绍
当前百度网盘开放平台支持三种授权模式:授权码模式(Authorization Code)、简化模式(Implicit Grant)、设备码模式(Device Code)。您可以根据自身业务,选择合适的授权模式,实现用户授权。
三种模式主要区别如下:
授权模式 | 描述 |
授权码模式 | 用户授权后生成授权码 Code,开发者应用通过 Code 换取 Access Token。 |
简化模式 | 无需通过 Code 换取 Access Token,直接获取 Access Token。 |
设备码模式 | 获取设备码,用户授权后,开发者应用通过设备码换取 Access Token。 |
前两种都要绑定自己的域名, 我没有域名, 所以我选择第三个 设备码模式
设备码模式(Device Code)
对于弱输入设备,不支持浏览器或输入受限的设备(如儿童手表),推荐使用设备码模式接入授权。
开发者获取设备码,用户授权成功后,开发者通过设备码成功换取 Access Token。
用户授权有两种方式:一种是扫描二维码方式,另一种是输入用户码方式。关于用户码,在开发者获取设备码的同时,会返回用户码。
我们看最后一句话, 用户授权有两种方法
- 扫描二维码
- 输入用户码
为了实现更方便的自动化下载文件, 我们采用输入用户码的模式
授权时序图
这是百度网盘官方的图片
代码就不讲了, 我就只贴一下,
懂的就懂, 不懂的讲了也不懂, 不费那个劲
获取用户码
这些代码都是用 ChatGPT 写的
有图为证
const axios = require("axios"); const { AppID, Appkey, Secretkey, Signkey } = require("./config.js");async function getDeviceCode() {try {const response = await axios.get("https://openapi.baidu.com/oauth/2.0/device/code", {params: {response_type: "device_code",client_id: Appkey,scope: "basic,netdisk",},headers: {"User-Agent": "pan.baidu.com",},});console.log(response.data);} catch (error) {console.log(error);} }getDeviceCode();
他返回的字段是这样的
{device_code: '7bb71f0xxxxxxxxxxx27',user_code: 'mexxxxxxxxx',verification_url: 'https://openapi.baidu.com/device',qrcode_url: 'https://openapi.baidu.com/device/qrcode/001xxxxxxxxxxxa',expires_in: 300,interval: 5 }
主要字段的作用
- device_code 设备码, 后面要获取token, 就要使用他
- user_code 用户码, 百度网页验证的时候填写的东西
- verification_url, 百度的网页验证, 用于填写用户码 (二选一)
- qrcode_url, 授权的另一种方式, 这是二维码的链接 (二选一)
如果你使用qrcode_url, 那么就需要用户使用网盘客户端扫描该二维码;
如果你使用verification_url, 那么用户只需要输入用户码即可, 也就是user_code字段的值, 就像这样
成功授权后是页面是这样提示的
这样比扫描二维码方便, 不然还得自己拿个手机扫来扫去的.
授权已经ok了, 接下来我们申请token
申请 Access Token
我原来的代码是Promise的, 我让ChatGPT改成async的
params中的code字段的值, 是上一次访问返回的数据中的device_code
const axios = require("axios"); const { AppID, Appkey, Secretkey, Signkey, device_code } = require("./config.js");async function getToken() {try {const response = await axios.get("https://openapi.baidu.com/oauth/2.0/token", {params: {grant_type: "device_token",code: device_code,client_id: Appkey,client_secret: Secretkey,},headers: {"User-Agent": "pan.baidu.com",},});console.log(response.data);} catch (error) {console.log(error);} }getToken();
返回的数据
{expires_in: 2592000,refresh_token: '127.11bbxxxxxxxxxxxxxxmg',access_token: '126.29cxxxxxxxxxxxOg',session_secret: '',session_key: '',scope: 'basic netdisk' }
token有了以后, 就可以为所欲为了
我看看我网盘有没有大文件,
找到了, 有个游戏, 50MB
如果文件的名字是中文, 我们需要编码, 就像你在浏览器的地址栏中, 看到的汉字一样,
中文编码
比如, 你在百度输入你好, 按下回车, 地址栏是这样的
,
如果你复制黏贴, 会发现没有你好了, 出现的是百分号表示的你好
https://www.baidu.com/s?wd=%E4%BD%A0%E5%A5%BD
百度文档是这样说的
他说dir字段, 如果有中文, 那么就要url编码, 你是不是也编码了?
是不是爆错了, 要么-7 要么-9
错误码
错误码 | 错误描述 |
-7 | 文件或目录无权访问 |
-9 | 文件或目录不存在 |
你要是信了文档, 你就得栽个跟头, 十万八千里的那种, 直接到西天
丫的我测试了半天, 根本不用编码,
也许以前要吧, 但是现在, 此时此刻, 是不需要编码的.
文档忽悠了我, 心碎一地
文档右下角有个评价功能, 我给了二颗星, 并且给了原因,
希望官方人员, 可以及时更新文档
下载文件流程
- 指定要下载的文件的父文件夹
- 获取父文件夹下的子文件信息
- 下载文件
代码就不分开一个一个讲了, 直接合起来
config.js
module.exports = {AppID: "xxx",Appkey: "xxx",Secretkey: "xxx",Signkey: "xxx",access_token: '1xxxx',fs_id: "xxx",device_code: "xxx", };
main.js
const axios = require("axios"); const path = require("path"); const fs = require("fs"); const { AppID, Appkey, Secretkey, Signkey, device_code, access_token } = require("./config.js");async function getFileList(directory) {try {const response = await axios.get("https://pan.baidu.com/rest/2.0/xpan/file", {params: {method: "list",dir: directory,order: "time",start: 0,limit: 10,folder: 0,access_token: access_token,desc: 1,},headers: {"User-Agent": "pan.baidu.com",},});return response.data.list;} catch (error) {console.log(error);} }async function getFileMetas(fs_id) {try {const response = await axios.get("http://pan.baidu.com/rest/2.0/xpan/multimedia", {params: {method: "filemetas",access_token: access_token,fsids: `[${fs_id}]`,thumb: 0,dlink: 1,extra: 0,},headers: {"User-Agent": "pan.baidu.com",},});return response.data;} catch (error) {console.log(error);} }function formatSize(size) {if (size < 1024 * 1024) {return (size / 1024).toFixed(2) + " KB";} else {return (size / (1024 * 1024)).toFixed(2) + " MB";} }function downloadFile(dlink, fileSavePath) {let url = `${dlink}&access_token=${access_token}`;return new Promise((resolve, reject) => {axios.get(url, {headers: {"User-Agent": "pan.baidu.com",Host: "d.pcs.baidu.com",},responseType: "stream",}).then((response) => {const totalSize = response.headers["content-length"];let downloadedSize = 0;const writeStream = fs.createWriteStream(fileSavePath);response.data.pipe(writeStream);response.data.on("data", (chunk) => {downloadedSize += chunk.length;});const printProgress = setInterval(() => {const progress = ((downloadedSize / totalSize) * 100).toFixed(2);console.log(`Downloaded: ${formatSize(downloadedSize)}, Progress: ${progress}%`);}, 1000);writeStream.on("finish", () => {clearInterval(printProgress);resolve(true);});writeStream.on("error", (error) => {clearInterval(printProgress);reject(error);});}).catch((error) => {console.log(error);reject(false);});}); }function getParentDirectory(filePath) {let parentDirectory = path.dirname(filePath);return parentDirectory; }async function downloadPanFile(panFilePath) {let parentDirectory = getParentDirectory(panFilePath);let fileList = await getFileList(parentDirectory);const foundItem = fileList.find((item) => item.path === panFilePath);if (!foundItem) {console.log("文件不存在");return;}const fileMetas = await getFileMetas(foundItem.fs_id);const dlink = fileMetas.list[0].dlink;let fileName = path.basename(panFilePath);// 正则表达式匹配非法文件名字符const invalidCharacters = /[\\/:"*?<>|]/g;// 用空字符串替换非法字符const sanitizedFileName = fileName.replace(invalidCharacters, "");fileName = sanitizedFileName.replace(/\s+/g, "-");let fileSavePath = path.join(__dirname, fileName);fileSavePath = path.resolve(fileSavePath);let startTime = new Date().getTime();await downloadFile(dlink, fileSavePath);let endTime = new Date().getTime();console.log(`下载耗时:${(endTime - startTime) / 1000}s`); } /* -------------------------------------------------------------------------- */ let panFilePath = "/eee/xxx/ccc/aaa.apk";downloadPanFile(panFilePath);
代码中的下载文件进度, 以及 formatSize 方法, 都是用 ChatGPT4 自动修改的, 不用自己动手,
改代码, 就是一句话的事;
优化代码
你还可以复制完整的代码, 让 ChatGPT4 优化它,
这是ChatGPT4给的代码优化的建议
I have made the following changes to the code:
Removed unnecessary variable assignment for parentDirectory and used it directly in getFileList() function call.
Removed unnecessary variable assignment for fileSavePath and used path.resolve() directly in its place.
Consolidated multiple lines of code into single lines where appropriate for better readability.
Removed unnecessary blank lines.
第一个优化点,
把
function getParentDirectory(filePath) {let parentDirectory = path.dirname(filePath);return parentDirectory; }
改为
function getParentDirectory(filePath) {return path.dirname(filePath); }
第二个优化点
把
let fileSavePath = path.join(__dirname, fileName);fileSavePath = path.resolve(fileSavePath);
改为
let fileSavePath = path.resolve(__dirname, fileName);
下载速度
日志一秒打印一次
Downloaded: 7.50 MB, Progress: 15.77% Downloaded: 14.00 MB, Progress: 29.44% Downloaded: 21.50 MB, Progress: 45.21% Downloaded: 28.00 MB, Progress: 58.88% Downloaded: 35.93 MB, Progress: 75.54% Downloaded: 42.00 MB, Progress: 88.31% 下载耗时:7.668s
- 文件大小 47.56MB
- 下载时间 7.668s
- 下载速度 6.20MB每秒
ChatGPT对本教程的作用
- 代码, 我是直接复制黏贴的百度官方文档, 然后跟他说, 这是文档接口, 写出代码, 这样就有了基本代码
- 如果他出的是Promise的风格, 我会让让改成async的风格
- 让他添加了下载进度, 并且小于1024kb, 就用kb显示, 否则用mb显示
- 优化大块的代码
ChatGPT联网版, Stable Diffusion画图, 这个星球全都有, 低调使用, 别外传