经常要用的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); /** /** /** /** /** /** /** /** /** /** /** /** /** /** /** /** /** /** /** /** /** /** /** /** /** /** |
为了避免接口太过庞大,因此把格式化的处理放在独立的结构中进行处理。
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); /** 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() { |
下面看看时间耗费情况:
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校验),在不需要校验的场景,需要容错性好及过滤性能高的场景下,是非常有优势的。