【问题标题】:Selecting Table Elements Between Two Known Elements Using XPath For Agility Pack使用 XPath For Agility Pack 在两个已知元素之间选择表元素
【发布时间】:2014-03-20 08:07:54
【问题描述】:

我正在尝试从此布局中的表格中选择元素:

<tbody>
<tr class="header">
      <th colspan="4">Tier 1</th>
 </tr>
 <tr>
          <td><a>First Thing</a></td>
          <td><a>Second Thing</a></td>
          <td><a>Third Thing</a></td>
          <td></td>
 </tr>
 <tr>
          <td><a>Fourth Thing</a></td>
          <td><a>Fifth Thing</a></td>
          <td><a>Sixth Thing</a></td>
          <td></td>
      </tr>


<tr class="header">
      <th colspan="4">Tier 2</th>
 </tr>
 <tr>
          <td><a>First Thing</a></td>
          <td><a>Second Thing</a></td>
          <td><a>Third Thing</a></td>
          <td></td>
 </tr>
 <tr>
          <td><a>Fourth Thing</a></td>
          <td><a>Fifth Thing</a></td>
          <td><a>Sixth Thing</a></td>
          <td></td>
      </tr>

我想选择“tr class=header”标签之间的所有值。我需要这样做 5 次(实际表上有 6 层,这里没有列出,因为它太长了),最后我需要从最后的标题中选择到表的底部。
我应该指定,我在 C# MVC 中使用 Agility Pack,所以 xpaths 似乎是要走的路。
到目前为止,我已经能够使用“//tr[@class='header']//th”来隔离标头。
主要问题似乎是我想要的节点是彼此的兄弟节点,而不是使遍历更容易的子节点。
最终游戏是我想在我的数据结构中为所有第 1 层元素的值赋予 1,所有第 2 层元素的值为 2,等等,以供以后比较。

【问题讨论】:

  • 解析这个 html 的预期结果是什么? tr class="header" 之间的所有值是什么意思?只有两个th 节点。你想要他们的价值观吗?
  • 制作列表
  • List&lt;T&gt; 只有一个泛型参数
  • 这实际上重复了 6 次。我只是不想一遍又一遍地复制整个事情。有 6 层。
  • 呃,对不起,列出

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


【解决方案1】:

首先 - 您需要扩展方法来按层拆分行:

public static IEnumerable<IEnumerable<T>> SplitBy<T>(
    this IEnumerable<T> source, Func<T, bool> separator)
{
    List<T> batch = new List<T>();

    using (var iterator = source.GetEnumerator())
    {
        while (iterator.MoveNext())
        {
            if (separator(iterator.Current) && batch.Any())
            {
                yield return batch;
                batch = new List<T>();
            }

            batch.Add(iterator.Current);
        }
    }

    if (batch.Any())
        yield return batch;
}

现在第一步将是查询层(每个层将包含多个tr 节点):

HtmlDocument doc = new HtmlDocument();
doc.Load(path_to_html);

var tiers = doc.DocumentNode.SelectNodes("//tr")
               .SplitBy(tr => tr.HasAttributes &&  
                              tr.Attributes["class"].Value == "header");

第二步是从每一层提取数据

var result = from t in tiers
             let tier = t.First().SelectSingleNode("th").InnerText
             from a in t.Skip(1).SelectMany(tr => tr.SelectNodes("td/a"))
             select new {
                 Tier = tier,
                 Value = a.InnerText
             };

结果是

[
  { Tier: "Tier 1", Value: "First Thing" },
  { Tier: "Tier 1", Value: "Second Thing" },
  { Tier: "Tier 1", Value: "Third Thing" },
  { Tier: "Tier 1", Value: "Fourth Thing" },
  { Tier: "Tier 1", Value: "Fifth Thing" },
  { Tier: "Tier 1", Value: "Sixth Thing" },
  { Tier: "Tier 2", Value: "First Thing" },
  { Tier: "Tier 2", Value: "Second Thing" },
  { Tier: "Tier 2", Value: "Third Thing" },
  { Tier: "Tier 2", Value: "Fourth Thing" },
  { Tier: "Tier 2", Value: "Fifth Thing" },
  { Tier: "Tier 2", Value: "Sixth Thing" }
]

【讨论】:

    猜你喜欢
    • 2012-02-10
    • 2012-06-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-01-01
    • 2011-09-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多