XmlParser和HtmlParser

经常要用的Xml和Html解决,实际上这个领域也有非常好的解决方案。

相对来说现在各种开源的Xml解析功能比较丰富,机制也比较灵活,但是由于他功能比较完善,干的事情比较多,所以性能方面也慢一点;另外,由于Xml天生是有严格格式的,所以问题不大,但是Html文件的内容是良莠不齐,有的网站经常缺少关闭标签,有的开始是大写,关闭是小写等等,没有严格遵守规范的时候,连Dom结构也解不正确,对于数据抓取程序来说,这就会严重影响正确性。

另外,一个重要的问题是数据遍历,一般来说在数据遍历方面,开源框架没有在性能做过充分优化,因此,如果要进行高速检索,就需要进行程序扩展。为此,本人编写一套XmlParser和HtmlParser,在数据校验方面做了删减,不支持进行数据校验,在容错性方面做了扩充,在Html解决时,即使格式不正确,在大多数情况下也可以返回正确的结果。最坏的情况也,也可以解决出Dom,但是Dom结构不一定正确,而不会出现崩溃或解析异常的问题。

还有一个是简体中文标签的支持能力,比如: <中文 属性1="1" 属性2="b" />

OK,费话少说,看看调用代码。

?
1
2
XmlStringParser parser = new XmlStringParser();
XmlDocument xmlDocument = parser.parse("<aa a=\"1\"><!--aa --><a a=\"aa\"></a></aa>");
上面就已经把xml解析好了。
?
1
2
HtmlStringParser parser = new HtmlStringParser();
HtmlDocument xmlDocument = parser.parse("<aa a=\"1\"><!--aa --><a a=\"aa\"></a></aa>");
上面就已经把html解析好了。

由于Xml及Html都是用得统一的接口,所以,会了Xml解析,Html也是一样样的。

解析出的Node,都实现了下面的接口,因此遍历方面也是非常方便的。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
public interface Node<T extends Node<T>> extends ForEachProcessor<T> {
    /**
     * 获取结点头标签相关内容
     *
     * @return StringBuffer
     */
    void getHeader(StringBuffer sb);
    /**
     * 返回子节点
     *
     * @param name
     * @return
     */
    List<T> getSubNodes(String name);
    /**
     * 添加内容节点
     *
     * @param content
     */
    void addContent(String content);
    /**
     * 设置结点名称
     *
     * @param name
     */
    void setNodeName(String name);
    /**
     * 获取结尾标签
     *
     * @return StringBuffer
     */
    void getFooter(StringBuffer sb);
    /**
     * 获取根结点
     *
     * @return T
     */
    T getRoot();
    /**
     * 设置父亲节点
     *
     * @param parent
     */
    void setParent(T parent);
    /**
     * 返回节点名称
     *
     * @return
     */
    String getNodeName();
    /**
     * 返回父亲节点
     *
     * @return
     */
    T getParent();
    /**
     * 返回中间内容
     *
     * @return
     */
    StringBuffer getBody();
    /**
     * 写出数据
     *
     * @param stream
     * @throws IOException
     */
    void write(OutputStream stream) throws IOException;
    /**
     * 返回节点类型
     *
     * @return
     */
    NodeType getNodeType();
    /**
     * 返回属性
     *
     * @param attributeName
     * @return
     */
    String getAttribute(String attributeName);
    /**
     * 删除属性
     *
     * @param attributeName
     */
    void removeAttrivute(String attributeName);
    /**
     * 设置属性值
     *
     * @param attributeName
     * @param value
     */
    void setAttribute(String attributeName, String value);
    /**
     * 添加节点
     *
     * @param node
     *            要增加的节点
     * @return 如果增加成功,则返回node节点,否则返回null
     */
    T addNode(T node);
    /**
     * 删除节点
     *
     * @param node
     * @return 删除的节点,如果当前节点中不包含node节点,则返回null
     */
    T removeNode(T node);
    /**
     * 删除指定节点
     *
     * @param nodeName
     * @return
     */
    List<T> removeNode(String nodeName);
    /**
     * 获取内容
     *
     * @return
     */
    String getContent();
    /**
     * 变成StreamBuffer
     *
     * @return
     */
    StringBuffer toStringBuffer();
    /**
     * 设置内容
     *
     * @param content
     */
    void setContent(String content);
    /**
     * 返回属属性
     *
     * @return
     */
    Map<String, String> getAttributes();
    /**
     * 返回子节点
     *
     * @return
     */
    List<T> getSubNodes();
    /**
     * 是否单节点
     *
     * @return
     */
    boolean isSingleNode();
    /**
     * 是否大小写敏感
     *
     * @return
     */
    boolean isCaseSensitive();
    /**
     * 根据大小写相关返回名字
     *
     * @param name
     * @return
     */
    String getCaseSensitiveName(String name);
    /**
     * 返回纯文本内容
     *
     * @return
     */
    String getPureText();
}
为了避免接口太过庞大,因此把格式化的处理放在独立的结构中进行处理。
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public interface NodeFormater<E extends Node<E>, T extends Document<E>> {
    /**
     * 格式化文档
     *
     * @param doc
     * @return String
     */
    String format(T doc);
    void setEncode(String encode);
    /**
     * 格式化文档、 并在指定的输出流中输出
     *
     * @param doc
     * @param out
     * @return void
     * @throws IOException
     */
    String format(E node);
    void format(T doc, OutputStream out) throws IOException;
    void format(E node, OutputStream out) throws IOException;
}
要格式化输入的话,下面的代码就可以了:
?
1
2
3
HtmlDocument doc= new XmlStringParser().parse("<html 中='文'><head><title>aaa</title></head></html>");
HtmlFormater f = new HtmlFormater();
System.out.println(f.format(doc));
输出结果如下:

?
1
2
3
4
5
6
7
<html 中="文">
  <head>
    <title>
      aaa
    </title>
  </head>
</html>
上面已经演示了解析和格式化以及遍历,下面看看检索。

首先构建60*60*60,三层的Dom结构,也就是现在有216000个Dom节点

?
1
2
3
4
5
6
7
8
9
10
XmlNode node = new XmlNode("root");
for (int i = 0; i < 60; i++) {
    XmlNode a = node.addNode(new XmlNode("a" + i));
    for (int j = 0; j < 60; j++) {
        XmlNode b = a.addNode(new XmlNode("b" + j));
        for (int k = 0; k < 60; k++) {
            b.addNode(new XmlNode("c" + k));
        }
    }
}
然后对其进行节点查找,用两种方法进行10000次节点过滤:
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public void testSpeed() {
    long t21 = System.currentTimeMillis();
    QuickNameFilter quick = new QuickNameFilter(node);
    long t22 = System.currentTimeMillis();
    System.out.println("quick初始化用时" + (t22 - t21));
    long t1 = System.currentTimeMillis();
    String nodeName = null;
    for (int x = 0; x < 10000; x++) {
        nodeName = quick.findNode("b6").toString();
    }
    long t2 = System.currentTimeMillis();
    System.out.println("QuickNameFilter用时" + (t2 - t1));
}
public void testSpeed1() {
    long t21 = System.currentTimeMillis();
    FastNameFilter fast = new FastNameFilter(node);
    long t22 = System.currentTimeMillis();
    System.out.println("fast初始化<span></span><span></span>用时" + (t22 - t21));
    long t1 = System.currentTimeMillis();
    String nodeName = null;
    for (int x = 0; x < 10000; x++) {
        nodeName = fast.findNode("b6").toString();
    }
    long t2 = System.currentTimeMillis();
    System.out.println("FastNameFilter用时" + (t2 - t1));
}

下面看看时间耗费情况:

?
1
2
3
4
quick初始化用时385
QuickNameFilter用时376
fast初始化用时122
FastNameFilter用时330
可以看到fast的初始化时间及查找用时,都是最快的;而quick的初始化时间和查找用时相比要慢一些。但是请注意,这都是在216000个节点中查找10000次所耗费的时间。

那么再用传统的方式试一下---一般的开源方式也差不多在这个量级。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
public void testSpeed2() {
    long t11 = System.currentTimeMillis();
    NameFilter filter = new NameFilter(node);
    long t12 = System.currentTimeMillis();
    System.out.println("Name初始化用时" + (t12 - t11));
    long t1 = System.currentTimeMillis();
    String nodeName = null;
    for (int x = 0; x < 10; x++) {
        nodeName = filter.findNode("b6").toString();
    }
    long t2 = System.currentTimeMillis();
    System.out.println("NameFilter用时" + (t2 - t1));
}
运行结果:
?
1
2
Name初始化用时12
NameFilter用时83
但是,请注意,他的查询次数是10次,如果变成10000次,就是83000ms,也就是83秒之多。与Fast过滤方式相差了680倍之多。

小结:我们实现的Xml及HtmlParser确实是有自己独特的优点(学习成本低,Html和Xml解析方法一致,格式化输出,紧凑输出,容错性,查询效率高等等),也有不足(不支持DTD,XSD校验),在不需要校验的场景,需要容错性好及过滤性能高的场景下,是非常有优势的。

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

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

相关文章

【QGIS入门实战精品教程】3.2:QGIS如何打开ArcGIS创建的个人数据库(MDB)?

文章目录 1. 下载并安装Microsoft Access 2010数据库引擎2. 配置系统环境变量3. 打开mdb数据库【相关阅读】 【QGIS入门实战精品教程】005:QGIS如何打开ArcGIS创建的文件数据库(GDB)? 1. 下载并安装Microsoft Access 2010数据库引擎 https://www.microsoft.com/zh-cn/down…

SharedPreferences操作数据

SharedPreferences是一个简单轻量的android存储数据类。适合保存配置信息等。是以XML方式存储的。 存储数据 SharedPreferences sharedPreferencesgetSharedPreferences("test", Context.MODE_PRIVATE);sharedPreferences.edit().putString("userName",&qu…

[转]IaaS、PaaS、SaaS、CaaS、MaaS五者的区别

云计算构架图 很明显&#xff0c;这五者之间主要的区别在于第一个单词&#xff0c;而aaS都是as-a-service&#xff08;即服务&#xff09;的意思&#xff0c;这五个模式都是近年来兴起的&#xff0c;且这五者都是云计算的落地产品&#xff0c;所以我们先来了解一下云…

使用java开发简单的mis系统所需的技术

开发mis系统用到的技术 1. b/s架构&#xff1a;就broser/server&#xff0c;浏览器/服务器的说法。服务器端要运行tomcat&#xff0c;提供链接数据库服务供java代码读写数据&#xff0c;这个可以在eclipse中配置运行。浏览器则解释jsp或html格式中的标记元素&#xff0c;用于显…

聊聊 C++ 中的四种类型转换符

一&#xff1a;背景 在玩 C 的时候&#xff0c;经常会用 void* 来指向一段内存地址开端&#xff0c;然后再将其强转成尺度更小的 char* 或 int* 来丈量一段内存&#xff0c;参考如下代码&#xff1a;int main() {void* ptr malloc(sizeof(int) * 10);int* int_ptr (int*)ptr;…

【QGIS入门实战精品教程】3.1:QGIS如何连接SQL Server数据库?

文章目录 软件必备测试SQL Server服务是否启动QGIS与SQL Server建立连接软件必备 SQL Server 2008 R2QGIS 3.22.3测试SQL Server服务是否启动 打开SQL Server配置管理器,查看MSSQLSERVER服务的启动情况。

开源重量级的流程引擎或UI引擎

关注这两个方面的同学个踊跃加粉了~~同时在下面回复期望开源哪一个&#xff0c;将先开源呼声高的一个。 好吧&#xff0c;先小秀一下UI&#xff1a; 增加下面的pom依赖&#xff0c;表示此工程需要org.tinygroup.aerowindow ?12345<dependency> <groupId>org.tinyg…

阿里云网盘内测开启 填写申请表获取邀请码 附最新申请地址

日前有相关报道显示&#xff0c;阿里方面推出了一款名为“阿里云网盘”的独立App&#xff0c;并且其是由阿里云团队开发&#xff0c;定位则是为C端用户提供可靠安全的存储备份及智能相册等功能。据了解&#xff0c;目前这款APP尚未正式上线&#xff0c;暂时还在邀请制测试阶段。…

02 JRE与JDK

JRE (Java Runtime Environment) JAVA 运行环境 包括JAVA虚拟机和JAVA程序所需要的核心类库&#xff0c;如查想要运行一个开发好的JAVA程序&#xff0c;计算机只需要安装JRE即可 JDK&#xff08;JAVA Development Kit &#xff09;JAVA开发工具包 JDK是提供给JAVA开发人员使用…

第 133 章 FAQ

133.1. Haproxy 与 Nginx Haproxy 与 Nginx 都能实现负载均衡&#xff0c;那么 Haproxy 与 Nginx proxy 有什么差异&#xff0c;我们怎样选择两种方案。 如果是用于 HTTP 负载均衡我建议使用 Nginx&#xff0c;它可以SSL证书挂载&#xff0c;缓存定制&#xff0c;实现各种复杂的…

跟我做⼀个⾼德地图的 iOS / Android MAUI控件(Android 原⽣库绑定)

我们已经介绍了如何通过 .NET 绑定 iOS 原⽣库 &#xff0c;本篇开始介绍⼀下如何通过 .NET 绑定 Android 原⽣库。Android的库Android 的库以 .jar 做打包&#xff0c; 通过⼯具你可以将多个 .jar 完成绑定&#xff0c;然后通过 C# 调⽤原⽣的 Java 库。对⽐起 iOS &#xff0…

【QGIS入门实战精品教程】4.6:QGIS实现栅格(影像、DEM)的拼接与掩膜提取

参考阅读:ArcGIS实验教程——实验十一:影像拼接与提取 加载实验数据 本实验所采用的栅格数据为两个dem数据和一个矢量范围数据,加载如下图所示: 栅格数据信息如下: 栅格拼接 点击下拉菜单【栅格】→【杂项】→【合并(merge)】,如下所示:

ReSharper 2020.2 补丁

ReSharper 是一个JetBrains公司出品的著名的代码生成工具。其能帮助Microsoft Visual Studio成为一个更佳的IDE&#xff0c;它包括一系列丰富的能大大增加C#和Visual Basic .net开发者生产力的特征。使用ReSharper&#xff0c;你可以进行深度代码分析&#xff0c;智能代码协助…

【转】【公司调查】车来了APP

http://blog.sina.com.cn/s/blog_83b10acc0102vk7k.html【APP简介】"车来了"是武汉元光科技有限公司开发的一款查询公交车实时位置的手机软件。不仅能提供公交车的到站距离、预计到站时间&#xff0c;还能显示整条线路的实时通行状况&#xff0c;让人们不再盲目等待&…

零中频接收机主要问题

直流偏差和本振泄漏问题基本不会影响超外差式接收机的性能&#xff0c;问题主要是镜频抑制。需要高Q值的带通滤波器。 零中频不存在镜频干扰&#xff0c;可以省掉镜像抑制滤波器和中频滤波器。零中频的主要问题是&#xff1a;1直流偏差 2本振泄漏 3 闪烁噪声。 1 本振泄漏 本振…

军哥华为HCNP(科目H12-221)真题解析课程:1-30题

华为HCNP&#xff08;科目H12-221&#xff09;真题解析HCNP-R&S-IERS&#xff1a;(Huawei Certified Network Professional-Implementing Enterprise Routing and Switching Network)第1部分 如何参加HCNP考试1.1很简单&#xff0c;华为HCNP即使没有HCNA证书也可以考取&…

Blazor University (36)组件库

原文链接&#xff1a;https://blazor-university.com/component-libraries/组件库组件库使我们能够将组件和页面以及任何支持文件&#xff08;例如 CSS 文件、JavaScript 和图像&#xff09;打包到一个可重用的项目中。创建一个名为 ClassLibraryConsumer 的新 Blazor 解决方案…

【QGIS入门实战精品教程】9.1:QGIS构建泰森多边形(Thiessen Polygon)实例精解

泰森多边形是进行快速插值和分析地理实体影响区域的常用工具。例如,用离散点的性质描述多边形区域的性质,用离散点的数据计算泰森多边形区域的数据。泰森多边形可用于定性分析、统计分析和临近分析等。 参考教程: ArcGIS构建泰森多边形(Thiessen Polygon)实例精解 【Glob…

“Visual Studio 启动不能打开上次打开的文件” 最正确的解决姿势

网上很多提供的方法&#xff0c;不是删除.vs目录&#xff0c;就是修改.suo文件。 删除有风险&#xff0c;操作需谨慎&#xff01;&#xff01; 其实最简单的方法就是&#xff1a;工具——选项——项目和解决方案——☑加载解决方案时重新打开文档(D)

【QGIS入门实战精品教程】5.1:QGIS地理坐标转火星坐标系(GCJ02)案例教程

本文以案例的形式,讲述WGS84(GPS)、火星坐标系(GCJ02)、百度地图(BD09)坐标系之间互相转换。 一、WGS转火星坐标系对比 文中将行政区的WGS坐标转为火星坐标系,局部效果对比: 二、火星坐标系简介 火星坐标系统是一种国家保密插件,也叫做加密插件或者加偏或者SM模组,其…