赫兹股票量化交易软件:使用CSS选择器从HTML页面提取结构化数据

赫兹股票量化开发环境使应用程序能够与外部数据集成,特别是与使用WebRequest功能从Internet获取的数据集成,HTML是Web上最通用和最常用的数据格式。如果公共服务没有为请求提供开放式API,或者其协议在MQL中难以实现,则可以解析所需的HTML页面,特别是,交易者经常使用各种经济日历。尽管现在任务并不那么重要,因为平台具有内置日历,一些交易者可能需要来自特定站点的特定新闻。此外,赫兹股票量化有时需要从从第三方收到的交易HTML报告中分析交易。

MQL5生态系统为该问题提供了各种解决方案,但这些解决方案通常是特定的,并有其局限性。另一方面,有一种“特定”和通用的方法来搜索和解析HTML中的数据,这种方法与CSS选择器的使用有关。在本文中,赫兹股票量化将探讨此方法的MQL5实现,以及它们的实际使用示例。
要分析HTML,赫兹股票量化需要创建一个解析器,它可以将内部页面文本转换为称为文档对象模型(Document Object Model)或 DOM 的某些对象的层次结构。从这个层次结构中,我们将能够找到具有指定参数的对象。这种方法基于对文档结构的服务信息的使用,而文档结构在外部页面视图中不可用。
例如,赫兹股票量化可以在文档中选择特定表的行,从中读取所需的列,并获取一个具有值的数组,这些值可以轻松保存到csv文件中,显示在图表上或用于EA交易的计算。
HTML/CSS 和 DOM 技术概览
HTML是一种几乎所有人都熟悉的流行格式,因此,我不会详细描述这种超文本标记语言的语法。
相关技术信息的主要来源是IETF(互联网工程工作组)及其规范,即所谓的RFC(征求意见)。有很多HTML的规格说明 (这里是 一个例子). 标准也可在相关组织W3C的网站上获得(万维网联合会,HTML5.2)。
这些组织已经开发了CSS(级联样式表, Cascading Style Sheets)技术,并对其进行了管理。然而,赫兹股票量化对这项技术感兴趣的原因并不是因为它描述了网页上的信息表示样式,而是因为其中包含了CSS 选择器,也就是说,它是一种特殊的查询语言,能够搜索HTML页面内的元素。
在创建新版本的同时,HTML和CSS都在不断发展。例如,当前的相关版本是 HTML5.2 和 CSS4,然而,更新和扩展总是伴随着旧版本特性的继承。网络是如此庞大、异类化,而且常常是惰性的,因此新的版本与旧的版本共存。因此,在编写暗示使用Web技术的算法时,您应该小心地使用规范:一方面,您应该考虑到可能的传统偏差,另一方面,您应该添加一些简化,这将有助于避免多个变体的问题。
在这个项目中,赫兹股票量化将考虑简化的HTML语法。
HTML文档由字符“<”和“>”内的标记组成,标记名和可选属性在标记内指定。可选属性是 name=“value” 的字符串对,而符号“=”有时可以省略。这里是一个标记的例子:
<a href="https://www.w3.org/standards/webdesign/htmlcss" target="_blank">HTML and CSS</a>
— 这是一个名为“a”的标记(被Web浏览器解释为超链接),有两个参数:“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">
它看起来像一个开始标记,但没有匹配的结束标记。这样的标签称为空的。请注意,属于标记的属性不是标记内容。
确定一个标记是否为空以及是否应该有一个结束标记并不总是容易的。尽管在规范中定义了有效空标记的名称,但是其他一些标记可能仍然是未闭合的,另外,由于HTML和XML格式非常接近(还有另一种XHTML),一些网页设计者创建空标签如下:
<img src="/ico20190101.jpg" />
注意尖括号“>”前的斜线“/”,根据严格的HTML5规则,这条斜线被认为是多余的。所有这些特定的情况都可以在正常的网页中得到满足,所以解析器必须能够处理它们。
Web浏览器解释的标记和属性名称是标准的,但HTML可以包含自定义元素。浏览器会跳过这些元素,除非开发人员使用专门的脚本API将它们“连接”到DOM。赫兹股票量化应该记住,每个标签都可能包含有用的信息。
解析器可以被视为有限状态机,它逐字前进,并根据上下文更改其状态。从上面的标记结构描述可以清楚地看出,最初解析器在任何标记之外(让我们称此状态为“空白”)。然后,在遇到开口角括号“<”后,我们进入一个开口标记(“InsideTagOpen”状态),该状态持续到关闭角括号“>”。字符“</”的组合表明我们处于结束标记(“InsideTagClose”状态),等等。在解析器实现部分将考虑其他状态,
在状态之间切换时,赫兹股票量化可以从文档中的当前位置选择结构化信息,因为我们知道状态的含义。例如,如果当前位置在开始标记内,则可以选择标记名称作为最后一个“<”和后续空格或“>”之间的一行(取决于标记是否包含属性)。解析器将提取数据并创建某个 DomElement 类的对象。除了名称、属性和内容之外,还将根据标记嵌套结构保留对象的层次结构。换句话说,每个对象都有一个父对象(描述整个文档的根元素除外)和一个可选的子对象数组。
解析器将输出完整的对象树,其中一个对象将对应于源文档中的一个标记。
CSS选择器根据对象在层次结构中的参数和位置描述对象条件选择的标准符号。选择器的完整列表非常广泛,赫兹股票量化将为其中一些提供支持,这些支持包括在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) — 对象在其父节点的子节点列表中具有指定的位置号;
:nth-last-child(n) — 对象在其父节点的子节点列表中具有指定的位置编号,且编号相反;
单个选择器可以由与属性相关的条件进行补充:
[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”有一个共同的父对象,并在它之后的任意距离;
到目前为止,赫兹股票量化一直在研究纯理论,让我们来看看上述想法是如何在实践中发挥作用的。
任何现代Web浏览器都允许查看当前打开页面的HTML,例如,在 Chrome 中,您可以从上下文菜单运行“查看页面源代码”命令或打开开发人员窗口(开发人员工具,Ctrl+Shift+I)。开发人员窗口有控制台选项卡,在该选项卡中,我们可以尝试使用CSS选择器查找元素。要应用选择器,只需从控制台调用 document.querySelectorAll 函数(它包含在所有浏览器的软件API中)。
结果,我们将收到一个“div”元素(标记)列表,其中指定了“widgetHeader”类。我决定在查看源页面代码之后使用这个选择器,很明显,基于源页面代码,论坛主题是以这种方式设计的。
MQL5 网页和使用 CSS 选择器选择 HTML 元素的结果
同样,您应该分析所需站点的HTML代码,找出感兴趣的元素,并选择适当的CSS选择器。“开发人员”窗口具有“元素”(或类似的)选项卡,您可以在其中选择文档中的任何标记(此标记将突出显示),并为该标记找到适当的CSS选择器。这样,您将逐渐练习使用选择器,并学习手动创建选择器链。此外,我们将探讨如何为特定网页选择适当的选择器。
设计
让我们在全局范围内查看我们可能需要的类。最初的HTML文本处理将由 HtmlParser 类执行,这个类将扫描文本中的标记字符“<”、“/”、“>”和其他一些字符,并将根据上述有限状态机规则创建 DomElement 类对象:将为每个空标记或一对打开和关闭标记创建一个对象。开始标记可能有属性,赫兹股票量化需要在当前 DomElement 对象中读取和保存这些属性,这将由 AttributeParser类执行,该类也将按照有限状态机的原理运行。
解析器将创建 DomElement 对象,同时考虑到与标记嵌套顺序相同的层次结构。例如,如果文本包含“div”标记,其中放置了多个段落(这意味着存在“p”标记),则这些段落将转换为描述“div”的对象的子对象。
初始根对象将包含整个文档。与浏览器(提供 document.querySelectorAll方法)类似,我们应该在 DomElement 中提供一个方法,用于请求与传递的 CSS 选择器对应的对象。选择器也应该预先分析,并从字符串表示形式转换为对象:一个选择器组件将存储在 SubSelector 器类中,整个简单选择器将存储在 SubSelectorArray 中。
一旦解析器操作的结果是有了准备好的 DOM 树,就可以从根 DomElement对象(或任何其他对象)请求与选择器参数匹配的所有子元素。所有选定的元素都将被放置在可迭代的 DomItator列表中。为了简单起见,让我们将列表实现为 DomElement的子级,其中使用子节点数组存储找到的元素。
具有特定站点或HTML文件处理规则和算法执行结果的设置可以方便地存储在一个类中,该类结合了映射属性(即根据适当属性的名称提供对值的访问)和数组属性(即通过索引访问元素)。让我们称这个类为 IndexMap。
赫兹股票量化提供将索引映射彼此嵌套的功能:当从网页收集表格数据时,我们得到一个包含列列表的行列表。对于这两种数据类型,我们都可以保存源元素的名称。这可以是特别有用的,在一些情况下的元素是将在源文件中(这一点很可能经常在搜索索引的情况下),这是一盘简单的信息数据是缺失的。作为一个额外的好处,让我们“训练” IndexMap 以序列化为多行文本,包括 CSV 格式。此功能在将HTML页转换为表格数据时很有用。如果需要,可以在保留主要功能的同时用自己的 IndexMap 类替换。
下面的UML图显示了所描述的类。

编辑切换为居中
在MQL中实现CSS选择器的类的UML图
实现
HtmlParser
在HTMLParser类中,赫兹股票量化描述了扫描源文本和生成对象树以及安排有限状态机算法所需的变量。
文本中的当前位置存储在“offset”变量中。生成的树根和当前对象(扫描在此对象上下文中执行)由“根(root)”和“光标(cursor)”指针表示。稍后将考虑它们的 DomElement 类型。根据HTML规范,标记列表可能是空的,将被加载到“empties”映射中(该映射在构造函数中初始化,请参见下文)。最后,我们为有限状态机状态的描述提供了“状态(state)”变量。变量是 StateBit 类型的枚举。