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校验),在不需要校验的场景,需要容错性好及过滤性能高的场景下,是非常有优势的。

时间: 2024-10-10 16:07:20

XmlParser和HtmlParser的相关文章

推荐使用Tiny Framework web开发UI组件

TINY FRAMEWORK 基于组件化的J2EE开发框架,from:http://www.tinygroup.org/ 名字 Tiny名称的来历 取名Tiny是取其微不足道,微小之意. Tiny的构建者认为,一个J2EE开发框架是非常复杂的,只有把框架分解成非常细小.可控的部分,并且对每个细小.可控的部分都有一个最优解或相对最优解, 那么整个方案也就可以非常不错的落地. 策略 Tiny框架的构建策略 Think big, start small, scale fast. 想法要宏伟,但是要从小

[Python3]模块和包

模块 在Python中模块可以理解为颗粒度更大的组织方式,其中可以包含类.函数.变量等等资源. 而为了更好的把一些基础服务提供给大家应用,Python提供了大量的标准模块,以及大量开源的第三方模块. 我们先看一下模块导入的基本格式: # 方式一 # 直接导入整个模块 import 模块名 # 例如导入sys模块 import sys # 方式二 # 从模块中导入指定的类.方法等资源 from 模块名 import 模块/类/方法/变量 # 例如从sys中导入path from sys impor

Tiny1.2.0-SNAPSHOT全面开源

感谢 感谢OSChina为我们提供了这么好的共享与交流平台. 感谢红薯及OSChina管理团队所有打过没打过交道同仁对Tiny一直以来的支持与帮助. 感谢所有鼓励和批评我的同学们,鼓励者为我们添加勇气,批评者让我们弥补不足. 感谢那些关注或点击过我的同学们,你们每一次的点击都会给我们力量,让我们坚信我们做的是有意义的:也感谢那些从来没有点击过我的同学们,因为你们让我知道我们还需要做得更多,更努力,更扎实. 当然尤其要感谢我的团队,正是我的团队们几年来对我的强力支持,才使我的梦想慢慢一步步变成现实

HtmlParser

HtmlParser 基本类库使用 HtmlParser 提供了强大的类库来处理 Internet 上的网页,可以实现对网页特定内容的提取和修改.下面通过几个例子来介绍 HtmlParser 的一些使用.这些例子其中的代码,有部分用在了后面介绍的简易爬虫中.以下所有的代码和方法都在在类 HtmlParser.Test.java 里,这是笔者编写的一个用来测试 HtmlParser 用法的类. 迭代遍历网页所有节点 网页是一个半结构化的嵌套文本文件,有类似 XML 文件的树形嵌套结构.使用Html

htmlparser 学习

htmlparser 学习系列 htmlparser 使用法使用与详解

使用HtmlParser抓去网页内容

package parser; import org.htmlparser.Parser; import org.htmlparser.beans.StringBean; importorg.htmlparser.filters.NodeClassFilter; importorg.htmlparser.parserapplications.StringExtractor; import org.htmlparser.tags.BodyTag; import org.htmlparser.uti

利用htmlparser提取网页纯文本的例子

import org.htmlparser.Node; import org.htmlparser.NodeFilter; import org.htmlparser.Parser; importorg.htmlparser.filters.TagNameFilter; import org.htmlparser.tags.TableTag; import org.htmlparser.util.NodeList; /** * 标题:利用htmlparser提取网页纯文本的例子 */ publi

python中HTMLParser简单理解

找一个网页,例如https://www.python.org/events/python-events/,用浏览器查看源码并复制,然后尝试解析一下HTML,输出Python官网发布的会议时间.名称和地点. 1 from html.parser import HTMLParser 2 from html.entities import name2codepoint 3 4 class MyHTMLParser(HTMLParser): 5 6 in_title = False 7 in_loca

HtmlParser中的各种Filter(1)

所有的Filter均实现了NodeFilter接口,此接口只有一个方法Boolean accept(Node node),用于确定某个节点 是否属于此Filter过滤的范围. HtmlParser在org.htmlparser.filters包之内一共定义了16个不同的Filter,也可以分为几类. 判断类Filter: TagNameFilter HasAttributeFilter HasChildFilter HasParentFilter HasSiblingFilter IsEqual