【GNZ48-章泽婷应援会】基于Java的SNH48Group应援会机器人(二)获取数据

整个项目最核心的部分就是获取数据了,因为如果获取不到数据,那发送消息都是空谈。我一开始有考虑过使用别人写好的Python项目来获取数据,但找了很多,我发现都是通过分析那些数据接口来模拟发送https请求获取数据。由于不会写Python,无法去拓展别人的代码,于是就用Java构造了一个HTTPS请求类,因为Java的rt.jar这个基础类库中包含有java.net这个网络编程相关的类,通过自己封装一个方便调用的类,来对那些数据接口发送hppts请求,从而获取数据。

 

构造一个Https类

构造一个这个类的目的,就是为了提供简单的方法,将url、请求参数、请求头等信息放入一个https请求中,简便地发送请求,不用重复编写相关代码,即代码复用。

package com.gnz48.zzt.util;

import java.awt.Image;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Reader;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.nio.charset.Charset;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.Map;

import javax.imageio.ImageIO;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;

import org.apache.catalina.webresources.war.Handler;
import org.json.JSONException;
import org.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.gnz48.zzt.confing.MyX509TrustManager;
import com.gnz48.zzt.exception.RuleException;

/**
 * @ClassName: Https
 * @Description: Https操作的工具类
 * @author JuFF_白羽
 * @date 2018年6月12日 上午10:39:42
 */
public class Https {

	private static final Logger LOGGER = LoggerFactory.getLogger(Https.class);

	private String url;

	private Map params;

	private String dataType;

	private String result;

	private Map requestPropertys;

	private String payloadJson;

	public Https() {
	}

	public Https(String url, String dataType, Map params, Map requestPropertys,
			String payloadJson) {
		this.url = url;
		this.dataType = dataType;
		this.params = params;
		this.requestPropertys = requestPropertys;
		this.payloadJson = payloadJson;
	}

	/**
	 * @Title: getUrl
	 * @Description: 获取当前使用的URL
	 * @author JuFF_白羽
	 * @return String
	 */
	public String getUrl() {
		return url;
	}

	/**
	 * @Title: setUrl
	 * @Description: 设置URL
	 * @author JuFF_白羽
	 * @param url
	 *            请求地址
	 * @return Https
	 */
	public Https setUrl(String url) {
		this.url = url;
		return this;
	}

	/**
	 * @Title: getParams
	 * @Description: 获取当前使用的参数
	 * @author JuFF_白羽
	 * @return Map
	 */
	public Map getParams() {
		return params;
	}

	/**
	 * @Title: setParams
	 * @Description: 设置参数
	 * @author JuFF_白羽
	 * @param params
	 *            参数键值对
	 * @return Https
	 */
	public Https setParams(Map params) {
		this.params = params;
		return this;
	}

	/**
	 * @Title: setDataType
	 * @Description: 设置请求方式
	 *               

* 可用"POST","GET" * @author JuFF_白羽 * @param dataType * @return Https 返回类型 */ public Https setDataType(String dataType) { this.dataType = dataType; return this; } /** * @Title: setRequestProperty * @Description: 设置请求头 * @author JuFF_白羽 * @param requestPropertys * 请求头的Map * @return Https 返回类型 */ public Https setRequestProperty(Map requestPropertys) { this.requestPropertys = requestPropertys; return this; } /** * @Title: setPayloadJson * @Description: 设置需要用流写入的json参数 *

* 该方法是为了无法用url?携带参数的请求而编写的,使用输出流将参数写入后台请求中。 * @author JuFF_白羽 * @param payloadJson * json字符串 * @return Https 返回类型 */ public Https setPayloadJson(String payloadJson) { this.payloadJson = payloadJson; return this; } /** * @Title: send * @Description: 发送请求 * @author JuFF_白羽 * @return String 返回类型 */ public String send() { validateRule(this.url); StringBuffer buffer = null; try { // 以SSL的规则创建SSLContext SSLContext sslContext = SSLContext.getInstance("SSL"); TrustManager[] tm = { new MyX509TrustManager() }; // 初始化 sslContext.init(null, tm, new SecureRandom()); // 获取SSLSocketFactory对象 SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory(); URL url = new URL(installParams(this.url, this.params)); HttpsURLConnection urlConn = (HttpsURLConnection) url.openConnection(); urlConn.setDoOutput(true); urlConn.setDoInput(true); // 请求不使用缓存 urlConn.setUseCaches(false); // 设置请求头 if (requestPropertys != null) { for (Map.Entry entry : requestPropertys.entrySet()) { urlConn.setRequestProperty(entry.getKey(), entry.getValue()); } } // 设置请求方式 urlConn.setRequestMethod(this.dataType); // 设置当前实例使用的SSLSoctetFactory urlConn.setSSLSocketFactory(sslSocketFactory); // 设置需要用流写入的请求参数 if (payloadJson != null && !payloadJson.equals("")) { OutputStreamWriter writer = new OutputStreamWriter(urlConn.getOutputStream(), "UTF-8"); writer.write(payloadJson); writer.close(); } urlConn.connect(); // 读取服务器端返回的内容 InputStream is = urlConn.getInputStream(); InputStreamReader isr = new InputStreamReader(is, "utf-8"); BufferedReader br = new BufferedReader(isr); buffer = new StringBuffer(); String line = null; while ((line = br.readLine()) != null) { buffer.append(line); } } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } catch (KeyManagementException e) { e.printStackTrace(); } catch (MalformedURLException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally { new Https(null, null, null, null, null);// 清空参数 } return buffer.toString(); } private static String readAll(Reader rd) throws IOException { StringBuilder sb = new StringBuilder(); int cp; while ((cp = rd.read()) != -1) { sb.append((char) cp); } return sb.toString(); } public static JSONObject readJsonFromUrl(String url) throws IOException, JSONException { InputStream is = new URL(url).openStream(); try { BufferedReader rd = new BufferedReader(new InputStreamReader(is, Charset.forName("UTF-8"))); String jsonText = readAll(rd); JSONObject json = new JSONObject(jsonText); return json; } finally { is.close(); // System.out.println("同时 从这里也能看出 即便return了,仍然会执行finally的!"); } } /** * @Title: validateRule * @Description: 对传入的URL进行必要的校验 * @author JuFF_白羽 * @param url * 请求地址 */ public static void validateRule(String url) { if (url.equals("")) { throw new RuleException("url不能为空!"); } if (!url.startsWith("http://") && !url.startsWith("https://")) { throw new RuleException("url的格式不正确!"); } } /** * @Title: installParams * @Description: 为请求地址增加参数 * @author JuFF_白羽 * @param url * 请求地址 * @param params * 装有参数的Map * @return String 用?带参数的url */ private static String installParams(String url, Map params) { if (params == null) { return url; } int flag = 1; url += "?"; for (Map.Entry param : params.entrySet()) { if (flag > 1) { url += "&"; } url = url + param.getKey() + "=" + param.getValue(); flag += 1; } return url; } // /** // * @Title: getImage // * @Description: 发送请求获取图片 // * @author JuFF_白羽 // * @param path // * 存储地址 // * @throws IOException // */ // public File getImage(String path) { // validateRule(this.url); // StringBuffer buffer = null; // try { // // 以SSL的规则创建SSLContext // SSLContext sslContext = SSLContext.getInstance("SSL"); // TrustManager[] tm = { new MyX509TrustManager() }; // // 初始化 // sslContext.init(null, tm, new SecureRandom()); // // 获取SSLSocketFactory对象 // SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory(); // URL url = new URL(this.installParams(this.url, this.params)); // HttpURLConnection urlConn = (HttpURLConnection) url.openConnection(); // urlConn.setDoOutput(true); // urlConn.setDoInput(true); // // 请求不使用缓存 // urlConn.setUseCaches(false); // // 设置请求头 // if (requestPropertys != null) { // for (Map.Entry entry : requestPropertys.entrySet()) { // urlConn.setRequestProperty(entry.getKey(), entry.getValue()); // } // } // // 设置请求方式 // urlConn.setRequestMethod(this.dataType); // // 设置当前实例使用的SSLSoctetFactory //// urlConn.setSSLSocketFactory(sslSocketFactory); // // 设置需要用流写入的请求参数 // if (payloadJson != null && !payloadJson.equals("")) { // OutputStreamWriter writer = new OutputStreamWriter(urlConn.getOutputStream(), "UTF-8"); // writer.write(payloadJson); // writer.close(); // } // urlConn.connect(); // // 读取服务器端返回的内容 // InputStream is = urlConn.getInputStream(); // int index; // byte[] bytes = new byte[1024]; // FileOutputStream downloadFile = new FileOutputStream(path); // while ((index = is.read(bytes)) != -1) { // downloadFile.write(bytes, 0, index); // downloadFile.flush(); // } // downloadFile.close(); // is.close(); // } catch (IOException e) { // e.printStackTrace(); // } catch (NoSuchAlgorithmException e) { // e.printStackTrace(); // } catch (KeyManagementException e) { // e.printStackTrace(); // } finally { // new Https(null, null, null, null, null);// 清空参数 // } // return null; // } }

主要参数有url、params、dataType、requestPropertys、payloadJson。

private String url接口的地址,相关方法有getUrl()和setUrl(String url)。

private Map params请求参数,是用来放跟在url后面的参数,主要是给GET请求携带的参数使用,相关方法有getParams()和setParams(Map params)。

private String dataType请求方式,即GET、POST等,相关方法有setDataType(String dataType)。

private Map requestPropertys请求头,请求头要正确设置才能得到返回结果集,相关方法有setRequestProperty(Map requestPropertys)。

private String payloadJsonjson参数,是用来放不跟在url后面的参数,主要是给POST请求携带的参数使用,相关方法有setPayloadJson(String payloadJson),其中字符串payloadJson要为有效的json字符串。

在设置完主要参数后,调用send()方法后就可得到json结果集,之后就可以开始遍历json获取里面的数据。在send()方法中有几点需要注意的:第一,安全套接字协议SSLContext使用SSL的实例;第二,new URL(url)调用前,GET携带的参数一定要先放入url中;第三,json参数需要用OutputStreamWriter写入,写入后记得关闭流;第四,读取服务器端返回的内容用输入流读取。


 

解析JSON结果集

我们所需要的数据有些是json对象,有些是存放在json对象中的html,因此并没有可封装的方法,只能一个请求接口一个解析方法。解析json需要导入新包,而解析html虽然可以用解析xml的Java自带的Document相关类,但由于过于繁琐,这里我也导入一个新包来帮助我进行解析。

导入的jar包:



	org.jsoup
	jsoup
	1.11.3



	org.json
	json
	20180130

解析json常用的一些方法:

/* 
 * 获得一个JSON对象,result为调用send()后返回的JSON结果集的字符串。
 * 但result应为有效的JSON字符串,若有多余部分,须裁剪至需要的部分。
 */
JSONObject jsonObject = new JSONObject(result);


/*
 * 获取key对应的Long值。
 */
jsonObject.getLong("memberId");


/*
 * 获取key对应的String值。
 */
jsonObject.getString("memberAvatar");


/*
 * 获取key对应的JSON对象。
 */
jsonObject.getJSONObject("data");


/*
 * 获取key对应的JSON数组。
 */
jsonObject.getJSONArray("cards");


/*
 * JSON对象和Map类似,都是以key和value一一对应,因此keys()可获得一个key的迭代器。
 * 通过while遍历迭代器中的key,以此获取JSON对象中的每个key对应的值。
 */
Iterator iterator = jsonObject.keys();
while (iterator.hasNext()) {// 遍历全体成员信息对象
	String key = (String) iterator.next();
	JSONObject memberObject = jsonObject.getJSONObject(key);// 此处不一定是JSON对象,应实际情况而变
}

HTML进行Document操作的相关文档:jsoup Cookbook(中文版)


 

本项目的轮询

由于Quartz相关博客我已有写,我这里就贴一个工作任务类出来说明一下。

package com.gnz48.zzt.job;

import java.text.ParseException;

import org.json.JSONException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.stereotype.Component;

import com.gnz48.zzt.service.HttpsService;

/**
 * @ClassName: SyncTask
 * @Description: 同步信息任务类
 * @author JuFF_白羽
 * @date 2018年7月27日 下午5:59:14
 */
@Configuration
@Component // 此注解必加
@EnableScheduling // 此注解必加
public class SyncTask {

	public SyncTask() {
	}

	private static final Logger LOGGER = LoggerFactory.getLogger(SyncTask.class);

	/**
	 * Https请求服务
	 */
	@Autowired
	private HttpsService httpsService;

	/**
	 * @Title: syncMember
	 * @Description: 定时任务:每小时同步成员的信息
	 * @author JuFF_白羽
	 */
	public void syncMember() {
		httpsService.syncMember();
	}

	/**
	 * @Title: syncDynamic
	 * @Description: 定时任务:每3分钟同步微博动态
	 * @author JuFF_白羽
	 */
	public void syncDynamic() {
		httpsService.syncDynamic();
	}

	/**
	 * @Title: syncRoomMessage
	 * @Description: 定时任务:每分3钟同步口袋房间消息
	 * @author JuFF_白羽
	 * @throws ParseException
	 * @throws JSONException
	 */
	public void syncRoomMessage() throws JSONException, ParseException {
		httpsService.syncRoomMessage();
	}

	/**
	 * @Title: syncModianPool
	 * @Description: 定时任务:每10分钟同步摩点集资项目
	 * @author JuFF_白羽
	 * @throws ParseException
	 * @throws JSONException
	 */
	public void syncModianPool() throws JSONException, ParseException {
		httpsService.syncModianPool();
	}

}

HttpsService类中具体编写了发送请求并解析返回的结果集的代码,SyncTask类中我并没有把每个同步服务写在一个方法中,而是分多个方法,意味着分了多个不同的轮询任务,这样有利于之后的动态设置任务的拓展。

Quartz相关见:Spring Boot与Quartz整合

 


【GNZ48-章泽婷应援会】基于Java的SNH48Group应援会机器人(一)项目简介

【GNZ48-章泽婷应援会】基于Java的SNH48Group应援会机器人(三)发送消息

你可能感兴趣的:(Java)