Kettle开发Jsoup组件进行网页爬取

1. 概述

在当今数据驱动的商业环境中,企业需要从各种来源收集和分析数据以支持决策。网页爬取作为一种数据收集手段,能够从网页中提取有价值的信息。Kettle作为一种强大的ETL工具,结合Jsoup库,可以实现高效、灵活的网页爬取功能。通过开发一个Kettle组件,用户可以将网页数据抽取任务集成到Kettle的工作流中,实现自动化处理。

2. 组件实现

2.1 组件设计

在Kettle中开发Jsoup组件,需要继承StepInterfaceStepDataInterface接口,并实现相关的抽象方法。组件的主要类包括:

  • JsoupClientMeta:负责存储组件的元数据,如配置参数、输入字段和输出字段等。

  • JsoupClientData:用于存储组件运行时的数据,如文件列表、解析的HTML文档、抽取结果等。

  • JsoupClient:实现组件的核心逻辑,包括数据抽取、处理和输出。

2.2 初始化组件

在组件初始化阶段,init方法会被调用。该方法负责加载配置参数,初始化文件列表,并创建输出行元数据。通过解析字段路径,将用户定义的XPath或CSS选择器转换为可执行的抽取路径。

public boolean init(StepMetaInterface smi, StepDataInterface sdi) {meta = (JsoupClientMeta) smi; // 转换元数据接口data = (JsoupClientData) sdi; // 转换数据接口if (super.init(smi, sdi)) { // 初始化父类data.rownr = 1L; // 初始化行号data.nrInputFields = meta.getInputFields().length; // 初始化输入字段数量// 解析字段路径for (int i = 0; i < data.nrInputFields; i++) {JsoupClientDataField xmlDataField = meta.getInputFields()[i];String XPathValue = environmentSubstitute(xmlDataField.getXPath());if (xmlDataField.getElementType() == JsoupClientDataField.ELEMENT_TYPE_ATTRIBUT) { // 处理属性if (!XPathValue.startsWith(JsoupClientMeta.AT)) {XPathValue = JsoupClientMeta.AT + XPathValue;}}xmlDataField.setResolvedXPath(XPathValue); // 设置解析后的XPath}data.PathValue = environmentSubstitute(meta.getLoopXPath()); // 设置循环路径if (Utils.isEmpty(data.PathValue)) { // 检查路径是否为空logError(BaseMessages.getString(PKG, "JsoupData.Error.EmptyPath"));return false;}data.prunePath = environmentSubstitute(meta.getPrunePath()); // 设置剪枝路径if (data.prunePath != null) {if (Utils.isEmpty(data.prunePath.trim())) {data.prunePath = null;} else {if (!data.prunePath.startsWith(JsoupClientMeta.N0DE_SEPARATOR)) {data.prunePath = JsoupClientMeta.N0DE_SEPARATOR + data.prunePath;}}}return true;}return false;
}

2.3 处理每一行数据

在数据处理阶段,processRow方法会被调用。该方法负责从输入流中获取数据,并使用Jsoup解析HTML文档。根据用户定义的抽取路径,从DOM中提取所需的数据。

public boolean processRow(StepMetaInterface smi, StepDataInterface sdi) throws KettleException {meta = (JsoupClientMeta) smi;data = (JsoupClientData) sdi;if (first) { // 如果是第一次处理first = false;data.files = meta.getFiles(this); // 初始化文件列表if (!meta.isdoNotFailIfNoFile() && data.files.nrOfFiles() == 0) {throw new KettleException(BaseMessages.getString(PKG, "JsoupData.Log.NoFiles"));}handleMissingFiles(); // 处理缺失文件data.outputRowMeta = new RowMeta(); // 创建输出行元数据meta.getFields(data.outputRowMeta, getStepname(), null, null, this, repository, metaStore); // 生成字段}// 获取HTML行数据Object[] r = getHTMLRow();if (r == null) { // 没有数据或发生错误,结束处理setOutputDone();return false;}putRowOut(r); // 输出数据return true;
}

2.4 获取HTML行数据

getHTMLRow方法负责从文件列表中读取文件,并使用Jsoup解析HTML文档。根据抽取路径提取数据,并返回处理后的行数据。

private Object[] getHTMLRow() throws KettleException {if (!meta.isInFields()) { // 如果不是从字段中读取if (!openNextFile()) { // 打开下一个文件return null;}}return getHTMLRowPutRowWithErrorhandling(); // 获取数据并处理错误
}private Object[] getHTMLRowPutRowWithErrorhandling() throws KettleException {Object[] r = null;try {if (meta.isInFields()) { // 如果是从字段中读取if (!ReadNextString()) { // 读取下一行数据return null;}if (data.readrow == null) { // 没有数据return null;}}// 处理数据r = processPutRow(data.document);} catch (Exception e) {throw new KettleException(BaseMessages.getString(PKG, "JsoupData.Error.UnableReadFile"), e);}return r;
}

2.5 数据抽取与处理

processPutRow方法负责从解析的HTML文档中提取数据,并进行清洗和转换。

private Object[] processPutRow(Document doc) throws KettleException {Object[] outputRowData = buildEmptyRow(); // 创建空行数据for (int i = 0; i < data.nrInputFields; i++) {JsoupClientDataField xmlDataField = meta.getInputFields()[i];String XPathValue = xmlDataField.getResolvedXPath();// 根据抽取路径获取数据String nodevalue = null;Elements elements = null;Element ele = null;switch (xmlDataField.getQueryType()) {case JsoupClientDataField.QUERY_TYPE_TAG:elements = doc.getElementsByTag(XPathValue);ele = elements.first();break;case JsoupClientDataField.QUERY_TYPE_SELECT:elements = doc.select(XPathValue);ele = elements.first();break;case JsoupClientDataField.QUERY_TYPE_ID:ele = doc.getElementById(XPathValue);break;case JsoupClientDataField.QUERY_TYPE_CLASS:elements = doc.getElementsByClass(XPathValue);ele = elements.first();break;case JsoupClientDataField.QUERY_TYPE_ATTRIBUTEVALUE:String[] attrs = XPathValue.split(",");elements = doc.getElementsByAttributeValue(attrs[0], attrs[1]);ele = elements.first();break;default:elements = doc.getElementsByTag(XPathValue);ele = elements.first();break;}if (ele == null) { // 没有找到节点continue;}// 获取节点值if (xmlDataField.getResultType() == JsoupClientDataField.RESULT_TYPE_VALUE_OF) {nodevalue = ele.text();} else {nodevalue = ele.html();}// 数据清洗switch (xmlDataField.getTrimType()) {case JsoupClientDataField.TYPE_TRIM_LEFT:nodevalue = Const.ltrim(nodevalue);break;case JsoupClientDataField.TYPE_TRIM_RIGHT:nodevalue = Const.rtrim(nodevalue);break;case JsoupClientDataField.TYPE_TRIM_BOTH:nodevalue = Const.trim(nodevalue);break;default:break;}// 数据转换ValueMetaInterface targetValueMeta = data.outputRowMeta.getValueMeta(data.totalpreviousfields + i);ValueMetaInterface sourceValueMeta = data.convertRowMeta.getValueMeta(data.totalpreviousfields + i);outputRowData[data.totalpreviousfields + i] = targetValueMeta.convertData(sourceValueMeta, nodevalue);// 处理重复字段if (xmlDataField.isRepeated()) {if (data.previousRow != null && Utils.isEmpty(nodevalue)) {outputRowData[data.totalpreviousfields + i] = data.previousRow[data.totalpreviousfields + i];}}}// 添加附加字段int rowIndex = data.totalpreviousfields + data.nrInputFields;if (meta.includeFilename() && !Utils.isEmpty(meta.getFilenameField())) {outputRowData[rowIndex++] = data.filename;}if (meta.includeRowNumber() && !Utils.isEmpty(meta.getRowNumberField())) {outputRowData[rowIndex++] = data.rownr;}if (meta.getShortFileNameField() != null && meta.getShortFileNameField().length() > 0) {outputRowData[rowIndex++] = data.shortFilename;}if (meta.getExtensionField() != null && meta.getExtensionField().length() > 0) {outputRowData[rowIndex++] = data.extension;}if (meta.getPathField() != null && meta.getPathField().length() > 0) {outputRowData[rowIndex++] = data.path;}if (meta.getSizeField() != null && meta.getSizeField().length() > 0) {outputRowData[rowIndex++] = data.size;}if (meta.isHiddenField() != null && meta.isHiddenField().length() > 0) {outputRowData[rowIndex++] = Boolean.valueOf(data.path);}if (meta.getLastModificationDateField() != null && meta.getLastModificationDateField().length() > 0) {outputRowData[rowIndex++] = data.lastModificationDateTime;}if (meta.getUriField() != null && meta.getUriField().length() > 0) {outputRowData[rowIndex++] = data.uriName;}if (meta.getRootUriField() != null && meta.getRootUriField().length() > 0) {outputRowData[rowIndex] = data.rootUriName;}return outputRowData;
}
3. 组件的价值

通过在Kettle中开发Jsoup组件,用户可以将网页数据抽取任务集成到Kettle的工作流中,实现自动化处理。这种集成方式带来了以下价值:

  • 提高效率:用户无需手动编写复杂的爬虫代码,可以直接通过Kettle的图形化界面配置Jsoup组件,快速实现数据抽取任务。

  • 灵活性:Jsoup组件支持多种配置选项,包括XPath和CSS选择器、数据格式化、清洗规则等,能够满足不同用户的需求。

  • 易于维护:通过Kettle的图形化界面配置组件,用户可以轻松地修改和维护数据抽取任务,无需深入理解底层代码。

总之,通过在Kettle中开发Jsoup组件,用户可以高效、灵活地从网页中抽取数据,并将其集成到Kettle的工作流中,实现自动化处理。这种集成方式不仅提高了数据抽取的效率,还增强了数据处理的灵活性和可靠性,为用户提供了一个强大的工具来满足他们的数据抽取需求。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/69859.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C++ Primer 跳转语句

欢迎阅读我的 【CPrimer】专栏 专栏简介&#xff1a;本专栏主要面向C初学者&#xff0c;解释C的一些基本概念和基础语言特性&#xff0c;涉及C标准库的用法&#xff0c;面向对象特性&#xff0c;泛型特性高级用法。通过使用标准库中定义的抽象设施&#xff0c;使你更加适应高级…

tomcat html乱码

web tomcat html中文乱码 将html文件改成jsp <% page language"java" contentType"text/html; charsetUTF-8" pageEncoding"UTF-8"%>添加 <meta charset"UTF-8">

[c语言日寄]在不完全递增序中查找特定要素

【作者主页】siy2333 【专栏介绍】⌈c语言日寄⌋&#xff1a;这是一个专注于C语言刷题的专栏&#xff0c;精选题目&#xff0c;搭配详细题解、拓展算法。从基础语法到复杂算法&#xff0c;题目涉及的知识点全面覆盖&#xff0c;助力你系统提升。无论你是初学者&#xff0c;还是…

【第1章:深度学习概览——1.2 深度学习与机器学习、传统人工智能的区别与联系】

在科技飞速发展的时代,AI 技术如同一颗璀璨的明星,照亮了我们生活的方方面面。今天,让我们一起深入探寻 AI 技术的演进历程,看看它是如何从最初的简单规则系统,一步步发展成为如今强大的智能技术。 一、开篇故事:三杯咖啡看懂三代 AI 想象一下,你经营着一家充满温馨氛…

小结:OSPF的网络类型,LSA

OSPF&#xff08;Open Shortest Path First&#xff09;是一个基于链路状态的内部网关协议&#xff08;IGP&#xff09;。以下是对OSPF网络类型、LSA类型、序列号与Age作用&#xff0c;以及相关配置指令的详细讲解。 一、OSPF的网络类型 OSPF支持多种网络类型&#xff0c;不同…

代码随想录-训练营-day26

452. 用最少数量的箭引爆气球 - 力扣&#xff08;LeetCode&#xff09; 这题是一个区间题&#xff0c;不过要求我们求最小的弓箭数&#xff0c;显然我们可以先进行排序&#xff0c;然后挨个比较气球的区间&#xff0c;如果两个气球的区间没有交集则弓箭数加一&#xff0c;如果有…

机器学习·决策树

前言 决策树是分类与回归问题中常用的方法之一。其实不仅是机器学习领域&#xff0c;在每天的日常决策中&#xff0c;我们都在使用决策树。流程图实际上就是决策树的可视化表示。 一、基本概念 决策树原理 通过一系列 逻辑规则&#xff08;特征分割条件&#xff09; 构建树形结…

Linux下载安装2

安装VMWare虚拟机 VMware是一个老牌的虚拟化软件。虚拟机指通过软件模拟的具有完整硬件系统功能的、运行在一个完全隔离环境中的完整。 下载CentOS 进入Centos官网找要下载的版本https://vault.centos.org/ …

Excel中不用复杂公式根据指定X列的数值N复制整行数据N行简单方法

Excel中不用复杂公式根据指定X列的数值N复制整行数据N行简单方法 1、在“数据表”sheet1中对指定X列&#xff08;假设X列的数字从X2开始到Xn结束&#xff09;求和&#xff0c;和为Y。 2、在“数据表”sheet1数据列之外新建一列Z&#xff0c;Z1输入表头“匹配数据列”&#xff…

DeepSeek、Kimi、文心一言、通义千问:AI 大语言模型的对比分析

在人工智能领域&#xff0c;DeepSeek、Kimi、文心一言和通义千问作为国内领先的 AI 大语言模型&#xff0c;各自展现出了独特的特点和优势。本文将从技术基础、应用场景、用户体验和价格与性价比等方面对这四个模型进行对比分析&#xff0c;帮助您更好地了解它们的特点和优势。…

数据库数据恢复—MongoDB丢失_mdb_catalog.wt文件导致报错的数据恢复案例

MongoDB数据库存储模式为文档数据存储库&#xff0c;存储方式是将文档存储在集合之中。 MongoDB数据库是开源数据库&#xff0c;同时提供具有附加功能的商业版本。 MongoDB中的数据是以键值对(key-value pairs)的形式显示的。在模式设计上&#xff0c;数据库受到的约束更少。这…

Flink在指定时间窗口内统计均值,超过阈值后报警

1、需求 统计物联网设备收集上来的温湿度数据&#xff0c;如果5分钟内的均值超过阈值(30摄氏度)则发出告警消息&#xff0c;要求时间窗口和阈值可在管理后台随时修改&#xff0c;实时生效(完成当前窗口后下一个窗口使用最新配置)。 物联网设备的数据从kafka中读取&#xff0c…

Flutter Gradle 命令式插件正式移除,你迁移旧版 Gradle 配置了吗?

在 Flutter 3.29 版本里官方正式移除了 Flutter Gradle Apply 插件&#xff0c;其实该插件自 3.19 起已被弃用&#xff0c;同时 Flutter 团队后续也打算把 Flutter Gradle 从 Groovy 转换为 Kotlin&#xff0c;并将其迁移到使用 AGP&#xff08;Android Gradle Plugin&#xff…

15.Python网络编程:进程池、进程间通信、多线程、进程和线程区别、网络通信、端口、IP地址、socket、UDP、TCP、http

1. 进程池&#xff08;Process Pool&#xff09; 进程池是通过将多个进程放入池中管理来避免频繁地创建和销毁进程&#xff0c;提高效率。Python 提供了 multiprocessing.Pool 类来实现进程池&#xff0c;它可以用于并行计算任务。 示例&#xff1a;使用进程池 from multipr…

Vue 中报错 TypeError: crypto$2.getRandomValues is not a function

问题 在新建的项目中&#xff0c;使用的是 npm init vue 创建项目后&#xff0c;执行命令 npm i &#xff0c;然后去 npm run dev 这个时候报错 TypeError: crypto$2.getRandomValues is not a function 起初是以为搞错了&#xff0c;然后再删掉 node_modules 和 package-lo…

如何通过挂载debugfs来访问内核调试信息

1. DebugFS 的作用 bugFS 的作用 内核调试接口&#xff1a;允许内核模块或子系统在 DebugFS 中创建虚拟文件或目录&#xff0c;暴露调试信息。 动态交互&#xff1a;用户可以直接读写这些文件来查看或修改内核状态&#xff08;如调整日志级别、触发特定操作&#xff09;。 零…

001 SpringCloudAlibaba整合 - Nacos注册配置中心、Sentinel流控、Zipkin链路追踪、Admin监控

SpringCloudAlibaba 文章目录 SpringCloudAlibaba1.版本依赖关系2022.x 分支2021.x 分支2.2.x 分支 组件版本关系 2.基础项目构建1.引入全局pom文件2.创建对应的模块 3.SpringBootAdmin监控服务整合1.cloud-admin服务搭建1.导入服务端依赖2.主启动类添加EnableAdminServer注解启…

常用的网络安全设备

一、 WAF 应用防火墙 范围&#xff1a;应用层防护软件 作用&#xff1a; 通过特征提取和分块检索技术进行模式匹配来达到过滤&#xff0c;分析&#xff0c;校验网络请求包的目的&#xff0c;在保证正常网络应用功能的同时&#xff0c;隔绝或者阻断无效或者非法的攻击请求 可…

Jenkins 新建配置Pipeline任务 三

Jenkins 新建配置Pipeline任务 三 一. 登录 Jenkins 网页输入 http://localhost:8080 输入账号、密码登录 一个没有创建任务的空 Jenkins 二. 创建 任务 图 NewItem 界面左上角 New Item 图NewItemSelect 1.Enter an item name&#xff1a;输入任务名 2.Select an ite…

如何构建有效的人工智能代理

目录 什么是 AI 代理? 何时应使用 AI 代理? 人工智能代理的构建模块 构建 AI 代理的常用方法 1. 提示链接(分步说明) 2.路由(将任务发送到正确的地方) 3.并行处理(同时做多件事) 4. 协调者和工作者 AI(团队合作) 5. 评估器和优化器(修复错误) 如何让人工…