From 5c46764251a1c2abdca67c778054a1f35243f417 Mon Sep 17 00:00:00 2001 From: pangyaxuan Date: Mon, 23 Mar 2026 14:02:21 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=20'W3'?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- W3 | 179 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 179 insertions(+) create mode 100644 W3 diff --git a/W3 b/W3 new file mode 100644 index 0000000..ea73c07 --- /dev/null +++ b/W3 @@ -0,0 +1,179 @@ +import org.apache.commons.lang3.RandomStringUtils; +import org.apache.http.HttpEntity; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.entity.StringEntity; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClients; +import org.apache.http.util.EntityUtils; +import org.bouncycastle.jce.provider.BouncyCastleProvider; +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONArray; +import com.alibaba.fastjson.JSONObject; + +import javax.crypto.Cipher; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.SecretKeySpec; +import java.nio.charset.StandardCharsets; +import java.security.Security; +import java.util.Base64; +import java.util.HashMap; +import java.util.Map; + +/** + * 网易云音乐热门评论爬虫 + */ +public class NeteaseCommentCrawler { + // 网易云固定加密密钥 + private static final String FIRST_KEY = "0CoJUm6Qyw8W8jud"; + private static final String IV = "0102030405060708"; + // 固定encSecKey(适配热门评论接口) + private static final String ENC_SEC_KEY = "00e0b50bcfcc08f1f8d6a8a9a0c96969b6f928a5069f07aa09f6062446f4224f42f42f42f42f42f42f42f42f42f42f42f42f42f42f42f42f42f42f42f42f42f4"; + + static { + // 注册BouncyCastle加密提供者 + Security.addProvider(new BouncyCastleProvider()); + } + + /** + * AES加密(CBC模式,PKCS7填充) + */ + private static String aesEncrypt(String content, String key) throws Exception { + SecretKeySpec secretKeySpec = new SecretKeySpec(key.getBytes(StandardCharsets.UTF_8), "AES"); + IvParameterSpec ivParameterSpec = new IvParameterSpec(IV.getBytes(StandardCharsets.UTF_8)); + Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding"); + cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivParameterSpec); + byte[] encrypted = cipher.doFinal(pad(content).getBytes(StandardCharsets.UTF_8)); + return Base64.getEncoder().encodeToString(encrypted); + } + + /** + * PKCS7补位 + */ + private static String pad(String s) { + int blockSize = 16; + int padCount = blockSize - (s.length() % blockSize); + StringBuilder sb = new StringBuilder(s); + for (int i = 0; i < padCount; i++) { + sb.append((char) padCount); + } + return sb.toString(); + } + + /** + * 生成加密参数(params + encSecKey) + */ + private static Map getEncryptedParams(String songId) throws Exception { + // 构造原始请求参数 + JSONObject originParam = new JSONObject(); + originParam.put("rid", "R_SO_4_" + songId); + originParam.put("offset", 0); + originParam.put("total", true); + originParam.put("limit", 20); + originParam.put("csrf_token", ""); + + // 生成16位随机密钥 + String secondKey = RandomStringUtils.randomAlphanumeric(16); + // 双重AES加密 + String params = aesEncrypt(aesEncrypt(originParam.toJSONString(), FIRST_KEY), secondKey); + + Map result = new HashMap<>(); + result.put("params", params); + result.put("encSecKey", ENC_SEC_KEY); + return result; + } + + /** + * 获取热门评论 + */ + public static JSONArray getHotComments(String songId) { + String url = "https://music.163.com/weapi/v1/resource/comments/R_SO_4_" + songId + "?csrf_token="; + CloseableHttpClient httpClient = HttpClients.createDefault(); + try { + // 1. 构造POST请求 + HttpPost httpPost = new HttpPost(url); + // 2. 设置请求头 + httpPost.setHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"); + httpPost.setHeader("Referer", "https://music.163.com/song?id=" + songId); + httpPost.setHeader("Content-Type", "application/x-www-form-urlencoded"); + + // 3. 生成加密参数并构造请求体 + Map encryptedParams = getEncryptedParams(songId); + String requestBody = "params=" + encryptedParams.get("params") + "&encSecKey=" + encryptedParams.get("encSecKey"); + httpPost.setEntity(new StringEntity(requestBody, StandardCharsets.UTF_8)); + + // 4. 发送请求并解析响应 + CloseableHttpResponse response = httpClient.execute(httpPost); + HttpEntity entity = response.getEntity(); + String responseStr = EntityUtils.toString(entity, StandardCharsets.UTF_8); + JSONObject responseJson = JSON.parseObject(responseStr); + + // 5. 返回热门评论数组 + return responseJson.getJSONArray("hotComments"); + + } catch (Exception e) { + System.err.println("爬取失败:" + e.getMessage()); + e.printStackTrace(); + } finally { + try { + httpClient.close(); + } catch (Exception e) { + e.printStackTrace(); + } + } + return new JSONArray(); + } + + /** + * 格式化输出评论 + 保存到CSV + */ + public static void printAndSaveComments(JSONArray hotComments) { + // 1. 格式化打印 + System.out.println("===== 网易云热门评论 ====="); + for (int i = 0; i < hotComments.size(); i++) { + JSONObject comment = hotComments.getJSONObject(i); + JSONObject user = comment.getJSONObject("user"); + String nickname = user.getString("nickname"); + String content = comment.getString("content").replace("\n", " "); + int likedCount = comment.getInteger("likedCount"); + String timeStr = comment.getString("timeStr"); + + System.out.printf("%d. %s:%s(%d赞)- %s%n", + i + 1, nickname, content, likedCount, timeStr); + } + + // 2. 保存到CSV(可选) + /* + try (FileWriter writer = new FileWriter("netease_hot_comments.csv", StandardCharsets.UTF_8)) { + // 写入表头 + writer.write("序号,用户,评论,点赞数,时间\n"); + // 写入评论数据 + for (int i = 0; i < hotComments.size(); i++) { + JSONObject comment = hotComments.getJSONObject(i); + JSONObject user = comment.getJSONObject("user"); + String nickname = user.getString("nickname"); + String content = comment.getString("content").replace("\n", " ").replace(",", ","); + int likedCount = comment.getInteger("likedCount"); + String timeStr = comment.getString("timeStr"); + + writer.write(String.format("%d,%s,%s,%d,%s%n", + i + 1, nickname, content, likedCount, timeStr)); + } + System.out.println("评论已保存到 netease_hot_comments.csv"); + } catch (Exception e) { + System.err.println("保存CSV失败:" + e.getMessage()); + } + */ + } + + // 主方法:测试爬取《晴天》(ID:186016)热门评论 + public static void main(String[] args) { + String songId = "186016"; // 晴天的歌曲ID + JSONArray hotComments = getHotComments(songId); + if (!hotComments.isEmpty()) { + printAndSaveComments(hotComments); + } else { + System.out.println("未获取到评论,请检查歌曲ID或网络"); + } + } +} \ No newline at end of file