让人感到头疼的问题, 是关于那个HTML标签的清理工作的处理。
对于从事软件开发并且使用Java编程语言的朋友们来说, 这种特定的场景应该是比较常见的。具体的需求描述如下。需要从网页或者富文本编辑器中获取到内容。
然后需要把这些内容里面包括各种HTML标签在内的所有内容都进行清理。目标是把所有的这些乱糟糟的代码痕迹全部去除。最终只保留那些干净的、没有任何格式的纯文本内容。
比如说, 你从那一个数据库里面, 把这么一段的内容, 给拿到手之后是怎么样的一种情况。
标题
这是第一段红色文字
也就是你内心深处所渴望获得的那部分。
标题这是第一段红色文字如果不去使用现成的库, 而是自己写代码来加以处理, 那么在很多情况之下, 人们往往容易陷入到各种各样的麻烦里面去。今天的话, 这里就来聊一下一种相对而言比较省事的解决方案方式吧。
自己写正则为什么容易翻车?
很多刚刚入手的人, 他们产生的最初的、直接的念头会是这样: 是不是可以直接利用正则表达式这种技术, 把位于某些特定位置之间的内容全部删除掉, 这样一来不就可以解决问题了吗?
这个计划本身看上去并不复杂, 不过等到真正动手去执行的时候, 很可能就会踩到好几个不容易发现的陷阱。
踩到第一个大坑, 发现标签里面竟然包含了属性。
对于这些类型的标签而言, 其内部所包含的相关属性具体有多长其实并不固定, 所以如果我们在去编写正则的时候没有做到足够严谨的情况存在那就很可能会造成某些结果被遗漏掉的这个现象。
这第二个坑呢, 其实是关于那个嵌套标签的问题。
HTML标签是可以实行嵌套的方式存在的, 打个比方来说。面对这种层次结构的时候, 正则表达式难以予以妥善的处理。
第三个问题所在的地方, 也就是那个坑, 是自闭合标签。
对于这种类型的自闭合标签, 还有注释这些内容, 都必须给予特别的处理, 需要单独来进行处理。
第四个坑是脚本以及样式这块内容。
和标签里面的那些内容, 虽然说也是被包在尖括号里面的, 但是它实际上是不应该出现在最终拿出来的纯文本文件里面的。要是你自己去写那个正则表达式的话, 那是很容易就会把源代码也给当成正文的一部分给提取出来弄错了的。
所以, 与其自己重新去实现那些基础的功能模块, 还不如直接调用市面上已有的、被其他开发者进行过高阶抽象和整合的现成软件组件库。
一种可行的解决方案
这里, 我们以那个叫作Spire.Doc for Java的软件工具当作例子来说明一下情况, 这个软件它的核心本事用来处理那些扩展名是DOC或者DOCX的微软办公软件文件, 不过, 它的设计者也顺手做了一件事, 就是给程序员提供了一个叫做API的程序接口, 通过这个接口, 大家可以很轻松地实现把以HTML开头的网页格式文档读进来, 并且把这些文档里面的文字内容给一个一个提取出来的功能。
实现思路是这样的, 先把HTML文件作为一个文档模型加载到内存里面, 接着再去调用一个办法, 把这篇文档里头所有的肉眼看得见的文字全都提取出来, 至于底层是怎么自动把各种标签、属性还有脚本和样式过滤掉的这些细节,开发者完全不用去操心具体的解析逻辑。
三步搞定:配置、写代码、运行
第一步:添加依赖
在Maven项目的配置文件里, 也就是那个叫的文件中, 我们需要加入下面这一段配置内容。
e-iceblue
https://repo.e-iceblue.com/nexus/content/groups/public/
e-iceblue
spire.doc
14.3.1
就是接着走第二步, 把用来提取数据的代码写出来。
import com.spire.doc.Document;import com.spire.doc.FileFormat;
import java.io.FileWriter;
import java.io.IOException;
public class HtmlExtractDemo {
public static void main(String[] args) {
Document doc = new Document();
// 加载HTML文件(指定格式为Html)
doc.loadFromFile("C:/test/article.html", FileFormat.Html);
// 提取纯文本
String text = doc.getText();
// 打印结果
System.out.println(text);
// 保存到txt文件
try (FileWriter writer = new FileWriter("C:/test/output.txt")) {
writer.write(text);
System.out.println("文本已保存至 output.txt");
} catch (IOException e) {
System.err.println("保存失败:" + e.getMessage());
}
}
}
核心代码这一块, 其实呢它就是两步, 就是加载和提取, 剩下所有的那些内容呢全部都是为了进行输入和输出的处理。
进入第三步, 开始对中文乱码的情况进行处理。
一旦咱们从文件里面提取出来的中文内容变得乱七八糟, 读都读不清的话, 这通常是因为那个HTML文件没有把字符集给正确地声明清楚。所以在开始加载之前, 咱们应该先把这个文件转换一下, 让它变成UTF-8编码, 又或者是等获取到文本以后, 再去明确指定输出的编码格式, 以此来解决这个问题。
如果情况是出现了一个网页链接, 那么应该进行什么样的处置手段呢?
假设说, 那一段的HTML代码内容, 并不是存放于电脑本机里面的, 而是位于某个网址上面, 比如。
这个工具自身是没有提供网络请求能力的, 因此必须先行独立执行网页内容的抓取与下载任务, 后续才能开展其他操作。
在接下来的是一个完整的过程展示, 首先发起请求去获取网页内容, 随后再从其中提取出相关的文本信息。
import java.io.*;import java.net.*;
import com.spire.doc.Document;
import com.spire.doc.FileFormat;
public class UrlToText {
public static void main(String[] args) throws Exception {
// 第一步:请求网页,拿到HTML源码
URL url = new URL("https://example.com/article.html");
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("GET");
conn.setConnectTimeout(5000);
conn.setRequestProperty("User-Agent", "Mozilla/5.0");
StringBuilder html = new StringBuilder();
try (BufferedReader reader = new BufferedReader(
new InputStreamReader(conn.getInputStream(), "UTF-8"))) {
String line;
while ((line = reader.readLine()) !
= null) {
html.append(line).append("\n");
}
}
// 第二步:保存为临时HTML文件
try (FileWriter writer = new FileWriter("temp.html")) {
writer.write(html.toString());
}
// 第三步:用本文的方法提取文本
Document doc = new Document();
doc.loadFromFile("temp.html", FileFormat.Html);
String result = doc.getText();
System.out.println("提取结果:\n" + result);
}
}
需要注意的几个点
关于性能
通过实际测试发现, 针对一个体积大约为200KB的HTML文件而言, 执行从加载到提取这整个过程, 其所消耗的时间点大概会处在100毫秒至300毫秒这个区间范围内, 面对绝大多数具体的业务应用场景来说, 这样的处理速度是完全可以满足使用需求的。
然而, 假如某次工作需求需要一次性对多达数千个的文件进行相关处理操作的话, 那么为了保障效率与稳定性, 建议相关人员务必加上线程池机制或者采取分批处理的策略来进行。
关于文本格式
这个()返回的结果是纯文本的内容, 在HTML中的表格、图片、颜色还有字号等格式信息都会被丢掉了, 如果你是想要保留这些格式的(比如说要转成某种带有格式的形式的话), 这种方案就不太适合你了。
关于版本
该工具拥有不少版本, 其中免费版足以支撑本文所提及的基础文本提取功能, 开发者完全可以根据自身的具体需要去挑选合适的那个版本。
请问除此之外, 还有没有其他的相关选择呢。
当然存在这种情况。假如你并不打算去引入那个特定的库, 那么还有一个备选的方案, 那就是选择使用Jsoup。这个工具主要是专门用于对HTML进行解析操作的, 它的总体积比较小, 而且在开发者社区之中拥有比较活跃的讨论气氛。关于它的使用方式, 也是比较简单的, 具体的写法可以表现为以下这样的形式:
String html = "";
String text = Jsoup.parse(html).text();
如果你是要把这两样东西拿来做一个对比的话。
在比较的时候, 绝对说哪一个更好是不存在的, 开发者需要根据自己项目具体的情况来做选择。
总结
从HTML里面提取出来的纯文本, 这个事情看起来好像特别简单, 可是真的做起来的话, 里面涉及到好多技术上的小细节, 比如说标签是嵌套在一层层里的, 还有各种属性的过滤, 以及字符还要进行转义处理等等, 这些东西如果你自己去用正则表达式来实现的话, 非常容易出错, 因此不容易做对。
这里我们就拿Spire.Doc for Java这个软件当作例子来详细说一说, 介绍一个基于整个文档模型来实现这个方法, 具体是怎么做的, 就是把那个HTML代码当成一个文档对象先加载进来, 然后再去调用专门用来提出文本的那个方法, 通过这样一番操作就可以拿到已经经过过滤的干干净净的纯文本了, 而且最让人佩服的是, 这种写法所需要的核心代码数量其实非常少, 根本不用担心写完还容易有问题。
该手段能够适用于那些需要赶快把HTML里面的标签给弄掉的开发环境情况, 必须得说清楚一点的是, 这样做不过是大家能想到好多种办法当中的一个罢了, 开发程序员完全可以去按照自己手头这个项目目前现有的那些依赖库, 还有对于运行速度性能的要求, 以及文件大小体积所设下的限制这些方面因素, 最终在文档模型这个路子跟像Jsoup那样的体积比较轻的HTML解析器工具这两个选择之间, 做出一个合乎情理的技术方向选择决定出来。