现如今基于微信公众号开发的应用服务在我们的日常生活中随处可见,很多从事软件开发的同学或多或少都会接触到与微信公众号开发的相关内容。这篇文章主要是分享如何快速批量获取微信公众号关注者(公众号粉丝)用户数据信息,文章以代码为主题,讲解整个实现过程,方便日后有需要的同学快速接入使用。
一、接口准备
整个过程中我们需要调用微信公众平台官网提供的三个API接口,如下:

微信公众号API
二、接口说明
这里只对接口做简单的基本信息描述,更加详细API描述可参考官网内容。
官网地址:https://developers.weixin.qq.com/doc/offiaccount/Getting_Started/Overview.html

获取Access token接口描述

获取用户列表接口描述

获取用户基本信息接口描述
三、代码实现
我们把调用微信API的代码统一封装成工具类(如WeChatUtil.java),完整代码如下:
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.sun.org.apache.xerces.internal.impl.dv.util.Base64;
import lombok.extern.slf4j.Slf4j;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import javax.net.ssl.HttpsURLConnection;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.URL;
import java.security.spec.AlgorithmParameterSpec;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* 微信小程序工具类
*
* @ClassName WeChatUtil
* @Date 2021/8/5 16:10
* @Version V1.0
*/
@Slf4j
public class WeChatUtil {
private static final String ACCESS_TOKEN_URL = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=%s&secret=%s";
private static final String GET_USER_LIST_URL = "https://api.weixin.qq.com/cgi-bin/user/get?access_token=%s";
private static final String GET_UNIONID_URL = "https://api.weixin.qq.com/cgi-bin/user/info?access_token=%s&openid=%s&lang=zh_CN";
/**
* 获取微信公众号AccessToken
*
* @param appId 微信公众号APPID
* @param secret 微信公众号secret
* @return 返回AccessToken
*/
public static String getAccessToken(String appId, String secret) {
String accessToken = "";
String url = String.format(ACCESS_TOKEN_URL, appId, secret);
// 发送请求,返回Json字符串
String jsonStr = sendRequest(url, "GET", null);
if (StrUtil.isNotEmpty(jsonStr)) {
// 转成Json对象 获取openid
JSONObject obj = JSONUtil.parseObj(jsonStr);
accessToken = obj.getStr("access_token");
}
return accessToken;
}
/**
* 获取公众号用户列表
*
* @param accessToken 访问token
* @param startOpenId 第一个拉取的OPENID,不填默认从头开始拉取
* @return 返回用户列表数据
*/
public static Map<String, Object> getUserList(String accessToken, String startOpenId) {
log.info("**********获取微信公众号用户列表数据**********");
Map<String, Object> resultMap = new HashMap<>();
String url = String.format(GET_USER_LIST_URL, accessToken);
if (StrUtil.isNotEmpty(startOpenId)) {
url += ("&next_openid=" + startOpenId);
}
// 发送请求,返回Json字符串
String jsonStr = sendRequest(url, "GET", null);
log.info("**********获取微信公众号用户列表数据返回:" + jsonStr + "**********");
if (StrUtil.isNotEmpty(jsonStr)) {
// 转成Json对象
JSONObject obj = JSONUtil.parseObj(jsonStr);
Integer total = obj.getInt("total");
Integer count = obj.getInt("count");
String nextOpenId = obj.getStr("next_openid");
JSONObject dataObj = obj.getJSONObject("data");
if (dataObj != null) {
JSONArray openidList = dataObj.getJSONArray("openid");
if (total != null && total > 0 && count != null && count > 0 && openidList != null && !openidList.isEmpty()) {
log.info("**********当前微信公众号总用户数:" + total + ",本次获取:" + count + ",nextOpenId=" + nextOpenId + "**********");
resultMap.put("nextOpenId", nextOpenId);
resultMap.put("openidList", openidList.toList(String.class));
}
}
}
return resultMap;
}
/**
* 获取公众号用户详细信息
*
* @param accessToken 访问token
* @param openId 用户openId
* @return 返回用户信息
*/
public static Map<String, String> getUserInfo(String accessToken, String openId) {
log.info("**********获取微信公众号用户详细信息**********");
Map<String, String> resultMap = new HashMap<>();
String url = String.format(GET_UNIONID_URL, accessToken, openId);
// 发送请求,返回Json字符串
String jsonStr = sendRequest(url, "GET", null);
log.info("**********获取微信公众号用户详细信息数据返回:" + jsonStr + "**********");
if (StrUtil.isNotEmpty(jsonStr)) {
// 转成Json对象
JSONObject obj = JSONUtil.parseObj(jsonStr);
String unionid = obj.getStr("unionid");
String nickname = obj.getStr("nickname");
String headimgurl = obj.getStr("headimgurl");
if (StrUtil.isNotEmpty(unionid) && StrUtil.isNotEmpty(nickname)) {
resultMap.put("unionid", unionid);
resultMap.put("nickname", nickname);
resultMap.put("headImgUrl", StrUtil.isNotEmpty(headimgurl) ? headimgurl : "");
}
}
return resultMap;
}
/**
* 发送微信请求
*
* @param path 请求地址
* @return 返回请求结果
*/
private static String sendRequest(String path, String requestMethod, String outputStr) {
try {
URL url = new URL(path);
HttpsURLConnection connection = (HttpsURLConnection) url.openConnection();
connection.setDoOutput(true);
connection.setDoInput(true);
connection.setUseCaches(false);
connection.setRequestMethod(requestMethod);
// 当有数据需要提交时
if (null != outputStr) {
OutputStream outputStream = connection.getOutputStream();
// 注意编码格式,防止中文乱码
outputStream.write(outputStr.getBytes("UTF-8"));
outputStream.close();
}
// 从输入流读取返回内容
InputStream inputStream = connection.getInputStream();
InputStreamReader inputStreamReader = new InputStreamReader(inputStream, "utf-8");
BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
String str = null;
StringBuffer buffer = new StringBuffer();
while ((str = bufferedReader.readLine()) != null) {
buffer.append(str);
}
bufferedReader.close();
inputStreamReader.close();
inputStream.close();
connection.disconnect();
return buffer.toString();
} catch (Exception e) {
e.printStackTrace();
}
return "";
}
}
以上是对获取微信公众号用户信息接口的封装,实际中我再根据业务情况调用工具类的方法完成用户数据获取功能。例如,我们想同步拉取所有用户数据,则实现代码大概如下:
String accessToken = WeChatUtil.getAccessToken("AppId", "Secret");
Map<String, Object> resultMap = WeChatUtil.getUserList(accessToken, null);
if (resultMap.size() > 0) {
while (resultMap.get("nextOpenId") != null && StrUtil.isNotEmpty(resultMap.get("nextOpenId").toString())) {
startOpenId = resultMap.get("nextOpenId").toString();
resultMap = WeChatUtil.getUserList(accessToken, startOpenId);
if (resultMap.size() == 0) {
break;
}
}
}
建议:accessToken在项目中最好统一获取刷新,否则容易造成冲突,而相互覆盖失效。获取到的accessToken我们最好缓存到本地,加上时效(目前accessToken的有效时间是7200秒之内),因为接口调用次数是有限制的。
感兴趣的同学,记得点赞[赞]并“ 关注 ”[握手]本头条号,一起指间Coding[中国赞]