一个好的耳机,好像是程序员的标配。固然有时候不光是为了听音乐,只是想告诉别人:忙碌中,莫挨老子...java
音乐软件有不少,为何说网易云音乐呢?由于我用的是这个。没有什么其余交易,固然我都要爬她了,她确定不会很爽,因此你们仍是悄悄的比较好。git
网易云音乐有网页版,因此分析接口仍是比较简单的。若是没有网页版,就要抓包了,最近发现了一款超级好用的抓包工具,http和https均可以抓程序员
Proxyman Mac版的,免费的,比青花瓷好用太多了。github
获取评论的url长这样,须要把歌曲id拼接在后面
http://music.163.com/api/v1/resource/comments/R_SO_4_
正则表达式
歌曲id能够点击分享的时候,复制连接
https://music.163.com/song?id=1350364644
spring
把id拼后面
http://music.163.com/api/v1/resource/comments/R_SO_4_1350364644
这就是完整的获取评论的url。apache
观察发现还有一个参数offset
,用来翻页json
连接有了,接下来就是用代码来实现了。api
其实我是先写好了代码,最后才获取歌曲的id的,不过仍是先从简单的说吧。 拿到这个连接,获取id
https://music.163.com/song?id=1350364644
首先想到的是split(),仍是太年轻啊,有时候连接像下面这样,若是登录了会有userid。
https://music.163.com/song?id=1350364644&userid=110
据我仔细的观察,id好像始终在第一个。
这个时候只能用正则表达式了。安全
正则表达式,不光是 ^([0-9]{0,})$
,还有 先行断言(lookahead)和后行断言(lookbehind)
具体分为
(?=pattern)
(?!pattern)
(?<=pattern)
(?<!pattern)
下面这个就是 正向后行断言 ,意思是前面必须有?id=
的一段数字
(?<=\?id=)(\d+)
因此把复制的连接传进来就能够取到id了
Pattern pb = Pattern.compile("(?<=\\?id=)(\\d+)");
Matcher matcher = pb.matcher(songUrl);
Assert.isTrue(matcher.find(), "未找到歌曲id!");
String songId = matcher.group();
复制代码
在平常的开发中,有时候须要发送http
或者https
请求。而http
请求的工具类也有不少种写法。
固然,不少公司可能封装好了工具类,下面这个是我本身参考了一些别人的写法, 而后写的一个能够发送http
和https
的get
,post
请求的工具类。使用也很简单。
依赖以下
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.10</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpmime</artifactId>
<version>4.5.7</version>
</dependency>
复制代码
package com.ler.pai.util;
import com.alibaba.fastjson.JSONObject;
import java.io.IOException;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpEntity;
import org.apache.http.NameValuePair;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
/** * http 工具类 * * @author lww */
@Slf4j
public class HttpUtils {
private static final String ENCODE = "UTF-8";
private HttpUtils() {
}
/* <dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpclient</artifactId> <version>4.5.10</version> </dependency> <dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpmime</artifactId> <version>4.5.7</version> </dependency> */
/** * 向指定URL发送GET方法的请求 http * * @param url 发送请求的URL * @param param 请求参数,请求参数应该是 name1=value1&name2=value2 的形式。 * @param headers 可为null * @return URL 所表明远程资源的响应结果 */
public static String sendGetHttp(String url, String param, Map<String, String> headers) {
HttpGet httpGet = new HttpGet(StringUtils.isBlank(param) ? url : url + "?" + param);
headers = initHeader(headers);
//设置header
for (Map.Entry<String, String> entry : headers.entrySet()) {
httpGet.setHeader(entry.getKey(), entry.getValue());
}
String content = null;
try (CloseableHttpClient closeableHttpClient = HttpClientBuilder.create().build()) {
CloseableHttpResponse httpResponse = closeableHttpClient.execute(httpGet);
HttpEntity entity = httpResponse.getEntity();
content = EntityUtils.toString(entity, ENCODE);
} catch (IOException e) {
log.error("HttpRequest_getForm_e:{}", e);
}
return content;
}
/** * 向指定URL发送GET方法的请求 https * * @param url 发送请求的URL * @param param 请求参数,请求参数应该是 name1=value1&name2=value2 的形式。 * @param headers 可为null * @return URL 所表明远程资源的响应结果 */
public static String sendGetHttps(String url, String param, Map<String, String> headers) {
HttpGet httpGet = new HttpGet(StringUtils.isBlank(param) ? url : url + "?" + param);
headers = initHeader(headers);
//设置header
for (Map.Entry<String, String> entry : headers.entrySet()) {
httpGet.setHeader(entry.getKey(), entry.getValue());
}
String content = null;
try (CloseableHttpClient closeableHttpClient = sslHttpClientBuild()) {
CloseableHttpResponse httpResponse = closeableHttpClient.execute(httpGet);
HttpEntity entity = httpResponse.getEntity();
content = EntityUtils.toString(entity, ENCODE);
} catch (IOException e) {
log.error("HttpRequest_getForm_e:{}", e);
}
return content;
}
/** * 向指定 URL 发送POST方法的请求 form参数 http * * @param url 发送请求的 URL * @param param 请求参数,请求参数能够 ?name1=value1&name2=value2 拼在url后,也能够放在param中。 * @param headers 可为null * @return 所表明远程资源的响应结果 */
public static String sendPostFormHttp(String url, Map<String, String> param, Map<String, String> headers) {
HttpPost httpPost = new HttpPost(url);
headers = initHeader(headers);
headers.put("Content-Type", "application/x-www-form-urlencoded");
for (Map.Entry<String, String> entry : headers.entrySet()) {
httpPost.setHeader(entry.getKey(), entry.getValue());
}
String content = null;
List<NameValuePair> pairList = new ArrayList<>();
if (param != null) {
for (Map.Entry<String, String> entry : param.entrySet()) {
pairList.add(new BasicNameValuePair(entry.getKey(), entry.getValue()));
}
}
try (CloseableHttpClient closeableHttpClient = HttpClientBuilder.create().build()) {
httpPost.setEntity(new UrlEncodedFormEntity(pairList, ENCODE));
CloseableHttpResponse httpResponse = closeableHttpClient.execute(httpPost);
HttpEntity entity = httpResponse.getEntity();
content = EntityUtils.toString(entity, ENCODE);
} catch (IOException e) {
log.error("HttpRequest_getForm_e:{}", e);
}
return content;
}
/** * 向指定 URL 发送POST方法的请求 form参数 https * * @param url 发送请求的 URL * @param param 请求参数,请求参数能够 ?name1=value1&name2=value2 拼在url后,也能够放在param中。 * @param headers 可为null * @return 所表明远程资源的响应结果 */
public static String sendPostFormHttps(String url, Map<String, String> param, Map<String, String> headers) {
HttpPost httpPost = new HttpPost(url);
headers = initHeader(headers);
headers.put("Content-Type", "application/x-www-form-urlencoded");
for (Map.Entry<String, String> entry : headers.entrySet()) {
httpPost.setHeader(entry.getKey(), entry.getValue());
}
String content = null;
List<NameValuePair> pairList = new ArrayList<>();
if (param != null) {
for (Map.Entry<String, String> entry : param.entrySet()) {
pairList.add(new BasicNameValuePair(entry.getKey(), entry.getValue()));
}
}
try (CloseableHttpClient closeableHttpClient = sslHttpClientBuild()) {
httpPost.setEntity(new UrlEncodedFormEntity(pairList, ENCODE));
CloseableHttpResponse httpResponse = closeableHttpClient.execute(httpPost);
HttpEntity entity = httpResponse.getEntity();
content = EntityUtils.toString(entity, ENCODE);
} catch (IOException e) {
log.error("HttpRequest_getForm_e:{}", e);
}
return content;
}
/** * 发送post,参数为json字符串 放在body中 requestBody http * * @param url url * @param params 参数 * @param headers 可为null */
public static String sendPostJsonHttp(String url, JSONObject params, Map<String, String> headers) {
HttpPost httpPost = new HttpPost(url);
headers = initHeader(headers);
headers.put("Content-Type", "application/json");
for (Map.Entry<String, String> entry : headers.entrySet()) {
httpPost.setHeader(entry.getKey(), entry.getValue());
}
StringEntity stringEntity = new StringEntity(params.toString(), ENCODE);
httpPost.setEntity(stringEntity);
String content = null;
try (CloseableHttpClient closeableHttpClient = HttpClientBuilder.create().build()) {
CloseableHttpResponse httpResponse = closeableHttpClient.execute(httpPost);
HttpEntity entity = httpResponse.getEntity();
content = EntityUtils.toString(entity, ENCODE);
closeableHttpClient.close();
} catch (IOException e) {
log.error("HttpUtil_sendPostJsonHttp_e:{}", e);
}
return content;
}
/** * 发送post,参数为json字符串 放在body中 requestBody https * * @param url url * @param params 参数 * @param headers 可为null */
public static String sendPostJsonHttps(String url, JSONObject params, Map<String, String> headers) {
HttpPost httpPost = new HttpPost(url);
headers = initHeader(headers);
headers.put("Content-Type", "application/json");
for (Map.Entry<String, String> entry : headers.entrySet()) {
httpPost.setHeader(entry.getKey(), entry.getValue());
}
StringEntity stringEntity = new StringEntity(params.toString(), ENCODE);
httpPost.setEntity(stringEntity);
String content = null;
try (CloseableHttpClient closeableHttpClient = sslHttpClientBuild()) {
CloseableHttpResponse httpResponse = closeableHttpClient.execute(httpPost);
HttpEntity entity = httpResponse.getEntity();
content = EntityUtils.toString(entity, ENCODE);
closeableHttpClient.close();
} catch (IOException e) {
log.error("HttpUtil_sendPostJsonHttps_e:{}", e);
}
return content;
}
private static Map<String, String> initHeader(Map<String, String> headers) {
if (headers == null) {
headers = new HashMap<>(16);
}
headers.put("accept", "*/*");
headers.put("connection", "Keep-Alive");
headers.put("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
return headers;
}
public static CloseableHttpClient sslHttpClientBuild() {
Registry<ConnectionSocketFactory> socketFactoryRegistry =
RegistryBuilder.<ConnectionSocketFactory>create()
.register("http", PlainConnectionSocketFactory.INSTANCE)
.register("https", trustAllHttpsCertificates()).build();
//建立ConnectionManager,添加Connection配置信息
PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(socketFactoryRegistry);
CloseableHttpClient httpClient = HttpClients.custom().setConnectionManager(connectionManager).build();
return httpClient;
}
private static SSLConnectionSocketFactory trustAllHttpsCertificates() {
SSLConnectionSocketFactory socketFactory = null;
TrustManager[] trustAllCerts = new TrustManager[1];
TrustManager tm = new Mitm();
trustAllCerts[0] = tm;
SSLContext sc;
try {
sc = SSLContext.getInstance("TLS");
sc.init(null, trustAllCerts, null);
socketFactory = new SSLConnectionSocketFactory(sc, NoopHostnameVerifier.INSTANCE);
} catch (NoSuchAlgorithmException | KeyManagementException e) {
log.error("HttpUtil_trustAllHttpsCertificates_e:{}", e);
}
return socketFactory;
}
static class Mitm implements TrustManager, X509TrustManager {
@Override
public X509Certificate[] getAcceptedIssuers() {
return null;
}
@Override
public void checkServerTrusted(X509Certificate[] certs, String authType) {
//don't check
}
@Override
public void checkClientTrusted(X509Certificate[] certs, String authType) {
//don't check
}
}
}
复制代码
获取评论的代码。注释很清楚了。(贴了代码有种被看光光的感受,害羞啊)
package com.ler.pai.service.impl;
import com.alibaba.fastjson.JSONObject;
import com.ler.pai.service.MusicService;
import com.ler.pai.util.HttpUtils;
import com.ler.pai.vo.CommentsInfoVO;
import com.ler.pai.vo.CommentsVO;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;
/** * @author lww * @date 2020-01-30 18:09 */
@Service
@Slf4j
public class MusicServiceImpl implements MusicService {
private Pattern pb = Pattern.compile("(?<=\\?id=)(\\d+)");
private static final String COMMENT_URL = "http://music.163.com/api/v1/resource/comments/R_SO_4_";
@Override
public String getComment(String songUrl) {
log.info("MusicServiceImpl_getComment_songUrl:{}", songUrl);
Matcher matcher = pb.matcher(songUrl);
Assert.isTrue(matcher.find(), "未找到歌曲id!");
//获取歌曲id
String songId = matcher.group();
//拼接
String url = COMMENT_URL + songId;
// offset = 0 有 20个热评 + 10 条评论
// offset = 1 无热评 从 第二条显示10 条评论
String sb = "?offset=0";
//发送请求
String s = HttpUtils.sendPostFormHttps(url + sb, null, null);
//解析
CommentsInfoVO vo = JSONObject.parseObject(s, CommentsInfoVO.class);
//获取总评论数
Long total = vo.getTotal();
Assert.isTrue(total != null, "资源不存在!");
//计算页数
Long page = (total % 10 == 0 ? total / 10 : total / 10 + 1);
//用于存放评论
StringBuilder res = new StringBuilder(1024);
int i = 0;
while (i < page) {
//先把连接里 页数置空
sb = sb.replace(i + "", "");
//解析评论
CommentsInfoVO commentsInfoVO = JSONObject.parseObject(s, CommentsInfoVO.class);
List<CommentsVO> hotComments = commentsInfoVO.getHotComments();
//热评 拼装数据
if (hotComments != null) {
for (CommentsVO hotComment : hotComments) {
res.append("========================").append("\n");
res./*append(hotComment.getUser().getNickname()).append(" : ").*/append(hotComment.getContent()).append("\n");
}
}
List<CommentsVO> comments = commentsInfoVO.getComments();
//评论 拼装数据
for (CommentsVO comment : comments) {
res.append("========================").append("\n");
res./*append(comment.getUser().getNickname()).append(" : ").*/append(comment.getContent()).append("\n");
}
i += 10;
if (i > 50) {
//避免爬取太多
break;
}
//要获取下一页,须要加10
sb = "?offset=" + i;
//发送请求 而后会再次进入循环,再次解析
s = HttpUtils.sendPostFormHttps(url + sb, null, null);
}
return res.toString();
}
}
复制代码
========================
不少时候 咱们心里的痛苦 都是由于本身放不过本身 然而 当咱们迟迟不愿与本身和解 不愿与过往和解 不愿与生活和解的时候 在那些数不清的黑夜里 另外一个本身老是乘机跑出来扰乱 带给咱们 纠缠 挣扎和矛盾
========================
她是个人病 注定成不了个人人
========================
尼采:“不需时刻敏感,迟钝有时即为美德”
========================
暧昧上头的那几秒真的像极了爱情 惋惜人生就是一个圆 上头有多快乐 下头就有多难过
========================
我想和你一块儿睡 但不想睡你
========================
劝你们,不要去接近心中有多年执念伤痕累累的人,不要把本身想象成拯救他的圣人。你的温暖和爱,只能换来他一句谢谢你和对不起。 同理,也不要在本身伤疤未好前用别人疗伤。感情多珍重,对己对他人。
========================
离别后 我裹上并不讨喜的白色外衣 再次投入黑色的怀抱 此地无银三百两
========================
学会和过去和解吧,日子但是过的未来的 学会和本身和解吧,揪着本身的头发也要把本身从泥地里拔起来 和生活的不公和解吧,消化委屈和遭受的恶意。让本身变得再有力量一点强大一点 但多数状况下,我只会跟不快乐和解,替难过找借口说服本身,可有时候我仍是会被反噬进去,掉进无底洞里
——日签晚安
========================
谢谢你们的支持[可爱]
========================
人啊 总要学会本身和本身和解 就像我说的算了吧不是由于我不想要了 而是我再怎么努力也没用 不如自我和解
最近在构思一个开源项目,主要是整合一些第三方公共API接口的例子。
好比腾讯的公共API,百度的API等等。作一个简单的调用例子。
因为如今登不了Github,代码在码云
如今已经搭了一个简单的项目,里面有如下接口
欢迎你们来共同维护,密钥等能够用我的申请的来测试。
不要泄露公司使用的或者本身的不可公开的。
注意保护我的数据安全。