【问题标题】:Selecting text from some elements inside a div and ignore other elements. HTML Agility Pack从 div 内的某些元素中选择文本并忽略其他元素。 HTML 敏捷包
【发布时间】:2020-06-20 22:32:24
【问题描述】:

我正在尝试为新闻网站构建网络抓取工具。我在选择相关文本时遇到问题,因为文本分为多个不同的元素。我正在使用 HTML Agility Pack,我尝试从主 div 中选择文本( //text() ),但是当我这样做时,我得到了很多我不想要的垃圾文本,比如 javascript 代码。 如何从某些嵌套元素中选择文本并忽略其他元素?

<div class="texto_container paywall">
  Some text I want
  <a href="https://www.sabado.pt/sabermais/ana-gomes" target="_blank" rel="noopener">
    Text I want
  </a> 
    sample of text I want
  <em>
    another text i want
  </em>
  <aside class="multimediaEmbed contentRight">
      A lot of nested elements here with some text I dont want
  </aside>
  <div class="inContent">
      A lot of nested elements here with some text I don't want
  </div>
  
  Back to the text I want!
  
  <twitter-widget class="twitter-tweet twitter-tweet-rendered" id="twitter-widget-0" >
    Don't want any of this text located in nested elements!
  </twitter-widget>
  
  <p>
    Final revelant text i want to collect!
  </p> 
  
</div>

编辑

我尝试使用 XPath 排除我不想要的标签,但我仍然从结果中的这些标签中获取文本节点。

var parse_me = htmlDoc.DocumentNode.SelectNodes("//div[@class='texto_container paywall']//text()[not(parent::aside)][not(parent::div[@class='inContent'])][not(parent::twitter-widget)]");

我认为这段代码不起作用,因为在标签上我不想包含文本父节点不是“主”标签,因为它位于许多嵌套标签中。

编辑

经过一些思考和研究,我通过使用 ancestor:: 而不是 parent:: 解决了之前的问题,并且我去掉了一些预期的文本。 但是我仍然无法摆脱 twitter-widget 文本,因为即使使用从 Google Chrome 检查元素工具复制的 XPath,它也总是返回一个空节点。

var Twitter_Node = htmlDoc.DocumentNode.SelectSingleNode("//*[@id='twitter - widget - 0']");

这将返回为 null。这怎么可能? XPath 是从 Chrome 复制而来的。

【问题讨论】:

  • HtmlAgilityPack 使用 XPath 语法 - 查看您的示例 html,您需要组合来自多个选择器的文本。这个网站上有很多questions and examples可以帮助你。
  • 删除 Xpath 中的空格://*[@id='twitter-widget-0']。替代方案://*[starts-with(./@id,"twitter")]

标签: c# html xpath replace html-agility-pack


【解决方案1】:

您可以尝试从特定标签中排除文本:

//body//text()[not(parent::aside)][not(parent::div[@class="inContent"])][not(parent::twitter-widget)]

你可以使用 concat 但它更复杂,因为你必须知道“链”中每个标签的数量和位置:

concat(//body//div[@class="texto_container paywall"]/text()[1],//body//a[@href]/text(),//body//div[@class="texto_container paywall"]/text()[2],//body//em/text(),//body//div[@class="texto_container paywall"]/text()[5],//body//p/text())

【讨论】:

  • 第一个解决方案不行,我在帖子里加了我用的代码。
  • 第一个解决方案使用提供的代码。但是,是的,因为你有嵌套元素,所以最好使用祖先而不是父级。
【解决方案2】:

我正在使用ScrapySharp nuget,它在下面的示例中添加,(HtmlAgilityPack 可能提供与它相同的功能,我只是从几年前就习惯了 ScrapySharp)

您可以简单地按时提取所有不需要的文本,然后用空字符串替换它们在主 div 文本中出现的位置,从最终结果中删除它们。

    var doc = new HtmlDocument();
    doc.Load(@"C:\Desktop\z.html"); //I created an html with your sample HTML set as the html body

    List<string> textsIWant = new List<string>();

    var textsIdoNotWant = new List<string>();
    //text I do not want
    var aside = doc.DocumentNode.CssSelect(".multimediaEmbed.contentRight").FirstOrDefault();
    if (aside != null)
    {
        textsIdoNotWant.Add(aside.InnerText);
    }

    var inContent = doc.DocumentNode.CssSelect(".inContent").FirstOrDefault();
    if (inContent != null)
    {
        textsIdoNotWant.Add(inContent.InnerText);
    }

    var twitterWidget = doc.DocumentNode.CssSelect("#twitter-widget-0").FirstOrDefault();
    if (twitterWidget != null)
    {
        textsIdoNotWant.Add(twitterWidget.InnerText);
    }

    var div = doc.DocumentNode.CssSelect(".texto_container.paywall").FirstOrDefault();
    if (div != null)
    {
        var text = div.InnerText;
        foreach (var textIDoNotWant in textsIdoNotWant)
        {
            text = text.Replace(textIDoNotWant, string.Empty);
        }

        textsIWant.Add(text);
    }

    foreach (var text in textsIWant)
        Console.WriteLine(text);

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-05-09
    • 1970-01-01
    • 2018-05-01
    • 2011-03-04
    • 2011-02-26
    • 1970-01-01
    • 2018-06-15
    相关资源
    最近更新 更多