JS逆向实战:破解网易云音乐评论接口的AES与RSA加密

📅 2026/7/7 22:54:14
JS逆向实战:破解网易云音乐评论接口的AES与RSA加密
1. 项目概述与核心思路拆解最近在做一个音乐数据分析的小项目需要批量获取网易云音乐上的歌曲评论。一开始我以为这活儿很简单不就是个GET请求吗结果一脚踩进了JS逆向的深坑里。网易云音乐对核心数据接口的保护做得相当到位所有请求参数都经过了复杂的加密直接抓包拿到的数据根本没法用。这恰恰是JS逆向的典型战场前端页面为了完成某个功能比如播放音乐、加载评论必须执行一段JavaScript代码来生成加密参数我们的任务就是找到这段代码理解它的逻辑然后在Python环境中复现它从而绕过加密直接与服务器对话。这个项目标题“js逆向分析-网易云音乐评论”点出了两个核心目标是获取评论数据手段是JS逆向。为什么非得用JS逆向因为网易云音乐的评论接口和它的播放接口一样使用了非对称加密RSA和对称加密AES的组合拳。你直接构造一个包含歌曲ID和页码的请求发过去服务器会直接拒绝。你必须按照它规定的“暗号”——也就是前端JavaScript计算出的那两个关键参数params和encSecKey——来提交请求服务器才会认为你是“自己人”把数据吐给你。所以整个项目的核心思路非常清晰定位前端加密函数 - 分析其加密逻辑与密钥 - 使用Python的execjs或PyExecJS库模拟执行该函数 - 生成合法的加密参数 - 成功请求数据。这个过程就像破解一个密码锁前端JS是密码机的设计图我们的Python脚本就是按照图纸造出一台一模一样的密码机生成正确的密码。注意本文所有技术分析及代码仅用于学习JS逆向技术与网络安全知识请严格遵守相关法律法规与网站robots.txt协议不得用于任何商业爬取、数据盗用或干扰网站正常服务的行为。合理控制请求频率避免对目标服务器造成压力。2. 逆向前的环境准备与抓包分析工欲善其事必先利其器。在开始逆向之前我们需要把“战场”布置好。这里不需要kali那么专业的渗透测试环境一台普通的Windows或macOS电脑配合浏览器和几个工具就足够了。2.1 核心工具链搭建首先你需要一个现代的、开发者工具强大的浏览器Chrome或EdgeChromium内核是首选。它们的开发者工具按F12打开是我们逆向分析的“主战场”。其次我们需要一个能执行JavaScript代码的Python环境。这里首推execjs库它就像一个桥梁能让Python调用本地安装的JavaScript运行时如Node.js来执行JS代码。安装Node.js去Node.js官网下载并安装LTS版本。安装完成后在命令行输入node -v和npm -v能显示版本号即表示成功。Node.js为我们提供了执行JS代码的引擎。安装Python库在命令行中执行以下命令安装我们需要的库。pip install requests execjs pyexecjsrequests用于发送HTTP请求。execjs/pyexecjs用于在Python中执行JavaScript代码。两者功能类似pyexecjs是execjs的一个分支有时兼容性更好可以都装上。2.2 关键请求的抓取与定位打开网易云音乐网页版进入任意一首歌曲的页面比如周杰伦的《晴天》。打开开发者工具F12切换到Network网络面板。记得勾选上“Preserve log”保留日志防止页面跳转时请求记录被清空。然后点击歌曲下方的“评论”或者滚动评论列表触发加载更多。此时Network面板中会刷出大量请求。我们的目标是找到那个真正携带评论数据的请求。如何快速定位看请求类型评论数据通常是动态加载的所以找XHR/Fetch类型的请求。看请求URL网易云音乐评论接口的URL通常包含weapi和comment关键字。经过抓包分析核心的评论获取接口地址是https://music.163.com/weapi/comment/resource/comments/get?csrf_token看请求载荷Payload点击这个请求查看它的Headers和Payload。你会发现这个请求的Request Method是POST并且在Form Data或Payload里并不是简单的song_idxxxpage1这样的格式而是两个让人一头雾水的参数params和encSecKey。这两串长长的、看似随机的字符就是我们需要攻破的加密结果。首次抓包结果示例请求URL: https://music.163.com/weapi/comment/resources/comments/get?csrf_token 请求方法: POST 请求载荷: params: u6wC4kFvVjHx2gLpZoqN9sDmK...很长一串加密文本 encSecKey: 7b226173...另一串很长的加密文本看到这个逆向的方向就确定了我们必须搞清楚前端是如何根据歌曲ID、页码等信息生成这组params和encSecKey的。3. 深入加密逻辑逆向核心JS代码找到了目标请求接下来就是最核心、最考验耐心的部分——逆向加密逻辑。我们需要在开发者工具的Sources源代码面板中找到生成这两个参数的JavaScript函数。3.1 使用“调用栈”定位加密函数在Network面板中找到我们刚才定位到的那个评论请求右键点击它选择“Copy - Copy as cURL”可能不太直观更直接的方法是使用“Initiator”列。点击该请求在详情面板的底部或右侧找到Initiator标签页这里显示了是哪个JS文件、哪行代码发起了这个请求。更通用的方法是使用“搜索”和“断点”。全局搜索在Sources面板按CtrlShiftF(Windows) 或CmdOptionF(Mac) 打开全局搜索。因为我们知道关键参数是params和encSecKey所以可以尝试搜索这两个词。但前端代码可能经过压缩混淆变量名会变。更有效的方法搜索加密函数名。根据常见的加密模式和之前爬取音频文件的经验参考文章中提到爬取音频文件时遇到了CryptoJS和RSAKeyPair我们可以搜索encText、encSecKey、RSA、AES、CryptoJS等关键词。打断点在可能包含加密逻辑的JS文件里在疑似加密函数被调用的地方比如网络请求发送XMLHttpRequest.send()或fetch()之前打上断点。然后重新触发评论加载动作代码执行就会在此暂停。查看调用栈Call Stack当代码在断点处暂停时右侧的Call Stack窗口会显示当前函数是被谁一层层调用的。沿着调用栈向上查找你就能找到最初构造params和encSecKey的那个函数。经过一番查找这个过程可能需要耐心和一定的JS调试经验我们通常会定位到一个被高度混淆但结构关键的JS函数。它的核心逻辑往往如下所示这是经过简化和梳理后的逻辑实际代码变量名可能是单个字母function encryptData(text, pubKey, modulus, secretKey) { // 1. 第一次AES加密使用固定密钥 secretKey (例如: 0CoJUm6Qyw8W8jud) var firstEncrypt aesEncrypt(text, secretKey); // 2. 生成一个16位的随机字符串作为第二次AES加密的密钥 var randomSecret generateRandomStr(16); // 3. 第二次AES加密使用随机密钥对第一次加密的结果再加密得到最终的 encText (即params) var encText aesEncrypt(firstEncrypt, randomSecret); // 4. 使用RSA公钥加密上一步生成的随机密钥得到 encSecKey var encSecKey rsaEncrypt(randomSecret, pubKey, modulus); return { params: encText, encSecKey: encSecKey }; }逻辑解读双重AES加密CBC模式原始数据一个JSON字符串先被一个固定密钥AES加密一次然后再被一个随机生成的密钥AES加密第二次。这增加了直接破解的难度。RSA加密那个随机生成的密钥本身是解密第二层AES所必需的。但它不能明文传输所以用RSA公钥加密后作为encSecKey发送给服务器。服务器持有对应的RSA私钥可以解密出随机密钥再用它解密第二层AES最后用固定密钥解密第一层AES得到原始数据。固定参数这里的pubKey(公钥指数常为010001)、modulus(公钥模数一串很长的16进制数)、secretKey(固定AES密钥常为0CoJUm6Qyw8W8jud) 都是硬编码在JS文件里的我们需要把它们找出来。3.2 提取并整理关键JS代码找到这个核心函数后我们不能只复制这一个函数。因为JS是解释型语言函数内部可能依赖其他全局函数或对象比如CryptoJS库、RSAKeyPair等自定义函数。我们需要将其依赖的所有函数和变量都提取出来保存到一个单独的.js文件中以便在Node.js环境中完整运行。提取技巧在Sources面板找到包含核心加密函数的JS文件。查看该函数上方和文件头部寻找类似var CryptoJS require(crypto-js);的引入语句或者一大段定义RSAKeyPair、BigInt、BarrettMu等算法的代码。这些往往是实现RSA加密的核心。将从文件开头到核心函数定义结束的整段JS代码通常是一个立即执行函数或一大段变量、函数定义复制出来。创建一个新文件例如netease_encrypt.js将代码粘贴进去。关键补全如果代码中使用了CryptoJS但文件里没有定义我们需要手动在文件开头引入。在Node.js环境下需要安装crypto-js库 (npm install crypto-js)并在JS文件开头添加var CryptoJS require(crypto-js);。最终你的netease_encrypt.js文件结构可能类似于// 1. 引入加密库如果需要 var CryptoJS require(crypto-js); // 2. 一大段RSA算法实现代码包括 BarrettMu, BigInt, RSAKeyPair, encryptedString 等函数 function setMaxDigits(a) {...} function BigInt(a) {...} function RSAKeyPair(a, b, c) {...} function encryptedString(a, b) {...} // ... (此处省略数百行RSA相关函数) // 3. 辅助函数如生成随机字符串 function generateRandomStr(length) { var chars abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789; var str ; for (var i 0; i length; i) { str chars.charAt(Math.floor(Math.random() * chars.length)); } return str; } // 4. AES加密函数 function aesEncrypt(text, key) { var keyUtf8 CryptoJS.enc.Utf8.parse(key); var ivUtf8 CryptoJS.enc.Utf8.parse(0102030405060708); // 固定IV var srcs CryptoJS.enc.Utf8.parse(text); var encrypted CryptoJS.AES.encrypt(srcs, keyUtf8, { iv: ivUtf8, mode: CryptoJS.mode.CBC, padding: CryptoJS.pad.Pkcs7 // 通常使用PKCS7填充 }); return encrypted.toString(); // 返回Base64格式的密文 } // 5. RSA加密函数 function rsaEncrypt(text, pubKey, modulus) { // 设置RSA位数 setMaxDigits(131); // 这个数字很重要对应密钥长度 var key new RSAKeyPair(pubKey, , modulus); return encryptedString(key, text); } // 6. 最终暴露给外部的核心加密函数 function getEncryptedParams(text, pubKey, modulus, aesKey) { var firstEncrypt aesEncrypt(text, aesKey); var randomSecret generateRandomStr(16); var encText aesEncrypt(firstEncrypt, randomSecret); var encSecKey rsaEncrypt(randomSecret, pubKey, modulus); return { params: encText, encSecKey: encSecKey }; } // 7. 导出函数如果是Node.js环境 module.exports { getEncryptedParams: getEncryptedParams };实操心得提取JS代码时最容易出错的地方是依赖缺失。务必在Node.js环境中用node -c netease_encrypt.js命令检查语法并尝试用node交互模式引入文件调用函数进行测试确保没有ReferenceError。另一个坑是编码和空格复制代码时可能会引入不可见的特殊字符导致执行失败建议使用纯文本编辑器处理。4. Python实现模拟加密与请求评论数据JS逆向的“逆向”部分完成后就进入了“模拟”阶段。我们要在Python中利用提取出的JS逻辑生成合法的加密参数然后发送请求获取评论。4.1 构建Python加密模块首先确保你的项目目录下有整理好的netease_encrypt.js文件。然后我们编写Python脚本。import json import requests import execjs from urllib.parse import quote class NeteaseMusicCommentCrawler: def __init__(self): # 固定加密参数从JS代码中提取 self.pub_key 010001 # RSA公钥指数 self.modulus 00e0b509f6259df8642dbc35662901477df22677ec152b5ff68ace615bb7b725152b3ab17a876aea8a5aa76d2e417629ec4ee341f56135fccf695280104e0312ecbda92557c93870114af6c9d05c4f7f0c3685b7a46bee255932575cce10b424d813cfe4875d3e82047b97ddef52741d546b8e289dc6935b3ece0462db0a22b8e7 # RSA公钥模数 self.aes_key 0CoJUm6Qyw8W8jud # 固定AES密钥 self.base_url https://music.163.com # 加载JS加密代码 with open(netease_encrypt.js, r, encodingutf-8) as f: js_code f.read() self.ctx execjs.compile(js_code) # 通用请求头模拟浏览器 self.headers { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36, Referer: https://music.163.com/, Content-Type: application/x-www-form-urlencoded, Origin: https://music.163.com } def _encrypt_params(self, data_dict): 加密请求参数。 :param data_dict: 需要加密的原始参数字典。 :return: 加密后的参数字典包含 params 和 encSecKey。 # 将字典转换为JSON字符串 text json.dumps(data_dict) # 调用JS加密函数 # 注意JS函数名需要与你导出的函数名一致这里是 getEncryptedParams encrypted self.ctx.call(getEncryptedParams, text, self.pub_key, self.modulus, self.aes_key) return { params: encrypted[params], encSecKey: encrypted[encSecKey] } def get_comments(self, resource_type, resource_id, page1, limit20): 获取评论。 :param resource_type: 资源类型歌曲为 0歌单为 2专辑为 3MV为 1。 :param resource_id: 资源ID如歌曲ID。 :param page: 页码从1开始。 :param limit: 每页评论数最大100。 :return: 评论数据的JSON对象。 # 构造原始请求体 raw_data { csrf_token: , # 通常为空如果需要可以从Cookie中获取 cursor: str((page - 1) * limit), # 偏移量用于分页 offset: 0, orderType: 1, # 1: 按推荐排序2: 按热度排序3: 按时间排序 pageNo: str(page), pageSize: str(limit), rid: str(resource_id), # 资源ID threadId: fR_{resource_type}_{resource_id} # 资源类型和ID组成的线程ID } # 加密参数 encrypted_data self._encrypt_params(raw_data) # 目标API地址 api_url f{self.base_url}/weapi/comment/resource/comments/get?csrf_token # 发送POST请求 response requests.post(api_url, headersself.headers, dataencrypted_data) response.raise_for_status() # 检查请求是否成功 return response.json() def get_song_comments(self, song_id, page1, limit20): 获取歌曲评论的便捷方法 return self.get_comments(resource_type0, resource_idsong_id, pagepage, limitlimit) # 使用示例 if __name__ __main__: crawler NeteaseMusicCommentCrawler() # 示例获取歌曲《晴天》ID: 1347227968的第一页评论每页20条 song_id 1347227968 try: result crawler.get_song_comments(song_id, page1, limit20) if result.get(code) 200: comments result.get(data, {}).get(comments, []) print(f成功获取到 {len(comments)} 条评论:) for idx, comment in enumerate(comments): user comment.get(user, {}).get(nickname, 未知用户) content comment.get(content, ) like_count comment.get(likedCount, 0) print(f{idx1}. [{user}]{content} (点赞: {like_count})) # 打印分页信息 total result.get(data, {}).get(totalCount, 0) print(f\n总计评论数: {total}) else: print(f请求失败错误码: {result.get(code)}, 信息: {result.get(msg)}) except Exception as e: print(f程序运行出错: {e})4.2 代码关键点解析固定参数pub_key、modulus、aes_key这三个值是加密的基石必须与前端JS代码中的值完全一致。它们通常不会频繁变化但并非永久不变如果某天发现加密失效首先要检查这些值是否更新了。execjs的使用我们使用execjs.compile()将整个JS文件编译成一个可执行上下文ctx然后通过ctx.call(函数名, 参数...)来调用JS中定义的加密函数。这相当于在Python里启动了一个微型的JS虚拟机。请求体构造get_comments方法中的raw_data字典是关键。这些字段名如cursor,rid,threadId和取值规则是通过分析多个正常请求的载荷归纳出来的。threadId的格式R_{type}_{id}是网易云的标准格式。请求头User-Agent、Referer、Origin等头部信息对于模拟浏览器行为、绕过一些基础的反爬机制很重要。Content-Type设置为application/x-www-form-urlencoded是因为加密后的参数是以表单形式提交的。错误处理使用response.raise_for_status()和try...except来捕获网络请求和JSON解析错误是编写健壮爬虫的基本功。注意事项直接运行上述代码可能会遇到execjs找不到JavaScript运行时的问题。确保已安装Node.js并且其路径已添加到系统环境变量。有时需要指定运行时例如import execjs; print(execjs.get().name)查看当前使用的运行时。如果遇到编码问题可以在打开JS文件时指定encodingutf-8并在execjs.compile()前后处理字符串编码。5. 数据解析与高级应用场景成功拿到加密的评论数据只是第一步返回的JSON结构比较复杂我们需要从中提取出有用的信息。此外这个加密逻辑可以应用到网易云音乐其他需要加密的接口上。5.1 解析评论JSON数据结构让我们深入看一下get_song_comments返回的JSON数据。一个成功的响应code: 200的data字段结构大致如下{ code: 200, data: { totalCount: 12543, // 评论总数 hasMore: true, // 是否有更多页 comments: [ // 评论列表 { user: { userId: 123456789, nickname: 云音乐用户, avatarUrl: https://p3.music.126.net/...jpg, vipType: 0 }, commentId: 987654321, content: 这首歌让我想起了..., // 评论内容 time: 1625000000000, // 时间戳毫秒 likedCount: 1520, // 点赞数 liked: false, // 当前用户是否点赞 replyCount: 5, // 回复数 replies: [ // 热门回复如果有 { user: {...}, content: ..., time: ..., likedCount: ... } ] } // ... 更多评论 ], cursor: 下一次请求的游标, sortType: 1 // 排序类型 } }我们可以编写一个更强大的解析函数将原始JSON转换成更易用的Python数据结构并保存到文件或数据库。import time from datetime import datetime def parse_comments_data(json_data): 解析评论JSON数据提取结构化信息 if json_data.get(code) ! 200: return None data json_data.get(data, {}) total data.get(totalCount, 0) has_more data.get(hasMore, False) comments_list data.get(comments, []) parsed_comments [] for comment in comments_list: user_info comment.get(user, {}) # 转换时间戳为可读格式 ts comment.get(time, 0) / 1000 # 转换为秒 time_str datetime.fromtimestamp(ts).strftime(%Y-%m-%d %H:%M:%S) if ts 0 else 未知时间 parsed { comment_id: comment.get(commentId), user_id: user_info.get(userId), user_nickname: user_info.get(nickname, 匿名用户), content: comment.get(content, ).replace(\n, ), # 替换换行符方便存储 publish_time: time_str, like_count: comment.get(likedCount, 0), reply_count: comment.get(replyCount, 0), is_hot: comment.get(hot, False), # 是否为热门评论 replies: [] # 存储回复 } # 解析回复 for reply in comment.get(replies, []): reply_user reply.get(user, {}) parsed[replies].append({ user: reply_user.get(nickname), content: reply.get(content, ), like_count: reply.get(likedCount, 0) }) parsed_comments.append(parsed) return { total: total, has_more: has_more, comments: parsed_comments, raw_cursor: data.get(cursor) # 用于下次请求 } # 在爬虫类中使用 crawler NeteaseMusicCommentCrawler() result crawler.get_song_comments(1347227968, page1, limit5) parsed parse_comments_data(result) if parsed: for comment in parsed[comments]: print(f用户: {comment[user_nickname]}) print(f内容: {comment[content][:50]}...) # 预览前50字 print(f时间: {comment[publish_time]} | 点赞: {comment[like_count]}) print(- * 40)5.2 扩展到其他应用场景掌握了核心加密逻辑你就拥有了打开网易云音乐许多数据接口的“钥匙”。除了评论这个加密模式广泛应用于其weapi接口群。你可以通过类似的方式探索和获取其他数据歌单详情与歌曲列表接口路径可能包含playlist/detail。你需要分析前端请求找到对应的请求体和加密方式很可能是一样的然后修改raw_data中的参数如歌单IDid。用户信息与创建歌单用户主页信息、收藏歌单等接口。搜索功能搜索歌曲、歌手、专辑的接口其加密逻辑是相通的主要变化在于请求体raw_data的结构。歌手详情与热门歌曲这正是热词“爬虫网易云音乐如何获取歌手简介”所对应的需求。你需要找到获取歌手详情的API例如通过搜索或直接构造分析其请求参数可能包含artistId然后使用相同的加密流程。通用化思路 你可以将爬虫类进一步抽象将加密部分与具体的业务请求分离。例如def make_encrypted_request(self, api_path, raw_data_dict): 通用加密请求方法 encrypted_data self._encrypt_params(raw_data_dict) url f{self.base_url}{api_path} response requests.post(url, headersself.headers, dataencrypted_data) return response.json() # 然后为不同接口封装专用方法 def get_playlist_detail(self, playlist_id): raw_data { id: playlist_id, n: 1000, # 获取的歌单歌曲数量 csrf_token: } return self.make_encrypted_request(/weapi/v3/playlist/detail, raw_data) def search(self, keyword, type1, limit30): # type: 1-单曲10-专辑100-歌手1000-歌单... raw_data { s: keyword, type: type, limit: limit, offset: 0, csrf_token: } return self.make_encrypted_request(/weapi/search/get, raw_data)通过这种方式你的爬虫框架就具备了高度的可扩展性。6. 常见问题、反爬策略与应对技巧实录在实际操作中你绝不会一帆风顺。以下是我在多次逆向和爬取过程中踩过的坑和总结的经验。6.1 高频问题排查清单问题现象可能原因排查步骤与解决方案execjs报错如ExecJS::RuntimeError1. Node.js未安装或未在PATH中。2. JS代码存在语法错误或依赖缺失。3. 编码问题导致JS文件读取错误。1. 终端运行node -v确认安装。检查Python中execjs.get().name输出。2. 用node -c your_file.js检查JS语法。在Node环境手动执行JS片段定位错误。3. 确保读写JS文件时使用encodingutf-8。请求返回-460或-460 Cheating错误码加密参数不正确服务器判定为非法请求。1.核对固定参数确认pub_key,modulus,aes_key是否与当前网页JS中的完全一致它们可能会更新。2.检查请求体格式确认raw_data字典的键名和值类型字符串/数字与浏览器抓包看到的一致。3.验证加密结果用相同的原始数据在浏览器控制台执行加密函数与Python生成的结果对比必须一模一样。返回401或数据为空缺少必要的Cookie或Token身份验证失败。1. 评论接口有时需要登录态。尝试从浏览器复制一个已登录状态的Cookie字符串添加到请求头headers[Cookie]中。2. 注意Cookie有有效期需要定期更新。3.csrf_token字段有时需要从Cookie中提取__csrf的值填入而不是留空。只能获取前几页评论分页逻辑不是简单的page参数而是使用了cursor。查看返回的JSON数据其中有一个cursor字段。获取下一页时需要将上一页返回的cursor值作为下一次请求raw_data中的cursor参数。pageNo和offset可能失效。请求频率稍高即被限制触发了IP频率限制或风控策略。1.增加延迟在请求间使用time.sleep(random.uniform(1, 3))。2.使用代理IP池轮换不同的IP地址发送请求。3.模拟更真实的用户行为携带更完整的请求头包括Referer,Accept-Language等。6.2 高级反爬策略与应对网易云音乐作为大型平台其反爬策略是多层次、动态的。JavaScript环境检测前端JS可能会检测执行环境比如检查window,document,navigator等浏览器特有对象。在Node.js环境中这些对象是不存在的可能导致加密函数运行失败。应对在执行的JS代码开头手动定义这些全局变量为空对象或模拟值以绕过简单的检测。// 在netease_encrypt.js文件最前面添加 if (typeof window undefined) { global.window {}; global.document {}; global.navigator { userAgent: Mozilla/5.0 ... }; }代码混淆与动态变化核心加密JS可能被严重混淆变量名替换为单字符、控制流扁平化等并且可能不定期更新导致之前提取的代码失效。应对不要试图完全去混淆重点是找到入口函数和关键参数。可以借助浏览器调试器的“Pretty-print”功能{}按钮格式化混淆代码便于阅读。关注那些包含encrypt,AES,RSA,CryptoJS等关键词的片段。参数指纹与签名更高级的反爬可能会在加密基础上加入与浏览器指纹、时间戳、用户行为相关的动态签名每次请求都不同且难以模拟。应对这大大增加了逆向难度。需要仔细分析在加密函数调用前是否有其他函数生成了额外的参数。如果遇到可能需要使用Puppeteer或Selenium这类自动化测试工具来完全模拟浏览器行为直接获取加密后的结果但这会牺牲效率。最后的忠告爬虫行为应当遵守法律法规和网站的robots.txt协议。网易云音乐的robots.txt通常会对/weapi/这类接口进行限制。本技术分享仅供学习JS逆向原理和网络安全知识之用。在实际项目中如果确实需要大量数据应优先考虑是否有官方API通常需要申请或合作渠道。控制请求频率避免对他人服务器造成不必要的负担是每个技术从业者应有的素养。