【Java】Jsoup 解析HTML报告
一、需求背景
有好几种报告文件,目前是人肉找报告信息填到Excel上生成统计信息
跟用户交流了下需求和提供的几个文件,发现都是html文件
其实所谓的报告的文件,就是一些本地可打开的静态资源,里面也有js、img等等
二、方案选型
前面老板一直说是文档解析,我寻思这不就是写爬虫吗....
因为是在现有系统上加新功能实现,现有系统还是Java做后端服务,所以之前学的Python就不想用了
写Python还需要单独起个服务部署起来,Java有JSOUP能用,没Python那么好用就是...
三、落地实现
1、JSOUP依赖坐标:
<!-- https://mvnrepository.com/artifact/org.jsoup/jsoup -->
<dependency>
<groupId>org.jsoup</groupId>
<artifactId>jsoup</artifactId>
<version>1.18.1</version>
</dependency>
2、文件读取问题
我发现每种类型的报告文件的存放方式都不一样
第一种单HTML文件:
这种相对简单,只需要读取路径后直接访问文件内容即可
String reportFilePath = "C:/Users/Administrator/Desktop/report-type/xxx.html";
String htmlContent = new String(Files.readAllBytes(Paths.get(reportFilePath)), StandardCharsets.UTF_8);
Document doc = Jsoup.parse(htmlContent);
第二种单Zip压缩文件:
单层压缩,可以通过zipFile的API访问,取出压缩条目一个个用条目名称进行判断
再通过zipFile打开读取流对该条目进行读取
String targetFile = "index.html";
ZipEntry targetEntry = null;
String reportFilePath = "C:/Users/Administrator/Desktop/report-type/xxxhtml.zip";
ZipFile zipFile = isWinSys() ? new ZipFile(new File(reportFilePath), ZipFile.OPEN_READ, Charset.forName("GBK")) : new ZipFile(reportFilePath);
Enumeration<? extends ZipEntry> zipEntries = zipFile.entries();
while (zipEntries.hasMoreElements()) {
ZipEntry zipEntry = zipEntries.nextElement();
boolean isDirectory = zipEntry.isDirectory();
if (isDirectory) continue;
String name = zipEntry.getName();
if (targetFile.equals(name)) {
targetEntry = zipEntry;
break;
}
}
boolean hasFind = Objects.nonNull(targetEntry);
if (!hasFind) return; /* 没有可读取的目标文件 */
InputStream inputStream = zipFile.getInputStream(targetEntry);
String htmlCode = IoUtil.readUtf8(inputStream);
Document doc = Jsoup.parse(htmlCode);
执行完成后记得要释放资源:
/* 资源释放 */
inputStream.close();
zipFile.close();
第三种多Zip嵌套压缩文件:
文件被压缩了两次,要解压两边才可以访问
1、读取内嵌的Zip文件时发现MALFORM报错,需要根据操作系统设置读取编码...
https://blog.csdn.net/qq_25112523/article/details/136060946
然后在创建ZipFile对象的API加了一个操作系统的判断
public static boolean isWinSys() {
String property = System.getProperty("os.name");
return property.contains("win") || property.contains("Win");
}
2、ZipFile只对单层压缩有用,如果是嵌套的压缩文件就不支持了
这个报告文件的情况是第一层只有一个条目,所以上传上来的文件我只关心里面只有一个内嵌的压缩文件就行
当匹配这个条件交给ZipFile读取输入流,转换成Zip输入流,否则不处理
可以在下面代码看到,对被压缩的文件进行inputStream读取后,要改用ZipInputStream读取
zipInputStream 等效 zipFile + zipEntries的合体,包含了条目迭代信息
但是只有一个getNextEntry方法,只能写While循环不断判断下一个条目是否还存在
文件名叫report.html,判断条目名是否匹配后结束循环
再利用IO工具类直接读取ZipInputStream即可 (getNextEntry方法就是让ZipInputStream不断切换到当前条目的引用)
如果要处理复杂情况要在While里面才能实现的,建议每个条目结束之后调用closeEntry方法
String targetSuffix = ".zip";
String targetFile = "report.html";
String reportFilePath = "C:/Users/Administrator/Desktop/report-type/xx_20240729153751.zip";
ZipFile zipFile = isWinSys() ? new ZipFile(new File(reportFilePath), ZipFile.OPEN_READ, Charset.forName("GBK")) : new ZipFile(reportFilePath);
Enumeration<? extends ZipEntry> enumeration = zipFile.entries();
/* 转换成集合条目,迭代条目不能判断size */
List<ZipEntry> zipEntrieList = new ArrayList<>();
while (enumeration.hasMoreElements()) {
ZipEntry zipEntry = enumeration.nextElement();
zipEntrieList.add(zipEntry);
}
/* 只有1个zip压缩文件时才处理 */
if (CollectionUtils.isEmpty(zipEntrieList)) return;
boolean isOnlyOneEntry = zipEntrieList.size() == 1;
boolean anyMatch = zipEntrieList.stream().anyMatch(ze -> ze.getName().endsWith(targetSuffix));
if (!isOnlyOneEntry || !anyMatch) return;
ZipEntry zipEntry = zipEntrieList.get(0);
/* 通过ZipInputStream不断切换条目找到目标文件 */
InputStream inputStream = zipFile.getInputStream(zipEntry);
ZipInputStream zipInputStream = new ZipInputStream(inputStream);
/* 在内层中寻找目标文件 */
ZipEntry reportEntry = zipInputStream.getNextEntry();
while (Objects.nonNull(reportEntry)) {
String name = reportEntry.getName();
if (targetFile.equals(name)) break;
reportEntry = zipInputStream.getNextEntry();
}
String htmlCode = IoUtil.readUtf8(zipInputStream);
Document doc = Jsoup.parse(htmlCode);
同样这里也需要释放资源:
/* 资源释放 */
zipInputStream.close();
inputStream.close();
zipFile.close();
3、常见查询API使用
一、常见API方法
下班到家才反应过来ownText是元素自己的文本内容,过滤掉其他嵌套的元素文本
也可以直接使用cssQuery
doc.select("table.y-report-ui-report-info-grid")
二、使用兄弟元素查找对应关系
有一个特殊的情况就是有些元素按文档结构应该是一个逐层关联的结构
先有A,然后B在A里面,C又在B里面这样
但是这个是摊开来的结构,A -> B -> C -> D,元素id和类名也没用直接关系,这样是很难构建关联的
只能通过元素的顺序推断结构:
1、获取当前ip标题元素和下一个ip标题元素的兄弟元素下标值
2、将idp元素的兄弟元素下标值取出
3、比较idp元素是否在两者之间,如果为是表示idp元素属于第一个ip标题元素
【Java】Jsoup 解析HTML报告的更多相关文章
- json-lib-2.4-jdk15.jar所需全部JAR包.rar java jsoup解析开彩网api接口json数据实例
json-lib-2.4-jdk15.jar所需全部JAR包.rar java jsoup解析开彩网api接口json数据实例 json-lib-2.4-jdk15.jar所需全部JAR包.rar ...
- [java] jsoup 解析网页获取省市区域信息
到国家统计局抓取数据, 到该class下解析数据 /** * jsoup解析网页 * @author xwolf * @date 2016-12-13 18:11 * @since V1.0.0 */ ...
- jsoup Java HTML解析器:使用选择器语法来查找元素
jsoup Java HTML解析器:使用选择器语法来查找元素 使用选择器语法来查找元素 问题 你想使用类似于CSS或jQuery的语法来查找和操作元素. 方法 可以使用Element.select( ...
- atitit. java jsoup html table的读取解析 总结
atitit. java jsoup html table的读取解析 总结 1. 两个大的parser ,,,jsoup 跟个 htmlparser 1 2. 资料比较 1 3. jsoup越佳. ...
- Java爬虫系列三:使用Jsoup解析HTML
在上一篇随笔<Java爬虫系列二:使用HttpClient抓取页面HTML>中介绍了怎么使用HttpClient进行爬虫的第一步--抓取页面html,今天接着来看下爬虫的第二步--解析抓取 ...
- [java] jsoup使用简介-汇率换算器实现-插曲2
[java] jsoup使用简介-汇率换算器实现-插曲2 // */ // ]]> [java] jsoup使用简介-汇率换算器实现-插曲2 Table of Contents 1 系列文章 ...
- jsoup解析HTML及简单实例
jsoup 中文参考文献 http://www.open-open.com/jsoup/ 本文将利用jsoup,简单实现网络抓取的功能,并给出一个小实例,该实例效果为:获取作者本人在博客园写的所 ...
- Android开发探秘之三:利用jsoup解析HTML页面
这节主要是讲解jsoup解析HTML页面.由于在android开发过程中,不可避免的涉及到web页面的抓取,解析,展示等等,所以,在这里我主要展示下利用jsoup jar包来抓取cnbeta.com网 ...
- 一步步教你为网站开发Android客户端---HttpWatch抓包,HttpClient模拟POST请求,Jsoup解析HTML代码,动态更新ListView
本文面向Android初级开发者,有一定的Java和Android知识即可. 文章覆盖知识点:HttpWatch抓包,HttpClient模拟POST请求,Jsoup解析HTML代码,动态更新List ...
- Jsoup 解析 HTML
Jsoup 文档 方法 要取得一个属性的值,可以使用Node.attr(String key) 方法 对于一个元素中的文本,可以使用Element.text()方法 对于要取得元素或属性中的HTML内 ...
随机推荐
- 7.13晚考试总结(NOIP模拟14)[队长快跑·影魔·抛硬币]
樱花满地集于我心,楪舞纷飞祈愿相随 前言 终于没有令人头疼的数学了,总感觉这次考试的题目比较良心. 尤其是对于部分分的明细就非常到位(不像上一场的凭感觉给出部分分). 这就令我这种靠部分分暴力的菜鸡选 ...
- containerd 源码分析:创建 container(三)
文接 containerd 源码分析:创建 container(二) 1.2.2.2 启动 task 上节介绍了创建 task,task 创建之后将返回 response 给 ctr.接着,ctr 调 ...
- ETL工具-nifi干货系列 第一讲 揭开nifi神秘面纱
1.nifi简介 Apache NiFi 是基于流程编程概念的数据流系统.它支持强大且可扩展的数据路由.转换和系统中介逻辑的有向图.NiFi具有基于Web的用户界面,用于设计.控制.反馈和监控数据流. ...
- 算法金 | LSTM 原作者带队,一个强大的算法模型杀回来了
大侠幸会,在下全网同名「算法金」 0 基础转 AI 上岸,多个算法赛 Top 「日更万日,让更多人享受智能乐趣」 时间拉回 2019 年,有「计算机界诺贝尔奖」之称图灵奖获得者公布,深度学习三巨头:Y ...
- ConfigurationManager 读取的不是最新值
用 ConfigurationManager 修改配置后,ConfigurationManager 读取的不是最新值. 解决方法: 第一种: ConfigurationManager.RefreshS ...
- win10 chrome 百分浏览器 centbrowser 收藏夹栏字体突然变小
win10 chrome 百分浏览器 centbrowser 收藏夹栏字体突然变小 解决方法: 在"开始" >"设置" >"轻松使用&qu ...
- MySql用户与权限控制
MySql用户与权限控制 -- 刷新权限命令 # -- 刷新mysql权限命令 flush privileges; 用户管理 1.查看用户 #查看用户 USE mysql; SELECT host,u ...
- 实验13.Nat转发telnet实验
# 实验13.Nat转发telnet实验 本节用于测试NAT服务,用于将流量转发到内网的指定设备上. 实验组 配置路由器 由于之前配置过ospf,所以这次用直接指静态练手,首先确保全网畅通 R3 GW ...
- 基于cifar数据集合成含开集、闭集噪声的数据集
前言 噪声标签学习下的一个任务是:训练集上存在开集噪声和闭集噪声:然后在测试集上对闭集样本进行分类. 训练集中被加入的开集样本,会被均匀得打上闭集样本的标签充当开集噪声:而闭集噪声的设置与一般的噪声标 ...
- Python 潮流周刊#58:最快运行原型的语言(摘要)
本周刊由 Python猫 出品,精心筛选国内外的 250+ 信息源,为你挑选最值得分享的文章.教程.开源项目.软件工具.播客和视频.热门话题等内容.愿景:帮助所有读者精进 Python 技术,并增长职 ...