html - 使用CSS选择器从流解析器(例如SAX流)收集HTML元素

标签 html css css-selectors sax

如何解析CSS(CSS3)选择器并使用它(以类似于jQuery的方式)收集HTML元素而不是从DOM(从树结构)而是从流(例如SAX),即使用基于顺序访问事件的解析器收集HTML元素?

顺便说一句,是否有需要访问DOM的CSS选择器(或其组合)(Wikipedia SAX页面说XPath选择器“需要能够随时访问已解析的XML树中的任何节点”)?

我对实现选择器组合器最感兴趣,例如'A B'后代选择器。

我更喜欢描述算法的解决方案,或者在Perl中(对于HTML::Zoom)。

最佳答案

我会用正则表达式来做到这一点。

首先,将选择器转换为正则表达式,该正则表达式与代表给定解析器堆栈状态的简单的开头标签的从上到下列表匹配。为了说明,这里有一些简单的选择器及其对应的正则表达式:

  • A变成/<A[^>]*>$/
  • A#someid变成/<A[^>]*id="someid"[^>]*>$/
  • A.someclass变成/<A[^>]*class="[^"]*(?<= |")someclass(?= |")[^"]*"[^>]*>$/
  • A > B变成/<A[^>]*><B[^>]*>$/
  • A B变成/<A[^>]*>(?:<[^>]*>)*<B[^>]*>$/

  • 等等。请注意,所有正则表达式均以$结尾,但不以^开头;这与CSS选择器不必从文档根部进行匹配的方式相对应。还要注意,在类匹配代码中有一些先行查找和提前查找的内容,这是必要的,这样当您想要截然不同的类“someclass”时,就不会意外地与“someclass-super-duper”进行匹配。

    如果您需要更多示例,请告诉我。

    构造选择器正则表达式后,就可以开始解析了。解析时,维护一堆当前适用的标签;每当您下降或上升时都更新此堆栈。要检查选择器是否匹配,请将该堆栈转换为可以匹配正则表达式的标记列表。例如,考虑以下文档:
    <x><a>Stuff goes here</a><y id="boo"><z class="bar">Content here</z></y></x>
    

    输入每个元素时,堆栈状态字符串将依次经过以下值:
  • <x>
  • <x><a>
  • <x><y id="boo">
  • <x><y id="boo"><z class="bar">

  • 匹配过程很简单:只要解析器进入一个新元素,就更新状态字符串并检查其是否与选择器regex相匹配。如果正则表达式匹配,则选择器匹配该元素!

    要注意的问题:
  • 属性内的双引号。要解决此问题,请在创建正则表达式时将html实体编码应用于属性值,在创建堆栈状态字符串时将html实体编码应用于属性值。
  • 属性顺序。同时构建正则表达式和状态字符串时,请对属性使用一些规范的顺序(最简单的字母顺序)。否则,您可能会发现,当解析器进入a#someid.someclass时,期望<a id="someid" class="someclass">的选择器<a class="someclass" id="someid">的正则表达式失败。
  • 区分大小写。根据HTML spec,class和id属性区分大小写(请注意相应部分的'CS'标记)。因此,您必须使用区分大小写的正则表达式匹配。但是,在HTML中,元素名称不区分大小写,尽管它们在XML中。如果要匹配类似HTML的不区分大小写的元素名称,则在选择器regex和状态堆栈字符串中将元素名称规范化为大写或小写。
  • 处理涉及到是否存在元素同级的选择器模式(即A:first-childA + B)需要额外的魔术。您可以通过在标签上添加一个特殊属性来实现这些目的,该属性应包含紧接其前的标签名称,如果该标签是第一个子标签,则可以添加“”。还有一个通用的同级选择器A ~ B;我不太确定该如何处理。

  • 编辑:如果您不喜欢正则表达式黑客,仍然可以使用此方法来解决问题,仅使用您自己的状态机而不是正则表达式引擎。具体来说,CSS选择器可以实现为nondeterministic finite state machine,这是一个令人生畏的术语,但实际上意味着以下含义:
  • 从任何给定状态
  • 可能有多个可能的过渡
  • 机器尝试其中的一种,如果仍无法解决,则回溯并尝试另一种
  • 实现此目的最简单的方法是为机器保留一个堆栈,只要您遵循路径,就将其插入堆栈,并在需要回溯时将其弹出。归结为您进行深度优先搜索时所用的相同方法。

  • 几乎所有令人赞叹的正则表达式背后的 secret 都在于它使用这种状态机的风格。

    关于html - 使用CSS选择器从流解析器(例如SAX流)收集HTML元素,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4656975/

    相关文章:

    javascript - 在转换内容的同时调整容器的大小

    html - 在单词之间的空白处换行后更改字体大小

    css - 如何仅设计导航的顶层样式?

    css - SVG 元素之间的奇数间距

    javascript - 根据颜色输入类型更改形状的颜色 javesctipt

    html - 视频背景显示在网页内容之上

    css - 如果在显示表内,预宽度太宽

    手机 : Elements in overflow scroll element ignore border radius

    css - 将样式应用于输入类型文本焦点上的提交按钮

    html - 如何删除最后一个 <i> 的右边距?