微信公众号文章拉取

微信公众号文章拉取

Posted by wenfengSAT on March 20, 2019

做了一个简单的微信公众号文章拉取功能,做个简单记录

Http请求工具类


package com.asiainfo.vehicle.modules.community.util;

import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
 
/**
 * 
 * @Description: Http请求工具类
 * @author [ Wenfeng.Huang ] on [2018年10月10日下午7:44:50]
 * @Modified By: [修改人] on [修改日期] for [修改说明]
 *
 */

public class HttpClientUtil {
	
	private static Logger logger = LoggerFactory.getLogger(HttpClientUtil.class);
 
	public static JSONObject doGet(String url) {
		// 指定get请求
		HttpGet httpGet = new HttpGet(url);
		// 创建httpclient
		@SuppressWarnings("resource")
		HttpClient httpClient = new DefaultHttpClient();
		// 发送请求
		HttpResponse httpResponse;
		//返回的json
		JSONObject jsonObject = null;
		try {
			httpResponse = httpClient.execute(httpGet);
			// 验证请求是否成功
			if (httpResponse.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
				// 得到请求响应信息
				String str = EntityUtils.toString(httpResponse.getEntity(),
						"utf-8");
				// 返回json
				jsonObject = JSON.parseObject(str);
			}
		} catch (ClientProtocolException e) {
			logger.error("HttpClientUtil =====get请求微信失败!");
		} catch (Exception e) {
			logger.error("HttpClientUtil =====get请求微信失败!");
		}
		return jsonObject;
	}
	
	
	public static JSONObject doPost(String url, JSONObject jsonData) {
		// 指定Post请求
		HttpPost httpPost = new HttpPost(url);
		// 创建httpclient
		@SuppressWarnings("resource")
		HttpClient httpClient = new DefaultHttpClient();
		// 发送请求
		HttpResponse httpResponse;
		// 返回的json
		JSONObject jsonObject = null;
		// 封装post请求数据
		StringEntity entity = new StringEntity(jsonData.toString(), "utf-8");
		httpPost.setEntity(entity);
		try {
			// 发送请求
			httpResponse = httpClient.execute(httpPost);
			// 判断请求是否成功
			if(httpResponse.getStatusLine().getStatusCode()==HttpStatus.SC_OK){
				// 得到请求响应信息
				String str = EntityUtils.toString(httpResponse.getEntity(), "utf-8");
				// 返回json
				jsonObject = JSON.parseObject(str);
			}
		} catch (ClientProtocolException e) {
			logger.error("HttpClientUtil =====post请求微信失败!");
		} catch (Exception e) {
			logger.error("HttpClientUtil =====post请求微信失败!");
		}
		return jsonObject;
	}
	
}

请求微信接口工具类


package com.asiainfo.vehicle.modules.community.util;

import org.springframework.stereotype.Component;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;

/**
 * 
 * @Description: 请求微信接口工具类
 * 
 * @author [ Wenfeng.Huang ] on [2018年10月10日下午7:46:05]
 * @Modified By: [修改人] on [修改日期] for [修改说明]
 *
 */
@Component
public class WeixinUtil {

	private static final String ACCESS_TOKEN_URL = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APP_ID&secret=APP_SECRET";
	private static final String CRATE_QRCODE_URL = "https://api.weixin.qq.com/device/getqrcode?access_token=accessToken&product_id=productId";
	private static final String COMPEL_BIND_URL = "https://api.weixin.qq.com/device/compel_bind?access_token=accessToken";
	private static final String COMPEL_UNBIND_URL = "https://api.weixin.qq.com/device/compel_unbind?access_token=accessToken";
	private static final String BIND_STATUS_URL = "https://api.weixin.qq.com/device/get_stat?access_token=accessToken&device_id=deviceId";
	private static final String GETCALLBACKIP_URL = "https://api.weixin.qq.com/cgi-bin/getcallbackip?access_token=accessToken";
	private static final String GETUSERINFO_URL = "https://api.weixin.qq.com/cgi-bin/user/info?access_token=accessToken&openid=openId&lang=zh_CN";
	private static final String GET_MATERIALCOUNT = "https://api.weixin.qq.com/cgi-bin/material/get_materialcount?access_token=accessToken";
	private static final String GET_BATCHGET_MATERIAL = "https://api.weixin.qq.com/cgi-bin/material/batchget_material?access_token=accessToken";
	
	
	
	/**
	 * 获取token
	 * 
	 * @return
	 */
	public static String getAccessToken(String appId, String appSecret) {
		String url = ACCESS_TOKEN_URL.replace("APP_ID", appId).replace("APP_SECRET", appSecret);
		JSONObject result = HttpClientUtil.doGet(url);
		return result.getString("access_token");
	}

	/**
	 * 验证token是否有效
	 * 
	 * @param token
	 * @return
	 * 
	 * 		{"errcode":40001,"errmsg":"invalid credential, access_token is
	 *         invalid or not latest hint: [TCmz5a03344694!]"}
	 */
	public static boolean validateAccessToken(String token) {
		String url = GETCALLBACKIP_URL.replace("accessToken", token);
		JSONObject result = HttpClientUtil.doGet(url);
		if (result.toJSONString().contains("ip_list") && !result.toJSONString().contains("errmsg")) {
			return true;
		} else {
			return false;
		}
	}

	/**
	 * 创建绑定二维码
	 * 
	 * @param token
	 * @return
	 */
	public static JSONObject createQrcode(String token, String productId) {
		String url = CRATE_QRCODE_URL.replace("accessToken", token).replace("productId", productId);
		JSONObject result = HttpClientUtil.doGet(url);
		return result;
	}

	/**
	 * 强制绑定用户和设备
	 * 
	 * @param token
	 * @param deviceId
	 * @param openId
	 * @return {base_resp:{"errcode": 0,"errmsg":"ok"}} {base_resp:{"errcode":
	 *         -1,"errmsg":"system error"}}
	 */
	public static JSONObject compelBind(String token, String deviceId, String openId) {
		JSONObject jsonData = new JSONObject();
		String url = COMPEL_BIND_URL.replace("accessToken", token);
		jsonData.put("device_id", deviceId);
		jsonData.put("openid", openId);
		JSONObject json = HttpClientUtil.doPost(url, jsonData);
		JSONObject result = JSON.parseObject(json.getString("base_resp"));
		return result;
	}

	/**
	 * 强制解绑用户和设备
	 * 
	 * @param token
	 * @param deviceId
	 * @param openId
	 * @return
	 */
	public static JSONObject compelUnbind(String token, String deviceId, String openId) {
		JSONObject jsonData = new JSONObject();
		String url = COMPEL_UNBIND_URL.replace("accessToken", token);
		jsonData.put("device_id", deviceId);
		jsonData.put("openid", openId);
		JSONObject json = HttpClientUtil.doPost(url, jsonData);
		JSONObject result = JSON.parseObject(json.getString("base_resp"));
		return result;
	}

	/**
	 * 设备状态查询 status 0:未授权 1:已经授权(尚未被用户绑定) 2:已经被用户绑定 3:属性未设置
	 * 
	 * @param token
	 * @param deviceId
	 * @return
	 */
	public static JSONObject getBingStatus(String token, String deviceId) {
		String url = BIND_STATUS_URL.replace("accessToken", token).replace("deviceId", deviceId);
		JSONObject result = HttpClientUtil.doGet(url);
		return result;
	}
	
	/**
	 * 通过openId获取微信用户信息
	 * @param token
	 * @param openId
	 * @return
	 */
	public static JSONObject getUserInfo(String token, String openId) {
		String url = GETUSERINFO_URL.replace("accessToken", token).replace("openId", openId);
		JSONObject result = HttpClientUtil.doGet(url);
		return result;
	}
	
	/**
	 * 获取公众号素材总数
	 * @param token
	 * @return
	 */
	public static Integer getMaterialCount(String token) {
		String url = GET_MATERIALCOUNT.replace("accessToken", token);
		JSONObject result = HttpClientUtil.doGet(url);
		if(result.toJSONString().contains("errcode")) {
			return null;
		}
		int count = result.getInteger("news_count");
		return count;
	}
	
	/**
	 * 获取微信素材文章
	 * @param token
	 * @param pageIndex
	 * @param pageCount
	 * @return
	 */
	public static JSONObject getBatchgetMaterial(String token,int pageIndex, int pageCount) {
		JSONObject jsonData = new JSONObject();
		String url = GET_BATCHGET_MATERIAL.replace("accessToken", token);
		jsonData.put("type", "news");
		jsonData.put("offset", pageIndex);
		jsonData.put("count", pageCount);
		JSONObject result = HttpClientUtil.doPost(url, jsonData);
		return result;
	}

}



package com.asiainfo.vehicle.modules.community.web;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;

import javax.servlet.http.HttpServletRequest;

import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.ModelAndView;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.asiainfo.common.IDataResult;
import com.asiainfo.vehicle.common.utils.CommonsService;
import com.asiainfo.vehicle.common.web.controller.BaseController;
import com.asiainfo.vehicle.modules.community.util.WeixinUtil;
import com.ibm.icu.text.SimpleDateFormat;

/**
 * 
 * @Description: 微信公众号文章功能
 * 
 * @author [ Wenfeng.Huang ] on [2019年2月20日下午4:07:13]
 * @Modified By: [修改人] on [修改日期] for [修改说明]
 *
 */
@Controller
public class ArticleManagementController extends BaseController {

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

	@Value("${appId}")
	private String appId;
	@Value("${appSecret}")
	private String appSecret;

	private static final String WEIXIN_REQUEST_TOKEN_KEY = "WEIXIN_REQUEST_ARTICLE_TOKEN_KEY";
	private static final int WEIXIN_MATERIAL_PAGECOUNT = 15;

	@Autowired
	private RedisTemplate<String, String> redisTemplate;
	@Autowired
	private ArticleDao articleDao;




	/**
	 * 下载素材文章
	 * 
	 * @param request
	 * @return
	 * @throws IOException
	 */
	@GetMapping(value = "/community/article/downArticle")
	@ResponseBody
	public Map<String, Object> downArticle(HttpServletRequest request) throws IOException {
		boolean flag = false;
		String token = getWeixinToken();
		// 公众号文章总数
		Integer weixinArticleCount = WeixinUtil.getMaterialCount(token);
		Map<String, Object> result = new HashMap<String, Object>();
		if (weixinArticleCount == null) {
			result.put("code", 100);
			result.put("msg", "查询失败!");
			return result;
		}
		if (weixinArticleCount == 0) {
			result.put("code", 101);
			result.put("msg", "没有更新的文章!");
			return result;
		}
		Integer dbArticleCount = queryDbArticleCount();
		if (dbArticleCount == null) {
			result.put("code", 102);
			result.put("msg", "查询数据库文章总数失败!");
			return result;
		}
		if (weixinArticleCount <= dbArticleCount) {
			result.put("code", 101);
			result.put("msg", "没有最新的文章!");
			return result;
		}
		/**
		 * 计算需要从微信公众号拉取多少次
		 */
		int pageNumber = getPageNumber((weixinArticleCount - dbArticleCount), WEIXIN_MATERIAL_PAGECOUNT);
		for (int i = 0; i < pageNumber; i++) {
			int offset = dbArticleCount + i * WEIXIN_MATERIAL_PAGECOUNT;
			flag = downArticle(token, offset);
		}
		if (flag) {
			result.put("code", 0);
			result.put("msg", "下载成功!");
			return result;
		} else {
			result.put("code", 100);
			result.put("msg", "下载失败!");
			return result;
		}
	}

	/**
	 * 记录素材
	 * 
	 * @param param
	 * @return
	 */
	public boolean recordArticleHeader(Map<String, Object> param) {
		//recordArticleHeader,存入数据库
		return true;
	}

	/**
	 * 记录文章
	 * 
	 * @param param
	 * @return
	 */
	public boolean recordArticleTrailer(JSONArray jsonArticles, String pId, Date updateTime) {
		Map<String, Object> params = new HashMap<String, Object>();
		List<Map<String, Object>> paramList = new ArrayList<Map<String, Object>>();
		for (int i = 0; i < jsonArticles.size(); i++) {
			JSONObject article = (JSONObject) jsonArticles.get(i);
			Map<String, Object> param = new HashMap<String, Object>();
			String id = get18UUIDString();
			param.put("ID", id);
			param.put("PARENT_ID", pId);
			param.put("AUTHOR", article.getString("author"));
			param.put("DIGEST", article.getString("digest"));
			param.put("TITLE", article.getString("title"));
			param.put("URL", article.getString("url"));
			param.put("CONTENT_SOURCE_URL", article.getString("content_source_url"));
			param.put("SHOW_COVER_PIC", article.getInteger("show_cover_pic"));
			param.put("THUMB_MEDIA_ID", article.getString("thumb_media_id"));
			param.put("THUMB_URL", article.getString("thumb_url"));
			param.put("UPDATE_TIME", updateTime);
			param.put("FILE_NAME", "");
			if(i == 0) {
				param.put("IS_HEAD", 1);
			}else {
				param.put("IS_HEAD", 0);
			}
			paramList.add(param);
		}
		params.put("resultList", paramList);
		//recordArticleTrailer,存入数据库
		return true;
	}

	/**
	 * 处理更新素材文章逻辑
	 * 
	 * @return
	 */
	public boolean downArticle(String token, int offset) {
		JSONObject materialObject = WeixinUtil.getBatchgetMaterial(token, offset,
				WEIXIN_MATERIAL_PAGECOUNT);
		JSONArray jsonMaterials = materialObject.getJSONArray("item");
		for (int i = 0; i < jsonMaterials.size(); i++) {
			JSONObject material = (JSONObject) jsonMaterials.get(i);
			Map<String, Object> param = new HashMap<String, Object>();
			JSONObject content = (JSONObject) material.getJSONObject("content");
			String id = get18UUIDString();
			param.put("ID", id);
			param.put("MEDIA_ID", material.getString("media_id"));
			param.put("CREATE_TIME", new Date(content.getLong("create_time") * 1000L));
			param.put("UPDATE_TIME", new Date(material.getLong("update_time") * 1000L));
			param.put("IS_DISPLAY", 0);
			boolean recordArticleHeaderFlag = recordArticleHeader(param);
			if (!recordArticleHeaderFlag) {
				return false;
			}
			JSONArray jsonArticles = content.getJSONArray("news_item");
			Date updateTime = new Date(content.getLong("update_time") * 1000L);
			boolean recordArticleTrailerFlag = recordArticleTrailer(jsonArticles, id, updateTime);
			if (!recordArticleTrailerFlag) {
				return false;
			}
		}
		return true;
	}

	/**
	 * 查询数据库文章数量
	 * 
	 * @param request
	 * @return
	 * @throws IOException
	 */
	public Integer queryDbArticleCount() throws IOException {
		Map<String, Object> resultMap = articleDao.queryDbArticleCount();//查询数据里的文章总数
		Object count = resultMap.get("ARTICLE_COUNT");
		return Integer.valueOf(count.toString());
	}

	/**
	 * 计算查询页数
	 * 
	 * @param total
	 * @param pageCount
	 * @return
	 */
	public static int getPageNumber(int total, int pageCount) {
		if(total <= 0) {
			return 0;
		}
		int pageNumber = total / pageCount;
		if ((total % pageCount) == 0) {
			return pageNumber;
		}
		return pageNumber + 1;
	}

	/**
	 * 获取微信token
	 * 
	 * @return
	 */
	public String getWeixinToken() {
		ValueOperations<String, String> opsForValue = redisTemplate.opsForValue();
		String token = opsForValue.get(WEIXIN_REQUEST_TOKEN_KEY);
		if (!"".equals(token) && token != null) {
			boolean flag = WeixinUtil.validateAccessToken(token);
			if (!flag) {
				String accessToken = WeixinUtil.getAccessToken(appId, appSecret);
				opsForValue.set(WEIXIN_REQUEST_TOKEN_KEY, accessToken);
			}
		} else {
			String accessToken = WeixinUtil.getAccessToken(appId, appSecret);
			opsForValue.set(WEIXIN_REQUEST_TOKEN_KEY, accessToken);
		}
		return opsForValue.get(WEIXIN_REQUEST_TOKEN_KEY);
	}

	/**
	 * 生成主键
	 *
	 * @return
	 * @throws Exception
	 */
	public String get18UUIDString() {
		Date date = new Date();
		SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMddHHmmss");
		String dateStr = dateFormat.format(date);
		StringBuffer result = new StringBuffer(dateStr);
		Random random = new Random();
		String num_4 = "";
		int randomNum;
		do {
			randomNum = Math.abs(random.nextInt()) % 10 + 48;
			char randomChar = (char) randomNum;
			String randomStr = Character.toString(randomChar);
			num_4 += randomStr;
		} while (num_4.length() < 4);
		result.append(num_4);
		return result.toString();
	}
}



附:

将微信公众号文章拉取下来之后,如果要在HTML上显示文章内容,会发现微信的图片会加载不出来。
解决办法:
在HTML页面加上  <meta name="referrer" content="never">