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.pswp.cn/news/284804.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/284804.shtml
英文地址,請注明出處:http://en.pswp.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模組,其…