0

我有一个我似乎无法在这里解决的问题。假设我有一些像 beneth 这样的 html,我想解析。所有这些 html 都在页面上的一个列表中。并且名称会像我写的示例一样重复。

<li class = "seperator"> a date </li>
<li class = "lol"> some text </li>
<li class = "lol"> some text </li>
<li class = "lol"> some text </li>

<li class = "seperator"> a new date </li>
<li class = "lol"> some text </li>


<li class = "seperator"> a nother new date </li>
<li class = "lol"> some text </li>
<li class = "lol"> some text </li>

我确实设法使用 htmlagility pack 来单独解析每个 li 对象,并且几乎按照我想要的方式格式化它。我的打印 atm 看起来像这样:

"a date"    "some text"
"some text"
"some text"
"some text"

"a new date"  "some text"

"a nother new date "    "some text"
"some text"
"some text"

我想要达到的目标:

"a date"    "some text"
"a date"    "some text"
"a date"    "some text"
"a date"    "some text"

"a new date"    "some text"

"a nother new date "    "some text"
"a nother new date "    "some text"
"a nother new date "    "some text"

但问题是在每个分隔符下,每个 lol 对象的计数可能会有所不同。所以有一天,网页可能在日期 1 下有一个 lol 对象,第二天它可能有 10 个 lol 对象。因此,如果有一种智能/简单的方法可以以某种方式计算分隔符之间的 lol 对象的数量,我会感到很痛苦。或者如果有另一种方法来解决这个问题?例如在 htmlagilitypack 中。是的,我需要在每个 lol 对象前面加上正确的日期,而不仅仅是在第一个对象前面。如果分隔符类在最后一个 lol 对象下方结束,这将是小菜一碟,但遗憾的是事实并非如此......我认为我不需要在这里粘贴我的代码,但基本上我所做的是解析页面,提取分隔符和 lol 对象并将它们添加到列表中,我将它们拆分为分隔符和 lol 对象。

4

1 回答 1

0

这是计划,选择所有seperator元素,然后找到所有具有所需类的连续兄弟元素。

不幸的是,在当前版本的 HTML Agility Pack 中没有简单的方法来获取同级的集合,您只能访问(一个)下一个同级。很难使用 LINQ 从链接结构中收集数据。而且由于 HTML 中没有真正的层次结构,这将是一个挑战。

如果您有可用的 XPath,则可以使用该following-sibling轴来获取所有以下同级元素以及TakeWhile()执行以下操作的方法:

var htmlStr = @"<li class = ""seperator""> a date </li>
<li class = ""lol""> some text </li>
<li class = ""lol""> some text </li>
<li class = ""lol""> some text </li>

<li class = ""seperator""> a new date </li>
<li class = ""lol""> some text </li>


<li class = ""seperator""> a nother new date </li>
<li class = ""lol""> some text </li>
<li class = ""lol""> some text </li>";

var doc = new HtmlDocument();
doc.LoadHtml(htmlStr);
var data =
    from li in doc.DocumentNode.SelectNodes("li[@class='seperator']")
    select new
    {
        Separator = li.InnerText,
        Content = li.SelectNodes("following-sibling::li")
            .TakeWhile(sli => sli.Attributes["class"].Value == "lol")
            .Select(sli => sli.InnerText)
            .ToList(),
    };

否则,如果您没有可用的 XPath,则可以使用以下任何链接结构创建可枚举:

public static class Extensions
{
    public static IEnumerable<TSource> ToLinkedEnumerable<TSource>(
        this TSource source,
        Func<TSource, TSource> nextSelector,
        Func<TSource, bool> predicate)
    {
        for (TSource current = nextSelector(source);
                predicate(current);
                current = nextSelector(current))
            yield return current;
    }

    public static IEnumerable<TSource> ToLinkedEnumerable<TSource>(
        this TSource source, Func<TSource, TSource> nextSelector)
        where TSource : class
    {
        return ToLinkedEnumerable(source, nextSelector, src => src != null);
    }
}

然后你的查询现在变成这样:

var data =
    from li in doc.DocumentNode.Elements("li")
    where li.Attributes["class"].Value == "seperator"
    select new
    {
        Separator = li.InnerText,
        Content = li.ToLinkedEnumerable(sli => sli.NextSibling)
            .Where(sli => sli.Name == "li")
            .TakeWhile(sli => sli.Attributes["class"].Value == "lol")
            .Select(sli => sli.InnerText)
            .ToList(),
    };
于 2012-10-07T03:12:58.450 回答