微搭使用笔记(五) 通过数据源API写入数据并展示到页面

news/2024/7/19 8:37:51 标签: 低代码, 微搭, weda, 腾讯云, 爬虫

一、背景

​今天在学习腾讯云微搭API的时候发现,微搭不仅提供了小程序或者H5调用数据源的API,还支持外部通过http请求+token的方式调用,于是决定体验一把。
​正好结合之前可视化爬虫爬取新闻的一些操作,想着可以把爬好的数据通过数据源API的方式写入到微搭数据源中,同时借用微搭数据模型的能力生成页面展示数据。
本文内容和思路出于突发奇想,可能有不合适的或者使用不到位的地方,仅供参考~

二、思路

结合上面提到的想法,基本思路如下:

	1. 在spiderflow项目中封装调用微搭API写入数据的相关操作
	2. 之前爬取数据后是写入到了数据库中,修改成调用API写入到微搭数据源中

三、spiderflow项目中引入微搭数据源API

微搭官方API提供了通过http请求调用的相关示例代码
官方文档地址: 官方文档地址

结合官方文档及上述思路,集成及开发过程如下:

  • 通过腾讯云控制台获取SecretId和SecretKey,用于后续获取token。
  • 引入jar包,添加相关配置文件
  • 添加获取token的方法以及增删改查相关方法

开发过程

  1. 主pom中需要引入http-client包,具体版本根据实际情况确定,本文以5.2为例。
<dependency>
    <groupId>org.apache.httpcomponents.client5</groupId>
    <artifactId>httpclient5</artifactId>
    <version>5.2.1</version>
</dependency>
  1. spider-flow-web模块的resources目录下新建application-weda.yml配置文件,加入如下内容:
tencent:
  token:
    secretId: <步骤1中获取到的secretId>
    secretKey: <步骤1中获取到的secretKey>
    baseUrl: https://<你的微搭环境ID>.ap-shanghai.tcb-api.tencentcloudapi.com
    tokenUrl: /auth/v1/token/clientCredential
  weda:
    enabled: true
    schemas: <你的数据模型的标识,可以后面新建完数据模型后回来补充>
    envType: pre
  1. 在spider-flow-web模块的configuration包中编写配置类TencentProperties.java
package org.spiderflow.configuration;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;

/**
 * @author 泽济天下
 * @date 2023年03月16日13:54
 * @description:
 */
@Configuration
@ConfigurationProperties(prefix = "tencent")
public class TencentProperties {

    private TecentTokenProperties token;

    private TecentWedaProperties weda;

    //省略getter和setter...

    public static class TecentTokenProperties {

        private String secretId;

        private String secretKey;

        private String baseUrl;

        private String tokenUrl;

        //省略getter和setter...
    }

    public static class TecentWedaProperties {

        private Boolean enabled;

        private String schemas;

        private String envType;

        //省略getter和setter...
    }
}
  1. 编写获取token及保存数据的方法

在configuration包中新建TencentClient.java类,主要是token获取及数据保存的方法。

package org.spiderflow.configuration;

import com.alibaba.fastjson.JSONObject;
import org.apache.hc.client5.http.ClientProtocolException;
import org.apache.hc.client5.http.classic.methods.HttpPost;
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
import org.apache.hc.client5.http.impl.classic.HttpClients;
import org.apache.hc.core5.http.ContentType;
import org.apache.hc.core5.http.HttpEntity;
import org.apache.hc.core5.http.io.HttpClientResponseHandler;
import org.apache.hc.core5.http.io.entity.EntityUtils;
import org.apache.hc.core5.http.io.entity.StringEntity;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.Base64Utils;

import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author 泽济天下
 * @date 2023年03月16日14:02
 * @description: 微搭数据源API请求相关方法,主要是token获取及数据保存
 */
@Component
public class WedaClient {

    @Autowired
    private TencentProperties tencentProperties;
	
    //用于保存token
    private static Map<String, Object> map = new ConcurrentHashMap<>();

    //这个方法只是简单实现,供参考
    public String getToken() {
        System.out.println("map = " + map);
        //如果map中不存在token直接调用接口获取
        if (!map.containsKey("token")) {
            return getRemoteToken();
        }
        if (map.containsKey("time")) {
            long time = Long.parseLong(map.get("time").toString());
            int expire = Integer.parseInt(map.get("expire").toString());
            //这里判断如果离过期时间小于2分钟的话就重新获取token
            if (System.currentTimeMillis() / 1000 - time > expire - 2 * 60) {
                return getRemoteToken();
            }
        }
        return map.get("token").toString();
    }

    /**
     * 获取 token
     */
    private synchronized String getRemoteToken() {
        if (map.containsKey("token")) {
            return map.get("token").toString();
        }
        HttpPost httpPost = new HttpPost(tencentProperties.getToken().getTokenUrl());

        String basicKey = tencentProperties.getToken().getSecretId() + ":" + tencentProperties.getToken().getSecretKey();
        String authorizationKey = "Basic " + Base64Utils.encodeToString(basicKey.getBytes());
        httpPost.addHeader("Authorization", authorizationKey);
        httpPost.addHeader("Content-Type", "application/json");

        Map<String, String> body = new HashMap<>();
        body.put("grant_type", "client_credentials");

        StringEntity requestBody = new StringEntity(JSONObject.toJSONString(body), ContentType.parse("UTF-8"));
        httpPost.setEntity(requestBody);
        //这里的handler用法与官方略有不同,主要是适配5.2的语法
        HttpClientResponseHandler<String> responseHandler = response -> {
            int status = response.getCode();
            HttpEntity entity = response.getEntity();
            if (status >= 200 && status < 300) {
                return entity != null ? EntityUtils.toString(entity) : null;
            } else {
                throw new ClientProtocolException("Unexpected response status: " + status + EntityUtils.toString(entity));
            }
        };
        try {
            CloseableHttpClient httpClient = HttpClients.createDefault();
            String responseBody = httpClient.execute(httpPost, responseHandler);
            Map<String, Object> responseMap = JSONObject.parseObject(responseBody, Map.class);
            String accessToken = "Bearer " + responseMap.get("access_token").toString();
			//保存数据到map中
            map.put("token", accessToken);
            long createTime = System.currentTimeMillis() / 1000;
            map.put("time", createTime);
            map.put("expire", Integer.parseInt(responseMap.get("expires_in").toString()));

            return accessToken;
        } catch (Exception e) {
            System.out.println(e.toString());
        }
        return "";
    }

    // POST: 创建记录
    public void post(Map<String, Object> jsonBody) {
        //这里的StringEntity构造方法的参数也是为了适配5.2.1版本的httpClient
        //需要注意的是第二个参数一定是StandardCharsets.UTF_8,而不是ContentType.parse("UTF-8")
        //否则会出现中文乱码问题
        StringEntity requestBody = new StringEntity(JSONObject.toJSONString(jsonBody), StandardCharsets.UTF_8);
        String url = tencentProperties.getToken().getBaseUrl() + "/weda/odata/v1/"
                + tencentProperties.getWeda().getEnvType() + "/" + tencentProperties.getWeda().getSchemas();
        HttpPost httpPost = new HttpPost(url);
        httpPost.setEntity(requestBody);
        httpPost.addHeader("Authorization", getToken());
        httpPost.addHeader("Content-Type", "application/json");
        HttpClientResponseHandler<String> responseHandler = response -> {
            int status = response.getCode();
            if (status >= 200 && status < 300) {
                HttpEntity entity = response.getEntity();
                return entity != null ? EntityUtils.toString(entity) : null;
            } else {
                throw new ClientProtocolException("Unexpected response status: " + status + EntityUtils.toString(response.getEntity()));
            }
        };
        try {
            CloseableHttpClient httpClient = HttpClients.createDefault();
            httpClient.execute(httpPost, responseHandler);
        } catch (Exception e) {
            System.out.println("e.getMessage() = " + e.getMessage());
        }
    }
}

说明:

  • 这个类主要功能就是提供了获取token和调用数据源API保存数据的方法。
  • 微搭提供的token有效期是43200秒,也就是120小时,5天,这里通过一个map的保存避免了token的重复获取(供参考)
  • HttpClient版本的问题结合代码中的注释查看。
  1. 编写controller及简单测试
    在spider-flow-web模块的controller包中编写测试用的TencentController
package org.spiderflow.controller;

import org.spiderflow.configuration.WedaClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.util.Map;

/**
 * @author 泽济天下
 * @date 2023年03月16日14:50
 * @description:
 */
@RestController
@RequestMapping("tencent")
public class TencentController {

    @Autowired
    private WedaClient wedaClient;

    @PostMapping("save")
    public String save(@RequestParam Map<String, Object> params) {
        wedaClient.post(params);
        return "success";
    }

}

很简单,通过autowired注入了WedaClient这个bean,在save方法中调用了post方法,完事。

到这里接口的准备工作就完成了。

四、spiderflow流程调整

​ 修改之前我们先规划下入微搭数据源的字段:

	- index:  对应数据中的id,页面上的唯一标识
	- title: 新闻标题
	- content: 新闻内容富文本
	- publishDate: 发布日期,最终格式化为yyyy-MM-dd的格式展示。
	- source: 预留,后续可以从别的地方抓取新闻入到这个库,通过这个字段可以区分。

对了,我们还需要添加一个方法,用于时间格式化。

​ 我们通过详情页面抓取到的发布时间格式是xxxx年xx月xx日这样的,我们希望格式化成yyyy-MM-dd的格式。

​ 在DateFunctionExecutor类中添加两个方法备用,如下:

@Comment("格式化日期")
@Example("${date.format('2022年11月22日', 'yyyy年MM月dd日', 'yyyy-MM-dd')}")
public static String format(String source, String sourcePattern, String targetPattern) {
    try {
        Date date = new SimpleDateFormat(sourcePattern).parse(source);
        return new SimpleDateFormat(targetPattern).format(date);
    } catch (ParseException e) {
        return "";
    }
}

@Comment("格式化日期")
@Example("${date.parse('2022年11月22日', 'yyyy年MM月dd日', 'yyyy-MM-dd')}")
public static Date parse(String source, String sourcePattern, String targetPattern) {
    try {
        String format = format(source, sourcePattern, targetPattern);
        return new SimpleDateFormat(targetPattern).parse(format);
    } catch (ParseException e) {
        return null;
    }
}

方法中入参是三个,分别是原始数据,原始数据格式,目标数据格式,完成的功能就是把原始数据格式化成目标格式(上述方法可能比较原始,比较笨拙, 可自行修改)

修改最后入库的步骤为请求本地接口,配置如下:
流程配置修改1
流程配置修改2
到这里,流程配置的调整也完成了,接下来我们对微搭做一些配置就可以测试了。

五、微搭页面处理

  1. 新增数据模型及配置

    数据模型的介绍之前有文章专门写过,这里就不赘述了,附上文章链接: 数据模型介绍和初步使用

    数据模型配置如下:
    <a class=微搭数据源配置" />
    配置完成后点击保存,在基本信息标签里点击立即发布即可。

    TIPS:

    ​ 这里新建了数据模型后会有个数据模型的唯一标识,需要复制到spider-flow项目的配置文件的对应位置。

  2. 通过数据模型生成列表页面及详情页面

    • 新建列表页面
      新建页面

    • 添加数据列表组件并做相应配置
      列表组件配置
      ​ 可以看到如果表里有数据这里配置完后是可以直接预览的,还是很方便的,数据模型以及数据模型应用也是微搭官方主推的应用类型。

    • 注意到上述页面上默认展示的对应信息的id,我们修改成发布日期,同时调整字体样式等。

      如果需要添加图片在对应位置添加图片展示组件即可,不做赘述。
      列表页面配置调整

    • 新建详情页面,与列表页面类似。

    • 列表页面添加点击跳转事件,点击后跳转到详情页面并传参
      页面跳转配置1页面跳转配置2
      打开对应的详情页面,新建URL参数并传入当前记录的id参数.如上图所示。

    • 详情页面配置

      详情页面添加数据详情组件,并配置数据源为之前新建的新闻对应的数据源,选中后会根据字段类型生成对应组件及页面,可以看到默认生成的页面布局字段都是平铺的,这里我们手动调整下布局样式。

      其实要展示的字段就标题、发布日期和富文本,很简单的三个组件,配置好对应的展示字段即可。

    详情页面配置1 详情页面配置2 详情页面配置3
    其他的字段类似绑定即可。

六、整体联调测试

  1. 数据爬取与入库测试

    要求application.yml配置文件中关于微搭的配置正确,流程配置正确即可在微搭的数据管理看到对应数据。

<a class=微搭数据源入库数据展示" />
2. 页面生成与数据展示测试

数据入库没问题后,这里要求对应页面展示字段配置正确即可。最终效果如下图:

最终效果展示1
最终效果展示2

七、问题与总结

​ 本文结合了可视化爬虫spiderflow及微搭对于数据源操作提供的API实现了爬虫数据入库,并利用微搭的数据模型的能力完成了新闻列表页面的生成以及数据的展示和调整。

​ 目前遗留问题如下:

  1. 后续新闻增量更新的问题,目前的方式是每天爬取第一页数据然后入库,入库失败说明有重复数据,不处理即可。

  2. 微搭提供的数据源API没有saveOrUpdate的操作,如果需要该操作的话需要先根据条件查询出对应记录,再根据查询结果调用新增或者更新的接口。

  3. 本文对于多条数据的处理目前是一条一条写入的,按说应该是考虑批量操作的,而且微搭官方也提供了批量操作的相关接口,待优化。

  4. 对于token的保存文中使用的方式比较一般,大家如果有好的想法欢迎留言。

以上就是本文所有内容,希望对大家关于微搭的使用能有所帮助,有任何疑问或者建议欢迎留言或者私信~


http://www.niftyadmin.cn/n/165418.html

相关文章

第十届蓝桥杯(省赛)之三升序列

输入&#xff1a; VLPWJVVNNZSWFGHSFRBCOIJTPYNEURPIGKQGPSXUGNELGRVZAG SDLLOVGRTWEYZKKXNKIRWGZWXWRHKXFASATDWZAPZRNHTNNGQF ZGUGXVQDQAEAHOQEADMWWXFBXECKAVIGPTKTTQFWSWPKRPSMGA BDGMGYHAOPPRRHKYZCMFZEDELCALTBSWNTAODXYVHQNDASUFRL YVYWQZUTEPFSFXLTZBMBQETXGXFUEBHGMJK…

Redis单节点部署与卸载教程

1、Redis安装 1.1、下载Redis 官网地址&#xff1a;Redis 进入后在上方导航栏找到Download&#xff0c;下载redis 下载完成之后&#xff0c;将压缩包上传至目标服务器&#xff0c;目录自己定 1.2、安装Redis 基本环境安装&#xff1a;yum install gcc-c 将安装包进行解压&a…

条款22:当使用Pimpl惯用法,请在实现文件中定义特殊成员函数

如果你曾经与过多的编译次数斗争过&#xff0c;你会对Pimpl&#xff08;pointer to implementation&#xff09;惯用法很熟悉。 凭借这样一种技巧&#xff0c;你可以将类数据成员替换成一个指向包含具体实现的类&#xff08;或结构体&#xff09;的指针&#xff0c;并将放在主类…

laravel 怎么部署到nginx 上

要将 Laravel 应用程序部署到 Nginx 上&#xff0c;需要遵循以下步骤&#xff1a;安装 Nginx 并启动它。你可以使用以下命令在 Ubuntu 上安装 Nginx&#xff1a;sudo apt-get updatesudo apt-get install nginxsudo systemctl start nginx将 Laravel 应用程序上传到服务器&…

【ThreadPoolExecutor】自定义线程池详解(一篇透彻)

这世界上没有优秀的理念&#xff0c;只有脚踏实地的结果 。 上一篇写了Executors创建线程池的四种方法【查看】 接下来主要对ThreadPoolExecutor类的核心参数及工作原理进行说明。 一、简介 ThreadPoolExecutor是线程池的核心实现类&#xff0c;用来执行被提交的任务。 二…

Python安装demjson模块报错:error in demjson setup command: use_2to3 is invalid

本来是项目中使用 demjson 包的 JSON 功能&#xff0c;但是安装的时候报错了。 A new release of pip available: 22.3.1 -> 23.0.1 这个原因是说 pip 包的版本太低了&#xff0c;需要升级到 23.0.1。 使用 DOS 命令&#xff0c;进入到项目的目录下执行以下命令&#xff1a…

Java设计模式 03-原型模式

原型模式 一、克隆羊问题 现在有一只羊 tom&#xff0c;姓名为: tom, 年龄为&#xff1a;1&#xff0c;颜色为&#xff1a;白色&#xff0c;请编写程序创建和 tom 羊 属性完全相同的 10只羊。 二、传统方式解决克隆羊问题 图解 代码演示 羊实体类 //羊实体类 public clas…

【DP动态规划】最长子序列

问题描述&#xff1a; 解题分析&#xff1a; 采用 DP 动态规划的方法&#xff1a; 先设置 f&#xff08;1&#xff09;1&#xff1b; 其次在计算出 f&#xff08;2&#xff09;&#xff0c;再依次计算出后续的 f 的数值 例F(3)是如何计算出来的&#xff1a; 首先判断a【3】是否…