> 文章列表 > dy对接接口获取数据

dy对接接口获取数据

dy对接接口获取数据

1.需求方:

1.已知账户,获取该账户下每天的发视频数据,同时获取一个视频连续30天的点赞数,分享数,评论数。
2.需求方确定在标题中附带来源和作者相关信息,从标题中提取该部分信息,作为原创和作者绩效考核。

2.实现方:

1.在douyin开放平台中注册,并绑定该抖音账户,获取APPID和APP_SECRET。

3.实现过程:

3.1 授权步骤一 通过用户授权获取授权码code

dy对接接口获取数据

该接口只适用于抖音获取授权临时票据(code)。

注意:

抖音的 OAuth API 以https://open.douyin.com/开头。 该 URL 不是用来请求的,
需要展示给用户用于扫码,在抖音 APP 支持端内唤醒的版本内打开的话会弹出客户端原生授权页面。 获取的 code
可以用来调用 oauth/access_token/ 换取用户 acccess_token。
若需要授权多个 scope 需要把多个 scope 使用英文 “,” 拼接,例如 scope1,scope2,scope3 。
使用本接口前提:

首先你需要去官网申请,使你的应用可以使用特定的 Scope,具体需要哪些 Scope,请查看各接口定义。 其次你需要在本 URL 的
scope 字段中填上用户需要授权给你的 Scope。 用户授权通过后,你才可以调用相应的接口。

       /*** 抖音授权** @param request* @param response*/@GetMapping("/videoConfig/douYin/getCode")@ResponseBodypublic R getDouYinCode(HttpServletRequest request, HttpServletResponse response) {VideoConfig dyInfo = videoConfigMapper.selectByType("dy");if (dyInfo == null) {logger.error("=============无抖音平台数据==============");throw new RuntimeException("无抖音平台数据");}// https://open.douyin.com/platform/oauth/connect/?client_key=xxxxxxx&response_type=code&scope=video.list&redirect_uri=https://baidu.com/login//需要用户开放的权限String scope = "trial.whitelist,video.list,renew_refresh_token";//回调地址 https://baidu.com/login 为临时回调地址的,正式上线要用线上的域名//VIDEO_AUTH_CALLBACK_URL 为网站的回调域名String redirect_uri = VIDEO_AUTH_CALLBACK_URL + "/douYin/authCallback";String requestUrl = "https://open.douyin.com/platform/oauth/connect/?client_key=" + dyInfo.getAppId()+ "&response_type=code" + "&scope=" + scope + "&redirect_uri=" + redirect_uri;return R.ok().data("url", requestUrl);}}

3.2 授权步骤二 获取access_token

该接口用于获取用户授权第三方接口调用的凭证 access_token;该接口适用于抖音/头条授权。

注意:

抖音的 OAuth API 以https://open.douyin.com/开头。 头条的 OAuth API
以https://open.snssdk.com/开头。 西瓜的 OAuth API
以https://open-api.ixigua.com/开头。 access_token
为用户授权第三方接口调用的凭证,存储在客户端,可能会被窃取,泄漏后可能会发生用户隐私数据泄漏的风险,建议存储在服务端。 获取到
access_token 后授权临时票据 (code) 不要再授权刷新,否则会导致上一次获取的 code 过期。

上面/videoConfig/douYin/getCode请求将回调下面请求地址,并在request中返回code,然后请求获取token,并记录access_token,refresh_token ,expires_in,refresh_expires_in,open_id

 @GetMapping("/douYin/authCallback")@ResponseBodypublic String dyAuthCallback(HttpServletRequest request) throws ParseException {// 请求参数VideoConfig dyInfo = videoConfigMapper.selectByType("dy");//请求参数String code = request.getParameter("code");
//        String code = "xxxxxxxxxxxxxxxxxx";Map<String, String> map = new HashMap<>();map.put("client_key", dyInfo.getAppId());map.put("client_secret", dyInfo.getAppSecret());map.put("code", code);map.put("grant_type", "authorization_code");//请求地址String url = dyInfo.getGetTokenUrl();HttpRequest httpRequest = HttpRequest.get(url, map, Boolean.TRUE);String result = httpRequest.body();JSONObject jasonObject = JSONObject.parseObject(result);JSONObject data = (JSONObject) jasonObject.get("data");System.out.println("jasonObject = " + jasonObject);int errorCode = Integer.parseInt(data.get("error_code").toString());if (errorCode == 0) {//查询数据库里面的内容String open_id = data.get("open_id").toString();String access_token = data.get("access_token").toString();String refresh_token = data.get("refresh_token").toString();//记录token,将他的过期时间也记录下来,查询时过期,就去更新long expires_in = Long.parseLong(data.get("expires_in").toString());long refresh_expires_in = Long.parseLong(data.get("refresh_expires_in").toString());long currentTimeMillis = System.currentTimeMillis();long tokenExpiresInLong = currentTimeMillis + expires_in * 1000;long refreshExpiresInLong = currentTimeMillis + refresh_expires_in * 1000;String tokenExpiresIn = TimeUtil.transferLongToDate("yyyy-MM-dd HH:mm:ss", tokenExpiresInLong);String refreshExpiresIn = TimeUtil.transferLongToDate("yyyy-MM-dd HH:mm:ss", refreshExpiresInLong);dyInfo.setOpenId(open_id);dyInfo.setToken(access_token);dyInfo.setRefreshToken(refresh_token);dyInfo.setTokenExpiresIn(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(tokenExpiresIn));dyInfo.setRefreshExpiresIn(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(refreshExpiresIn));videoConfigMapper.updateByPrimaryKeySelective(dyInfo);logger.info("accessToken记录成功");return "授权成功,请关闭当前窗口!";}return "授权失败,请联系管理人员";}

3.3 授权步骤三 刷新access_token

 /*** 刷新 access_token 的有效期*/private void refresh_token() throws ParseException {logger.info("=================抖音刷新 access_token 的有效期=================");VideoConfig dyInfo = videoConfigMapper.selectByType("dy");String client_key = dyInfo.getAppId();Map<String, String> map = new HashMap<>();map.put("client_key", client_key);map.put("grant_type", "refresh_token");map.put("refresh_token", dyInfo.getRefreshToken());//请求地址String url = dyInfo.getGetRefreshTokenUrl();HttpRequest httpRequest = HttpRequest.post(url, map, Boolean.TRUE).header("Content-Type", "multipart/form-data");String result = httpRequest.body();JSONObject jasonObject = JSONObject.parseObject(result);JSONObject data = (JSONObject) jasonObject.get("data");logger.info("=================抖音刷新刷新access_token 的有效期:{}=================", jasonObject);String errorCode = data.get("error_code").toString();if (errorCode.equals("10008") || errorCode.equals("2190008")) {//这表示access_token过期 需要重新获取refresh_token 后会获取一个新的 access_token 以及新的超时时间。//说明要刷新重新获取refresh_tokenlogger.info("=================抖音刷新刷新重新获取refresh_token=================");renew_refresh_token();} else {//未过期,提取并记录刷新后的access_tokenif (errorCode.equals("0")) {logger.info("=================抖音 未过期,提取并记录刷新后的access_token=================");String open_id = data.get("open_id").toString();String access_token = data.get("access_token").toString();//记录token,将他的过期时间也记录下来,查询时过期,就去更新long expires_in = Long.parseLong(data.get("expires_in").toString());long refresh_expires_in = Long.parseLong(data.get("refresh_expires_in").toString());long currentTimeMillis = System.currentTimeMillis();long tokenExpiresInLong = currentTimeMillis + expires_in * 1000;long refreshExpiresInLong = currentTimeMillis + refresh_expires_in * 1000;String tokenExpiresIn = TimeUtil.transferLongToDate("yyyy-MM-dd HH:mm:ss", tokenExpiresInLong);String refreshExpiresIn = TimeUtil.transferLongToDate("yyyy-MM-dd HH:mm:ss", refreshExpiresInLong);dyInfo.setOpenId(open_id);dyInfo.setToken(access_token);dyInfo.setRefreshToken(dyInfo.getRefreshToken());dyInfo.setTokenExpiresIn(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(tokenExpiresIn));dyInfo.setRefreshExpiresIn(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(refreshExpiresIn));videoConfigMapper.updateByPrimaryKeySelective(dyInfo);logger.info("accessToken记录成功");}}// 提取并记录刷新后的access_token 获取成功后再执行一次获取视频列表数据// 如果access_token刷新后则进行renew_refresh_token操作}

3.4 授权步骤四 刷新refresh_token

 /*** 该接口用于刷新refresh_token的有效期*/private void renew_refresh_token() throws ParseException {VideoConfig dyInfo = videoConfigMapper.selectByType("dy");String client_key = dyInfo.getAppId();Map<String, String> map = new HashMap<>();map.put("client_key", client_key);map.put("refresh_token", dyInfo.getRefreshToken());//请求地址String url = dyInfo.getGetRenewRefreshTokenUrl();HttpRequest httpRequest = HttpRequest.post(url, map, Boolean.TRUE).header("Content-Type", "multipart/form-data");String result = httpRequest.body();logger.info("=================刷新refresh_token的有效期,数据获取result:{}=================", result);JSONObject jasonObject = JSONObject.parseObject(result);JSONObject data = (JSONObject) jasonObject.get("data");logger.info("=================刷新refresh_token的有效期,数据获取jasonObject:{}=================", jasonObject);String errorCode = data.get("error_code").toString();//未过期,提取并记录刷新后的access_tokenif (errorCode.equals("0")) {//过期时间 30天long expires_in = Long.parseLong(data.get("expires_in").toString());String refresh_token = data.get("refresh_token").toString();long currentTimeMillis = System.currentTimeMillis();System.out.println("currentTimeMillis = " + currentTimeMillis);long refreshExpiresInLong = currentTimeMillis + expires_in * 1000;String refreshExpiresIn = TimeUtil.transferLongToDate("yyyy-MM-dd HH:mm:ss", refreshExpiresInLong);dyInfo.setRefreshToken(refresh_token);dyInfo.setRefreshExpiresIn(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(refreshExpiresIn));videoConfigMapper.updateByPrimaryKeySelective(dyInfo);logger.info("刷新refresh_token记录成功");}else{//TODO 给管理员发一个短信需要他认证。}}

3.5 获取视频列表

dy对接接口获取数据

1.执行一个定时器任务,进行数据抓取,并记录数据库中。
2.抖音每次只支持十条数据返回,每次不能全部抓取,分批次读取数据,找到30天内的数据,30天内的数据是需要更新总点击数和总分享数等。
3.列表是有规律的,除指定的几个其余是按照当前时间逆序排序的。
作者可能有多个人,因此每个数据插入前要进行人员匹配哦

 /*** 获取视频列表并插入数据库    每天凌晨2点30分抓取*/@Scheduled(cron = "${dy.task.cron}")public void getVideoList() throws ParseException {/*** open_id = * access_token = */VideoConfig dyInfo = videoConfigMapper.selectByType("dy");if (dyInfo == null) {throw new RuntimeException("无数据抖音平台数据");}//检查是否需要刷新access_tokenif (dyInfo.getTokenExpiresIn().getTime() < System.currentTimeMillis()) {//为空则说明需要去刷新 获取老版本然后更新refresh_token();//刷新后再次获取最新的access_token相关信息dyInfo = videoConfigMapper.selectByType("dy");}//获取列表String has_more = null;String cursor = "0";int count = 0;JSONArray jsonArray = new JSONArray();do {JSONObject list10 = getList10(dyInfo, cursor);if (list10 == null) return;has_more = list10.get("has_more").toString();cursor = list10.get("cursor").toString();JSONArray list = (JSONArray) list10.get("list");for (int i = 0; i < list.size(); i++) {JSONObject json = (JSONObject) list.get(i);SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");String date = json.get("create_time").toString();String createTime = sdf.format(new Date(Long.parseLong(String.valueOf(Long.parseLong(date) * 1000))));    //时间戳转化成时间boolean flag = TimeUtil.isPastNDays(createTime, 30);if (!flag) {//不在该时间范围就移除数据.list.remove(i);System.out.println("移除时间为:" + TimeUtil.TimeStampToTime(date));System.out.println("移除的标题为:" + json.get("title").toString());//动态调整结束请求页数,当从第二页开始,如果有连续移除时,说明后面的页数据已经不是要取的了.if (count >= 2) {has_more = "false";}}}jsonArray.addAll(list);//标记页数count++;} while (has_more.equals("true"));logger.info("本次翻页{}次,共采集到有效数据{}条", count, jsonArray.size());dataDeal(jsonArray);logger.info("=================抖音数据采集结束=================");}
 //数据处理过程private void dataDeal(JSONArray jsonArray) throws ParseException {for (int i = 0; i < jsonArray.size(); i++) {JSONObject jsonObject = jsonArray.getJSONObject(i);//视频创建时间戳String date = jsonObject.get("create_time").toString();SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");String createTime = sdf.format(new Date(Long.parseLong(String.valueOf(Long.parseLong(date) * 1000))));    //时间戳转化成时间//网页链接String share_url = jsonObject.get("share_url").toString();//标题String title = jsonObject.get("title").toString();String layout = "";String authors = "";String editor = "";String editor2 = "";String editor3 = "";String editor4 = "";if (title.indexOf("来源:") > 0||title.indexOf("来源:") > 0) { //判断是否有这个String str1 = "";if(title.indexOf("来源:")>0){str1 = title.substring(0, title.indexOf("来源:"));}else{str1 = title.substring(0, title.indexOf("来源:"));}//获取作者相关信息,处理格式问题String contactInfo = title.substring(str1.length()).replace(" ", "").replace("\\n", "");logger.info("=================抖音数据获取contactInfo:{}=================", contactInfo);Pattern from = Pattern.compile("来源:.+作者|编辑");Matcher fromM = from.matcher(contactInfo);while (fromM.find()) {//获取作者信息然后去匹配字段layout = contactInfo.substring(fromM.start(), fromM.end()).replace("来源:", "").replace("编辑", "");if (layout.contains("xxxxx")) { //xxxx表示来来源后的一段文字//自己的媒体有作者信息// 以 编辑:开头,以 编辑: 结束的字符串Pattern p = Pattern.compile("作者:.+编辑:");Matcher m = p.matcher(contactInfo);while (m.find()) {//获取作者信息然后去匹配字段authors = contactInfo.substring(m.start(), m.end()).replace("作者:", "").replace("编辑", "");logger.info("作者:" + authors);}}}//获取编辑Pattern ed = Pattern.compile("编辑:.+责编");Matcher em = ed.matcher(contactInfo);while (em.find()) {//获取作者信息然后去匹配字段editor = contactInfo.substring(em.start(), em.end()).replace("编辑:", "").replace("责编", "");logger.info("编辑:" + editor);}//获取责编Pattern ed2 = Pattern.compile("责编:.+编审");Matcher em2 = ed2.matcher(contactInfo);while (em2.find()) {//获取作者信息然后去匹配字段editor2 = contactInfo.substring(em2.start(), em2.end()).replace("责编:", "").replace("编审", "");logger.info("责编:" + editor2);}//获取编审Pattern ed3 = Pattern.compile("编审:.+监制");Matcher em3 = ed3.matcher(contactInfo);while (em3.find()) {//获取作者信息然后去匹配字段editor3 = contactInfo.substring(em3.start(), em3.end()).replace("编审:", "").replace("监制", "");logger.info("编审:" + editor3);}//获取监制Pattern ed4 = Pattern.compile("监制:.+");Matcher em4 = ed4.matcher(contactInfo);while (em4.find()) {//获取监制信息然后去匹配字段editor4 = contactInfo.substring(em4.start(), em4.end()).replace("监制:", "");logger.info("监制:" + editor4);}}//统计数据JSONObject statistics = (JSONObject) jsonObject.get("statistics");            //分享数//播放数int play_count = (int) statistics.get("play_count");//点赞数int digg_count = (int) statistics.get("digg_count");//分享数int share_count = (int) statistics.get("share_count");//首先根据时间和标题看是否已经插入了title = title.replace("\\n", "");Audit audi = auditMapper.selectByTitleAndTime(title, createTime);//如果已经入库则更新总点击和总分享量if (audi == null) {Audit audit = new Audit();audit.setCreateTime(createTime);audit.setMediaName("抖音");audit.setType("video");audit.setTitle(title);audit.setLayout(layout);audit.setLink(share_url);audit.setReadCount(play_count);audit.setClickTotal(digg_count);audit.setShareCount(share_count);audit.setReadTotal(play_count);audit.setClickTotal(digg_count);audit.setShareTotal(share_count);insertAudit(authors, editor, editor2, editor3, editor4, audit);} else {logger.info("================= 更新数据 =================");SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd");String currentDate = df.format(new Date());int dayNum = TimeUtil.daysBetween(createTime, currentDate);//未审核并且时间再30天内if (audi.getIsAudit() < 2 && dayNum <= 30) {audi.setReadTotal(audi.getReadTotal() + play_count);audi.setClickTotal(audi.getClickTotal() + digg_count);audi.setShareTotal(audi.getShareTotal() + share_count);//更新auditMapper.updateByPrimaryKeySelective(audi);logger.info("更新一条数据成功,{},{}", audi.getTitle(), audi.getCreateTime());}}}}
//获取数据private JSONObject getList10(VideoConfig dyInfo, String cursor) {//数据读取String open_id = dyInfo.getOpenId();String access_token = dyInfo.getToken();Map<String, String> map = new HashMap<>();map.put("open_id", open_id);map.put("cursor", cursor);map.put("count", "10");logger.info("=================抖音数据采集开始=================");//请求地址String url = dyInfo.getGetVideoListUrl();HttpRequest httpRequest = HttpRequest.get(url, map, Boolean.TRUE).header("access-token", access_token);String result = httpRequest.body();JSONObject jasonObject = JSONObject.parseObject(result);JSONObject data = (JSONObject) jasonObject.get("data");if (!data.get("error_code").toString().equals("0")) {logger.error("抖音数据获取失败:{}", jasonObject);return null;}JSONArray jsonArray = (JSONArray) data.get("list");logger.info("=================抖音数据获取,接口响应数据:{}=================", jasonObject);logger.info("数据条数为:{}", jsonArray.size());return data;}//开启一个事务,要么都成功插入数据库,要么都失败会滚@Transactional(rollbackFor = Exception.class)public void insertAudit(String authors, String editor, String editor2, String editor3, String editor4, Audit audit) {try {auditMapper.insertSelective(audit);logger.info("新加一条数据成功,{},{}", audit.getTitle(), audit.getCreateTime());//执行插入作者信息authorDoUtil.insertAuthorInfoPlus(audit.getAuditId(), authors, AuthorType.AUTHOR);//执行编辑作者信息authorDoUtil.insertAuthorInfoPlus(audit.getAuditId(), editor, AuthorType.EDITOR);//执行插入责编信息authorDoUtil.insertAuthorInfoPlus(audit.getAuditId(), editor2, AuthorType.EX_EDITOR);//执行插入编审信息authorDoUtil.insertAuthorInfoPlus(audit.getAuditId(), editor3, AuthorType.EDITORIAL);//执行插入监制信息authorDoUtil.insertAuthorInfoPlus(audit.getAuditId(), editor4, AuthorType.SUPERVISOR);} catch (Exception e) {logger.error("执行抖音数据采集失败,回滚成功!标题为:{},{},{}", audit.getTitle(), audit.getCreateTime(), e.getMessage());//手动回滚TransactionAspectSupport.currentTransactionStatus().setRollbackOnly(),使得事务生效,出现异常回滚。TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();}}