Music is a gift to the soul.

A good headset, it seems, is standard for programmers. Of course, sometimes not only for listening to music, just want to tell others: busy, don’t bother me…

There are a lot of music software, why netease cloud music? Because I’m using this. There is no other deal, of course I have to climb her, she will not be very happy, so we should keep quiet better.

Netease cloud music has a web version, so the analysis of the interface is relatively simple. If there is no web version, it is necessary to capture the package, recently found a super easy to use capture package tool, HTTP and HTTPS can be captured

Proxyman Mac version, free, is so much better than blue and white China.

1 Access link

Comments for url so long, need to put the song id splicing behind http://music.163.com/api/v1/resource/comments/R_SO_4_

Song id can click share, copy the link to https://music.163.com/song?id=1350364644

Spell id behind the url of http://music.163.com/api/v1/resource/comments/R_SO_4_1350364644 this is complete for review.

Observe that there is also a parameter called offset, which is used to turn the page

  • Offset = 0 returns 20 reviews + 10 reviews
  • Offset = 1 shows 10 comments from the second one, so add 10 each time

Now that you have the link, it’s time to implement it in code.

Get the song ID

In fact, I wrote the code first, and finally get the id of the song, but let’s keep it simple. Get this link, get id https://music.163.com/song?id=1350364644 first think of the split (), or too young, sometimes links as follows, if landed there will be a userid. As far as I observe, https://music.163.com/song?id=1350364644&userid=110 id seems to be always in the first. At this point, you can only use regular expressions.

1. Regular expressions

Regular expressions, not only ^([0-9]{0,})$, but also lookahead and lookbehind

Concrete is divided into

  • A forward prior assertion must be followed by(? =pattern)
  • Negative prior assertion cannot be followed by(? ! pattern)
  • Must be preceded by a forward trailing assertion(? <=pattern)
  • Negative trailing assertion cannot be preceded(? <! pattern)

The following is a forward after assertion, which means it has to be, right? Id = a string of numbers (? < = \? Id =)(\d+)

Pattern pb = Pattern.compile("(? < = \ \? id=)(\\d+)");
Matcher matcher = pb.matcher(songUrl);
Assert.isTrue(matcher.find(), "Song ID not found!");
String songId = matcher.group();
Copy the code

Http request tool class

In everyday development, sometimes you need to send HTTP or HTTPS requests. The HTTP request utility class can be written in many different ways. Of course, many companies may encapsulate the utility class, the following is a reference to others, and then write a utility class can send HTTP and HTTPS GET, POST requests. It’s easy to use.

Rely on the following

<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>
Copy the code
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 tool class **@author lww
 */
@Slf4j
public class HttpUtils {

	private static final String ENCODE = "UTF-8";

	private HttpUtils(a) {}/* 
       
       
        org.apache.httpcomponents
        
       
        httpclient
        < version > 4.5.10 < / version > < / dependency > < the dependency > < groupId >. Org. Apache httpcomponents < / groupId > The < artifactId > httpmime < / artifactId > < version > 4.5.7 < / version > < / dependency > * /
      

	/** * HTTP ** to send a request for the GET method to the specified URL@paramUrl Indicates the URL of the request *@paramParam Request parameters. The request parameters should be of the form name1=value1&name2=value2. *@paramHeaders can be null *@returnThe response result of the remote resource represented by the 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);
		/ / set the 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;
	}

	/** * sends a request for the GET method HTTPS ** to the specified URL@paramUrl Indicates the URL of the request *@paramParam Request parameters. The request parameters should be of the form name1=value1&name2=value2. *@paramHeaders can be null *@returnThe response result of the remote resource represented by the 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);
		/ / set the 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;
	}

	/** * sends the request form parameter HTTP ** for the POST method to the specified URL@paramUrl Indicates the URL of the request *@paramParam request parameters, request parameters ok? Name1 =value1&name2=value2 After the URL, can also be placed in the PARam. *@paramHeaders can be null *@returnRepresents the response result of the remote resource */
	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(newBasicNameValuePair(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;
	}

	/** * sends the request form parameter HTTPS ** for the POST method to the specified URL@paramUrl Indicates the URL of the request *@paramParam request parameters, request parameters ok? Name1 =value1&name2=value2 After the URL, can also be placed in the PARam. *@paramHeaders can be null *@returnRepresents the response result of the remote resource */
	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(newBasicNameValuePair(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;
	}

	/** * Send post with json string in body requestBody HTTP **@param url     url
	 * @paramParams parameters *@paramHeaders can be 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;
	}

	/** * Send post with json string in body requestBody HTTPS **@param url     url
	 * @paramParams parameters *@paramHeaders can be 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(a) {
		Registry<ConnectionSocketFactory> socketFactoryRegistry =
				RegistryBuilder.<ConnectionSocketFactory>create()
						.register("http", PlainConnectionSocketFactory.INSTANCE)
						.register("https", trustAllHttpsCertificates()).build();
		// Create a ConnectionManager and add the Connection configuration information
		PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(socketFactoryRegistry);
		CloseableHttpClient httpClient = HttpClients.custom().setConnectionManager(connectionManager).build();
		return httpClient;
	}

	private static SSLConnectionSocketFactory trustAllHttpsCertificates(a) {
		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}}}Copy the code

Four no code no BB

Get the code for the comment. The comments are clear. (Paste code has a kind of look at the light of the feeling, shy ah)

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
 * @dateThe 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(), "Song ID not found!");
		// Get the song id
		String songId = matcher.group();
		/ / stitching
		String url = COMMENT_URL + songId;
		// offset = 0 has 20 comments + 10 comments
		// offset = 1 no comments display 10 comments from the second item
		String sb = "? offset=0";
		// Send the request
		String s = HttpUtils.sendPostFormHttps(url + sb, null.null);
		/ / parsing
		CommentsInfoVO vo = JSONObject.parseObject(s, CommentsInfoVO.class);
		// Get total commentsLong total = vo.getTotal(); Assert.isTrue(total ! =null."Resources don't exist!");
		// Count the pages
		Long page = (total % 10= =0 ? total / 10 : total / 10 + 1);
		// Used to store comments
		StringBuilder res = new StringBuilder(1024);
		int i = 0;
		while (i < page) {
			// Empty the page number in the link
			sb = sb.replace(i + ""."");
			// Parse comments
			CommentsInfoVO commentsInfoVO = JSONObject.parseObject(s, CommentsInfoVO.class);
			List<CommentsVO> hotComments = commentsInfoVO.getHotComments();
			// Create data
			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();
			// Comment on the assembled data
			for (CommentsVO comment : comments) {
				res.append("= = = = = = = = = = = = = = = = = = = = = = = =").append("\n");
				res./*append(comment.getUser().getNickname()).append(" : ").*/append(comment.getContent()).append("\n");
			}
			i += 10;
			if (i > 50) {
				// Avoid crawling too much
				break;
			}
			// To get the next page, add 10
			sb = "? offset=" + i;
			// The request is sent and the loop goes through again, parsed again
			s = HttpUtils.sendPostFormHttps(url + sb, null.null);
		}
		returnres.toString(); }}Copy the code

The results of tao Junxi’s Reconciliation are as follows:

======================== a lot of time the pain of our heart is because they put their own however, when we are reluctant to reconcile with their own reconciliation is not willing to reconcile with the past is not willing to reconcile with life in those countless night another oneself always wait for an opportunity to run out to disturb us Struggle and conflict = = = = = = = = = = = = = = = = = = = = = = = = she is my disease was destined to be my person = = = = = = = = = = = = = = = = = = = = = = = = Nietzsche: “No need for constant sensitivity, Dull sometimes is the virtue “= = = = = = = = = = = = = = = = = = = = = = = = ambiguous above those few seconds really like love It’s a pity that how happy life is a circle above Head is much sad = = = = = = = = = = = = = = = = = = = = = = = = I want to sleep with you But don’t want to sleep you = = = = = = = = = = = = = = = = = = = = = = = = advice, don’t go to close to the heart has many years of wounded people, not to think of myself as to save his saints. Your warmth and love, can only get him a thank you and sorry. In the same way, don’t use someone else to heal your wounds before they heal. Love more treasure, to yourself to others. = = = = = = = = = = = = = = = = = = = = = = = = after parting I don’t welcome wrapped in a white coat Once again into the arms of black Shattered glass = = = = = = = = = = = = = = = = = = = = = = = = to learn and the settlement, but to day in the future society and their settlement, Pull your hair to pull yourself out of the mud and reconcile with the injustice of life, digest the injustice and malevolence suffered. But most of the time, I just make peace with the unhappiness, make excuses for the sadness, but sometimes I get sucked back in, – day in fall into the bottomless pit, good night = = = = = = = = = = = = = = = = = = = = = = = = thank you for your support/cute = = = = = = = = = = = = = = = = = = = = = = = = Total want to learn to yourself and your settlement as I said forget it, not because I don’t want to But I again how efforts are useless It’s better to reconcile with yourself.

Write in the last

I am currently working on an open source project to integrate some examples of third-party public APIS. Such as Tencent’s public API, Baidu’s API and so on. Make a simple call example.

Full Project Address

Since I can’t log on to Github right now, the code is in the code cloud

A simple project has been built with the following interfaces

  • Amap queries addresses based on keywords
  • Amap weather query (to simplify the development, only Beijing, Hangzhou weather query)
  • Amap gets addresses based on IP
  • Netease Cloud Music to obtain song reviews
  • Short chain generated

Everyone is welcome to jointly maintain, the key and so on can use individual application to test. Don’t disclose the company’s or your own non-public ones. Pay attention to personal data security.