【问题标题】:Issue returning <a> link excluding mailto links问题返回 <a> 链接,不包括 mailto 链接
【发布时间】:2015-10-19 15:47:05
【问题描述】:

我正在使用 HTML Agility Pack 来定义一个返回网页链接的函数。问题是它返回所有链接,包括 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();

【问题讨论】:

  • 尝试将您的 linq 更改为 htmlDocument.Value.DocumentNode.SelectNodes("//a[@href]").Where(a =&gt; !a.Attributes["href"].Value.StartsWith("mailto:")).ToList();
  • 在其中一次迭代中出现错误:{"Value cannot be null.\r\nParameter name: source"}
  • 如果您有 c#6,请尝试使用空条件运算符。 htmlDocument.Value.DocumentNode.SelectNodes("//a[@href]").Where(a =&gt; !a.Attributes["href"].Value?.StartsWith("mailto:") ?? false).ToList();
  • 我想我看到了,但是解释我哪里出错了会有所帮助,我可以标记一个已接受的答案

标签: c# linq html-agility-pack linq-to-objects


【解决方案1】:

关于选择和位置:

根据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 返回一个新的字符串集合,但基于 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 =&gt; 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 不会更改集合类型。只有当某个元素的条件为真时,它才会从集合中选择元素。

根据我在上一个查询中的解释hrefa.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 conditionalNull-coalescing 运算符来解决此问题。

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

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

因为?. 的返回类型是nullable bool,所以当运算符左侧为空时,?? false 将返回 false。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-07-08
    • 2018-10-20
    • 1970-01-01
    • 2018-01-16
    • 1970-01-01
    • 1970-01-01
    • 2022-11-06
    相关资源
    最近更新 更多