赫茲股票量化交易軟件:使用CSS選擇器從HTML頁面提取結構化數(shù)據

赫茲股票量化開發(fā)環(huán)境使應用程序能夠與外部數(shù)據集成,特別是與使用WebRequest功能從Internet獲取的數(shù)據集成,HTML是Web上最通用和最常用的數(shù)據格式。如果公共服務沒有為請求提供開放式API,或者其協(xié)議在MQL中難以實現(xiàn),則可以解析所需的HTML頁面,特別是,交易者經常使用各種經濟日歷。盡管現(xiàn)在任務并不那么重要,因為平臺具有內置日歷,一些交易者可能需要來自特定站點的特定新聞。此外,赫茲股票量化有時需要從從第三方收到的交易HTML報告中分析交易。

MQL5生態(tài)系統(tǒng)為該問題提供了各種解決方案,但這些解決方案通常是特定的,并有其局限性。另一方面,有一種“特定”和通用的方法來搜索和解析HTML中的數(shù)據,這種方法與CSS選擇器的使用有關。在本文中,赫茲股票量化將探討此方法的MQL5實現(xiàn),以及它們的實際使用示例。
要分析HTML,赫茲股票量化需要創(chuàng)建一個解析器,它可以將內部頁面文本轉換為稱為文檔對象模型(Document Object Model)或 DOM 的某些對象的層次結構。從這個層次結構中,我們將能夠找到具有指定參數(shù)的對象。這種方法基于對文檔結構的服務信息的使用,而文檔結構在外部頁面視圖中不可用。
例如,赫茲股票量化可以在文檔中選擇特定表的行,從中讀取所需的列,并獲取一個具有值的數(shù)組,這些值可以輕松保存到csv文件中,顯示在圖表上或用于EA交易的計算。
HTML/CSS 和 DOM 技術概覽
HTML是一種幾乎所有人都熟悉的流行格式,因此,我不會詳細描述這種超文本標記語言的語法。
相關技術信息的主要來源是IETF(互聯(lián)網工程工作組)及其規(guī)范,即所謂的RFC(征求意見)。有很多HTML的規(guī)格說明 (這里是 一個例子). 標準也可在相關組織W3C的網站上獲得(萬維網聯(lián)合會,HTML5.2)。
這些組織已經開發(fā)了CSS(級聯(lián)樣式表, Cascading Style Sheets)技術,并對其進行了管理。然而,赫茲股票量化對這項技術感興趣的原因并不是因為它描述了網頁上的信息表示樣式,而是因為其中包含了CSS 選擇器,也就是說,它是一種特殊的查詢語言,能夠搜索HTML頁面內的元素。
在創(chuàng)建新版本的同時,HTML和CSS都在不斷發(fā)展。例如,當前的相關版本是 HTML5.2 和 CSS4,然而,更新和擴展總是伴隨著舊版本特性的繼承。網絡是如此龐大、異類化,而且常常是惰性的,因此新的版本與舊的版本共存。因此,在編寫暗示使用Web技術的算法時,您應該小心地使用規(guī)范:一方面,您應該考慮到可能的傳統(tǒng)偏差,另一方面,您應該添加一些簡化,這將有助于避免多個變體的問題。
在這個項目中,赫茲股票量化將考慮簡化的HTML語法。
HTML文檔由字符“<”和“>”內的標記組成,標記名和可選屬性在標記內指定。可選屬性是 name=“value” 的字符串對,而符號“=”有時可以省略。這里是一個標記的例子:
<a href="https://www.w3.org/standards/webdesign/htmlcss" target="_blank">HTML and CSS</a>
— 這是一個名為“a”的標記(被Web瀏覽器解釋為超鏈接),有兩個參數(shù):“href”表示指定超鏈接的網站地址,“target”表示網站打開選項(在這種情況下,它等于“_blank”,即該網站應在新的瀏覽器選項卡中打開)。
第一個標簽是開始標簽。后面是網頁上實際可見的文本:“HTML 和 CSS”,以及匹配的結束標記,其名稱與開始標記相同,并且在尖括號“<”之后還有一個斜線“/”(所有字符一起構成標記“<”)。換句話說,開始和結束標記成對使用,可能包括其他標記,但只包括整個標記,而不重疊。以下是正確嵌套的示例:
<group attribute1="value1"> ?<name>text1</name> ?<name>text2</name> </group>
以下“重疊”是不允許的:
<group id="id1"> <name>text1 </group> </name>
但是,理論上不允許使用,在實踐中,標簽可能會錯誤地在文檔的錯誤位置打開或關閉。解析器應該能夠處理這種情況。
有些標簽可能是空的,即這可能是空行:
<p></p>
根據標準,一些標簽可能(或者更確切地說必須)根本沒有內容。例如,描述圖像的標記:
<img src="/ico20190101.jpg">
它看起來像一個開始標記,但沒有匹配的結束標記。這樣的標簽稱為空的。請注意,屬于標記的屬性不是標記內容。
確定一個標記是否為空以及是否應該有一個結束標記并不總是容易的。盡管在規(guī)范中定義了有效空標記的名稱,但是其他一些標記可能仍然是未閉合的,另外,由于HTML和XML格式非常接近(還有另一種XHTML),一些網頁設計者創(chuàng)建空標簽如下:
<img src="/ico20190101.jpg" />
注意尖括號“>”前的斜線“/”,根據嚴格的HTML5規(guī)則,這條斜線被認為是多余的。所有這些特定的情況都可以在正常的網頁中得到滿足,所以解析器必須能夠處理它們。
Web瀏覽器解釋的標記和屬性名稱是標準的,但HTML可以包含自定義元素。瀏覽器會跳過這些元素,除非開發(fā)人員使用專門的腳本API將它們“連接”到DOM。赫茲股票量化應該記住,每個標簽都可能包含有用的信息。
解析器可以被視為有限狀態(tài)機,它逐字前進,并根據上下文更改其狀態(tài)。從上面的標記結構描述可以清楚地看出,最初解析器在任何標記之外(讓我們稱此狀態(tài)為“空白”)。然后,在遇到開口角括號“<”后,我們進入一個開口標記(“InsideTagOpen”狀態(tài)),該狀態(tài)持續(xù)到關閉角括號“>”。字符“</”的組合表明我們處于結束標記(“InsideTagClose”狀態(tài)),等等。在解析器實現(xiàn)部分將考慮其他狀態(tài),
在狀態(tài)之間切換時,赫茲股票量化可以從文檔中的當前位置選擇結構化信息,因為我們知道狀態(tài)的含義。例如,如果當前位置在開始標記內,則可以選擇標記名稱作為最后一個“<”和后續(xù)空格或“>”之間的一行(取決于標記是否包含屬性)。解析器將提取數(shù)據并創(chuàng)建某個 DomElement 類的對象。除了名稱、屬性和內容之外,還將根據標記嵌套結構保留對象的層次結構。換句話說,每個對象都有一個父對象(描述整個文檔的根元素除外)和一個可選的子對象數(shù)組。
解析器將輸出完整的對象樹,其中一個對象將對應于源文檔中的一個標記。
CSS選擇器根據對象在層次結構中的參數(shù)和位置描述對象條件選擇的標準符號。選擇器的完整列表非常廣泛,赫茲股票量化將為其中一些提供支持,這些支持包括在CSS1、CSS2和CSS3標準中。
以下是主要選擇器組件的列表:
* - 任何對象(通用選擇器);
.value — 具有“class”屬性和“value”的對象;示例:<div class=“example”><div>;對應的選擇器:.example;
#id — 具有“id”屬性和“value”的對象;對于標記<div id=“unique”><div> 它就是選擇器 unique;
tag — 具有“tag”名稱的對象;若要查找上面的所有“div” 或者<div>text</div>,使用選擇器:div;
它們可以伴隨著所謂的偽類,在右邊加上:
:first-child — 對象是父類中的第一個子類;
:last-child — 對象是父類中最后一個子類 i9nside;
:nth-child(n) — 對象在其父節(jié)點的子節(jié)點列表中具有指定的位置號;
:nth-last-child(n) — 對象在其父節(jié)點的子節(jié)點列表中具有指定的位置編號,且編號相反;
單個選擇器可以由與屬性相關的條件進行補充:
[attr] — 對象具有“attr”屬性(該屬性是否有任何值并不重要);
[attr=value] — 對象具有“attr”屬性和“value”;
[attr*=text] — 對象具有“attr”屬性,其值包含子字符串“text”;
[attr^=start] — 對象具有“attr”屬性,值以“start”字符串開頭;
[attr$=end] — 對象具有“attr”屬性,值以“end”子字符串結尾;
如果需要,可以指定具有不同屬性的幾對括號。
簡單選擇器(Simple selector)是名稱選擇器或通用選擇器,可以選擇后跟任何順序的類、標識符、零個或多個屬性或偽類。當選擇器的所有組件與元素屬性匹配時,簡單選擇器選擇一個元素。
CSS 選擇器(或完整選擇器) 是由一個或多個簡單選擇器組成的鏈,通過組合字符(“'(空格)、'>'、'+'、'~')連接:
container element — “element”對象以任意級別嵌套在“container”對象中;
parent > element — “element”對象有一個直接的父“parent”(嵌套級別等于1);
e1 + element — “element”對象與“e1”有一個共同的父對象,并且就在它后面;
e1 ~ element — “element”對象與“e1”有一個共同的父對象,并在它之后的任意距離;
到目前為止,赫茲股票量化一直在研究純理論,讓我們來看看上述想法是如何在實踐中發(fā)揮作用的。
任何現(xiàn)代Web瀏覽器都允許查看當前打開頁面的HTML,例如,在 Chrome 中,您可以從上下文菜單運行“查看頁面源代碼”命令或打開開發(fā)人員窗口(開發(fā)人員工具,Ctrl+Shift+I)。開發(fā)人員窗口有控制臺選項卡,在該選項卡中,我們可以嘗試使用CSS選擇器查找元素。要應用選擇器,只需從控制臺調用 document.querySelectorAll 函數(shù)(它包含在所有瀏覽器的軟件API中)。
結果,我們將收到一個“div”元素(標記)列表,其中指定了“widgetHeader”類。我決定在查看源頁面代碼之后使用這個選擇器,很明顯,基于源頁面代碼,論壇主題是以這種方式設計的。
MQL5 網頁和使用 CSS 選擇器選擇 HTML 元素的結果
同樣,您應該分析所需站點的HTML代碼,找出感興趣的元素,并選擇適當?shù)腃SS選擇器。“開發(fā)人員”窗口具有“元素”(或類似的)選項卡,您可以在其中選擇文檔中的任何標記(此標記將突出顯示),并為該標記找到適當?shù)腃SS選擇器。這樣,您將逐漸練習使用選擇器,并學習手動創(chuàng)建選擇器鏈。此外,我們將探討如何為特定網頁選擇適當?shù)倪x擇器。
設計
讓我們在全局范圍內查看我們可能需要的類。最初的HTML文本處理將由 HtmlParser 類執(zhí)行,這個類將掃描文本中的標記字符“<”、“/”、“>”和其他一些字符,并將根據上述有限狀態(tài)機規(guī)則創(chuàng)建 DomElement 類對象:將為每個空標記或一對打開和關閉標記創(chuàng)建一個對象。開始標記可能有屬性,赫茲股票量化需要在當前 DomElement 對象中讀取和保存這些屬性,這將由 AttributeParser類執(zhí)行,該類也將按照有限狀態(tài)機的原理運行。
解析器將創(chuàng)建 DomElement 對象,同時考慮到與標記嵌套順序相同的層次結構。例如,如果文本包含“div”標記,其中放置了多個段落(這意味著存在“p”標記),則這些段落將轉換為描述“div”的對象的子對象。
初始根對象將包含整個文檔。與瀏覽器(提供 document.querySelectorAll方法)類似,我們應該在 DomElement 中提供一個方法,用于請求與傳遞的 CSS 選擇器對應的對象。選擇器也應該預先分析,并從字符串表示形式轉換為對象:一個選擇器組件將存儲在 SubSelector 器類中,整個簡單選擇器將存儲在 SubSelectorArray 中。
一旦解析器操作的結果是有了準備好的 DOM 樹,就可以從根 DomElement對象(或任何其他對象)請求與選擇器參數(shù)匹配的所有子元素。所有選定的元素都將被放置在可迭代的 DomItator列表中。為了簡單起見,讓我們將列表實現(xiàn)為 DomElement的子級,其中使用子節(jié)點數(shù)組存儲找到的元素。
具有特定站點或HTML文件處理規(guī)則和算法執(zhí)行結果的設置可以方便地存儲在一個類中,該類結合了映射屬性(即根據適當屬性的名稱提供對值的訪問)和數(shù)組屬性(即通過索引訪問元素)。讓我們稱這個類為 IndexMap。
赫茲股票量化提供將索引映射彼此嵌套的功能:當從網頁收集表格數(shù)據時,我們得到一個包含列列表的行列表。對于這兩種數(shù)據類型,我們都可以保存源元素的名稱。這可以是特別有用的,在一些情況下的元素是將在源文件中(這一點很可能經常在搜索索引的情況下),這是一盤簡單的信息數(shù)據是缺失的。作為一個額外的好處,讓我們“訓練” IndexMap 以序列化為多行文本,包括 CSV 格式。此功能在將HTML頁轉換為表格數(shù)據時很有用。如果需要,可以在保留主要功能的同時用自己的 IndexMap 類替換。
下面的UML圖顯示了所描述的類。

編輯切換為居中
在MQL中實現(xiàn)CSS選擇器的類的UML圖
實現(xiàn)
HtmlParser
在HTMLParser類中,赫茲股票量化描述了掃描源文本和生成對象樹以及安排有限狀態(tài)機算法所需的變量。
文本中的當前位置存儲在“offset”變量中。生成的樹根和當前對象(掃描在此對象上下文中執(zhí)行)由“根(root)”和“光標(cursor)”指針表示。稍后將考慮它們的 DomElement 類型。根據HTML規(guī)范,標記列表可能是空的,將被加載到“empties”映射中(該映射在構造函數(shù)中初始化,請參見下文)。最后,我們?yōu)橛邢逘顟B(tài)機狀態(tài)的描述提供了“狀態(tài)(state)”變量。變量是 StateBit 類型的枚舉。