项目

AngleSharp Html 解析器配置

HTML 解析器附带了一些选项,这些选项可能有助于覆盖特定场景。

IsNotConsumingCharacterReferences

如果启用了 IsNotConsumingCharacterReferences,那么每个 & 符号都将被视为普通的 &。对于序列化,这仍然意味着 & 会被表示为 &,但我们知道可以简单地用 & 替换 &

或者,我们可以使用自己的格式化器。

来看一个例子:

var formatter = new MyFormatter();
var parser = new HtmlParser(new HtmlParserOptions
{
    IsNotConsumingCharacterReferences = true,
});
var html = "<html><head></head><body><p>&amp;foo</p></body></html>";
IDocument document = parser.ParseDocument(html);
Console.WriteLine(document.DocumentElement.ToHtml(formatter));

在这里我们使用了一个自定义格式器,如:

class MyFormatter : IMarkupFormatter
{
    public string CloseTag(IElement element, bool selfClosing) => HtmlMarkupFormatter.Instance.CloseTag(element, selfClosing);
    public string Comment(IComment comment) => HtmlMarkupFormatter.Instance.Comment(comment);
    public string Doctype(IDocumentType doctype) => HtmlMarkupFormatter.Instance.Doctype(doctype);
    public string LiteralText(ICharacterData text) => HtmlMarkupFormatter.Instance.LiteralText(text);
    public string OpenTag(IElement element, bool selfClosing) => HtmlMarkupFormatter.Instance.OpenTag(element, selfClosing);
    public string Processing(IProcessingInstruction processing) => HtmlMarkupFormatter.Instance.Processing(processing);
    public string Text(ICharacterData text) => HtmlMarkupFormatter.Instance.LiteralText(text);
}

现在输出如下:

<html>
  <head></head>
  <body>
    <p>&foo</p>
  </body>
</html>

相比之下,将 IsNotConsumingCharacterReferences 设置为 false 的结果是:

<html>
  <head></head>
  <body>
    <p>&amp;foo</p>
  </body>
</html>

总结一下:

此选项影响了 & 字符的解析。当设为 false(默认)时,我们开始消费字符引用,这是规范行为,但可能会“吃掉”你希望稍后处理的信息。设为 true 时,我们从不消费 & 字符,而是将其发射到 DOM(就像在规范兼容模式下看到 &amp; 一样)。

对于序列化(例如,使用 InnerHtml 或更明确地通过 ToHtml),然而,我们将任何见到的 & 解释为 &amp;(这样它就可以循环使用,且符合序列化规范)。所以在上面的例子中,我们使用自定义格式器来直接使用字符节点数据。

IsKeepingSourceReferences

IsKeepingSourceReferences 选项决定是否保持文本源的位置信息或引用以便序列化。如果不保持源引用,在序列化时,我们无法获取文档中任何选定元素的源引用响应。

这个选项的一个示例是:

var parser = new HtmlParser(new HtmlParserOptions
{
    IsKeepingSourceReferences = false
});
var html = "<html><head></head><body><p>foo</p></body></html>";
IDocument document = parser.ParseDocument(html);
Console.WriteLine(document.QuerySelector("a").SourceReference?.Position.ToString());

结果将是无,SourceReference 会因解析器给定而为 null。将该选项设为 true 后,我们将按预期获得位置信息:

Ln 1, Col 26, Pos 26

我们也可以格式化 HTML 使其更美观,并得到更漂亮的输出,添加代码:

document = parser.ParseDocument(document.Prettify());

然后我们将得到 Ln 4, Col 3, Pos 33

IsSupportingProcessingInstructions

IsSupportingProcessingInstructions 选项导致解析器在遇到 <? ... > 令牌时发出 ProcessingInstruction 节点,这些是用于向应用程序传递指令的 SGML 和 XML 节点类型。

SGML PI 由 <?> 包围,而 XML PI 由 <??> 包围。

以以下情况为例:

var parser = new HtmlParser(new HtmlParserOptions
{
    IsSupportingProcessingInstructions = true
});
var html = "<html><head></head><body><p><?xml version=\"1.0\" encoding=\"UTF - 8\" ?></p></body></html>";
IDocument document = parser.ParseDocument(html);
Console.WriteLine(document.DocumentElement.ToHtml());

由于启用了 PI 支持选项,这将给出 <html><head></head><body><p><??xml version=\"1.0\" encoding=\"UTF - 8\" ?></p></body></html> 的响应。 否则,我们会得到一个注释节点包围发出的 PI 节点:"<html><head></head><body><p><!--<?xml version=\"1.0\" encoding=\"UTF - 8\" ?>--></p></body></html>"

OnCreated

OnCreated 功能基于新元素被创建和在文档中的位置执行动作,该动作随后由格式器相应地解析。具体来说,可以在元素被创建时重新定位、更改其属性并执行其他操作。

例如,我们可以执行以下操作:

var parser = new HtmlParser(new HtmlParserOptions
{
    OnCreated = (element, position) =>
    {
        if (25 <= position.Index && position.Index < 35)
            element.TextContent += " bar";
    }
});
var html = "<html><head></head><body><p>foo</p></body></html>";
IDocument document = parser.ParseDocument(html);
Console.WriteLine(document.DocumentElement.ToHtml());

这将根据位置范围 [25, 35) 和该范围内的 <p> 元素格式化输出 <html><head></head><body><p>foo bar</p></body></html>

一般来说,对于足够大的文本,不期望在一次解析中传递这个选项,因为它需要更多时间来处理。

IsStrictMode

JavaScript 的 ES5 中的 "严格模式" 指令在此处由 IsStrictMode 选项表示。简而言之,将此选项设置为 true$HtmlParserOptions$ 中通知解析器,它包含的任何 JS 代码都将应用 "严格模式"。

以下代码将给我们一个 HtmlParseException

var parser = new HtmlParser(new HtmlParserOptions
{
    IsStrictMode = true
});
var html = "<html><head></head><body><script>x = 0;</script><p>foo</p></body></html>";
IDocument document = parser.ParseDocument(html);
Console.WriteLine(document.DocumentElement.ToHtml());

在这种情况下,因为我们启用了严格模式,所以我们必须声明 xlet x,例如。

默认情况下,严格模式为 false,我们将按预期获得响应。

IsEmbeddedIsNotSupportingFramesIsScripting

IsEmbedded 选项允许您使用 HTML 解析器,假设内容已经被嵌入到一个有效的 HTML 文档中。这用于确定是否需要文档类型声明。在嵌入模式下,即使缺少文档类型声明也不会进入怪异模式。

同样,IsNotSupportingFrames 选项可用于表现得好像不允许框架一样。当实际遇到 frameset 或类似标签时,像 noframes 这样的标签会被不同地解释(即,就好像它们不存在一样)。目前,大多数浏览器仍然支持框架——尽管它们不应再被使用。注意,框架不包括 <iframe>,此标志不受影响。

IsScripting 选项模拟浏览器在解析 innerHTML 时的行为。没有脚本的情况下,noscriptscript 标签会互换位置。在这里,noscript 标签将被评估(而不是被忽略)。此外,script 标签的内容将被忽略。启用 IsScripting 以从解析的角度来看页面,就像 JavaScript 启用的浏览器所做的那样。

备注:开启 IsScripting 选项和集成 JavaScript 引擎(例如,来自 AngleSharp.Js)不是一回事。AngleSharp 实际上会在发现已包含 JavaScript 引擎时自动开启 IsScripting

IsAcceptingCustomElementsEverywhere 选项

IsAcceptingCustomElementsEverywhere 选项允许在通常禁止使用自定义元素(如 my-element)的位置使用它们。

考虑以下 HTML:

<html>
  <head>
    <my-element foo="bar"></my-element>
  </head>
</html>

实际上,DOM 会将 my-element 节点置于 body 中。看起来原始 HTML 被重排成了这样:

<html>
  <head> </head>
  <body>
    <my-element foo="bar"></my-element>
  </body>
</html>

如果你想允许自定义元素出现在任何地方,只需提供该标志:

var parser = new HtmlParser(new HtmlParserOptions
{
    IsAcceptingCustomElementsEverywhere = true
});
var html = @"<html><head><my-element foo=""bar""></my-element></head></html>";
IDocument document = parser.ParseDocument(html);
Console.WriteLine(document.DocumentElement.ToHtml());

这将使 my-element 保持在头部。但请记住,自定义元素的内容,即子元素,需要遵循外部上下文的规则。

IsPreservingAttributeNames 选项

此选项允许你保留原始属性名称。通常,属性名称会被规范化为只使用小写字母。

以下是非标准的 Angular 模板 HTML:

<div *ngIf="condition">当条件为真时渲染的内容。</div>

如果不使用此选项,HTML DOM 看起来会像是原始源码为:

<div *ngif="condition">当条件为真时渲染的内容。</div>

相比之下,你可以这样使用这个选项:

var parser = new HtmlParser(new HtmlParserOptions
{
    // 保留属性名原样
    IsPreservingAttributeNames = true
});
var html = @"<div *ngIf=""condition"">当条件为真时渲染的内容。</div>";
IDocument document = parser.ParseDocument(html);
Console.WriteLine(document.DocumentElement.ToHtml());

这将保持属性原样,即为 *ngIf 而不是 *ngif

在本文档中