问题返回 <a> link 不包括 mailto links

Issue returning <a> link excluding mailto links

我正在使用 HTML Agility Pack 来定义 returns 网页链接的功能。问题是它 returns 所有链接包括 mailto。

稍后在程序中,当处理链接时,mailto 链接断开。我试图消除它们在 _links

的函数输出列表中的包含

我的函数定义为:

var linkNodes = _htmlDocument.Value.DocumentNode.SelectNodes("//a");
if (linkNodes == null)
    return Enumerable.Empty<Link>();

var links = new List<Link>();
foreach (var linkNode in linkNodes)
{
    var href = linkNode.GetAttributeValue("href", "#");
    if (!Uri.IsWellFormedUriString(href, UriKind.RelativeOrAbsolute))
        continue;

    var url = href.ToAbsoluteUri(Url);
    var follow = linkNode.GetAttributeValue("rel", "follow");

    links.Add(new Link(Url, url, linkNode.InnerText, follow));
}

_links = links;

我的 LINQ 几乎可以工作(在摆脱 mailto 方面工作,但返回的是字符串而不是与所用战士匹配的节点):

var linkNodes = _htmlDocument.Value.DocumentNode.SelectNodes("//a[@href]")
                        .Select(a => a.Attributes["href"].Value)
                        .Where(href => !href.StartsWith("mailto:")) // skip emails, find only url links
                        .ToList();

关于 Select 和地点:

根据MSDN

Linq Select 将根据该集合中的项目将您的集合转换为新形式。这是一个简单的例子。

IEnumerable<int> collectionOfInt = Enumerable.Range(0, 10);
IEnumerable<string> collectionOfString = collectionOfInt.Select(i => i.ToString());
IEnumerable<int> lengthOfStrings = collectionOfString.Select(str => str.Length);

首先,您有一个从 0 到 9 的 int 集合。如您所见,Select return 是一个新的字符串集合,但基于 collectionOfInt 的项目所以你有来自 "0","1",...,"9" 的字符串。请注意,Select 的执行被推迟,因此必须使用 ToList 来实际执行该查询。

collectionOfString 上执行 Select 时也是如此。如您所见,您松开了实际的字符串,您将得到这些字符串的长度 (1,1,...,1)。

现在你的 Linq

var linkNodes = _htmlDocument.Value.DocumentNode.SelectNodes("//a[@href]")
                    .Select(a => a.Attributes["href"].Value)
                    .Where(href => !href.StartsWith("mailto:"))
                    .ToList();

同样的事情发生在这里。你有一个节点集合,但 Select(a => a.Attributes["href"].Value) 实际上会将你的节点变成字符串集合,你将失去实际的节点。

.Select(a => a.Attributes["href"].Value) // Changes main collection into values
.Where(href => !href.StartsWith("mailto:")) // searches on values not main collection thus returns values again.

所以你必须把它全部放在Where部分。因为 Where 不会更改集合类型。它仅在该元素的条件为真时才从集合中选择元素。

根据我的解释 href 在之前的查询中是 a.Attributes["href"].Value。因此,为了不丢失原始元素,只需将 a.Attributes["href"].Value 包裹在 href 内,这样您将拥有

.Where(node => !node.Attributes["href"].Value.StartsWith("mailto:")) // searches on nodes collection thus returns nodes

关于Null异常部分:

Where Linq 查询不会搜索空项。因此,只要先前查询中的 hrefa.Attributes["href"].Value 为空,它就会跳过该项目而不选择它。

Select 内联到 Where 后,现在只检查 node 的可空性而不检查函数 !node.Attributes["href"].Value.StartsWith("mailto:").

的执行

基本上是因为 Value 有可能为 null,你会在 StartsWith("mailto:") 上遇到无法处理 null 的异常。

在 C#6 中,您可以通过混合 null conditional and Null-coalescing 运算符来解决此问题。

htmlDocument.Value.DocumentNode.SelectNodes("//a[@href]")
    .Where(node => !node.Attributes["href"].Value?.StartsWith("mailto:") ?? false).ToList();

如果Value?.的值为null,则不会继续执行StartsWith("mailto:"),而是直接returns null。

因为 ?. 的 return 类型是 nullable bool 那么 ?? false 将 return false 当运算符的左侧为空时。