从动态列 HtmlAgility 包中选择页面数据

Selecting page data from dynamic columns HtmlAgility pack

我正在使用 HtmlAgility 包从这个 url 中抓取数据: http://www.myfitnesspal.com/food/diary/chuckgross

基本上,我真正需要的唯一数据是卡路里、蛋白质、脂肪和碳水化合物。问题是这些列是用户排序的(用户甚至可以不显示其中的一些!)。

我正在尝试 return 那个页面数据到 class:

public class NutritionRecord
    {
        public string Calories { get; set; }
        public string Protein { get; set; }
        public string Fat { get; set; }
        public string Carbs { get; set; }
    }

我的想法是用列名(它的页脚)抓取行,然后抓取总计行,然后将它们组合成一个新的 table,然后以某种方式弄清楚如何select 列的数据。我还没到那一步。这是我到目前为止所拥有的,但感觉我只是在挣扎: http://pastebin.com/uYvMYuM3

此代码 return 是 HTML table,我不知道如何从列中获取数据。英文示例:给我列 header == "protein" 的单元格中的数据。

table 的样子:

<table class='resultsTable'>
    <tr class='labels'>
        <th>Calories</th>

        <th>Protein</th>

        <th>Fat</th>

        <th>Carbs</th>

        <th>Fiber</th>
    </tr>

    <tr class='resultsTotals'>
        <td>2,386</td>

        <td>194</td>

        <td>109</td>

        <td>161</td>

        <td>38</td>
    </tr>
</table>

试试这个,您不需要废弃总计,只需根据以下结果生成总计,这应该会处理隐藏和重新排序的列

 public class NutritionRecord
    {
        public string Meal { get; set; }
        public string MealPart { get; set; }
        public string Calories { get; set; }
        public string Protein { get; set; }
        public string Fat { get; set; }
        public string Carbs { get; set; }
        public string Fiber { get; set; }
        public string Sugar { get; set; }
    }

和刮擦部分:

       var html = new WebClient().DownloadString("http://www.myfitnesspal.com/food/diary/chuckgross");
        var doc = new HtmlAgilityPack.HtmlDocument();
        doc.LoadHtml(html);

        var list = new List<NutritionRecord>();
        var orderedColumnsList = doc.DocumentNode.SelectNodes("//tr[@class='meal_header']/td[@class='alt']").Select(td=>td.InnerText.Trim()).ToList();
        var trs = doc.DocumentNode.SelectNodes("//tr").ToList();
        for (var i = 0; i < trs.Count; i++)
        {
            bool isMealHeader = false;
            if (trs[i].Attributes["class"] != null)
            {
                isMealHeader = trs[i].Attributes["class"].Value == "meal_header";
            }

            if (isMealHeader)
            {
                var dataRows = trs[i].SelectNodes("./following-sibling::*").TakeWhile(tr => !tr.HasAttributes)
                    .Select(tr => new NutritionRecord() { 
                        Meal = WebUtility.HtmlDecode( trs[i].SelectSingleNode("./td[@class='first alt']").InnerText.Trim()), 
                        MealPart = WebUtility.HtmlDecode(tr.SelectSingleNode("./td[@class='first alt']").InnerText.Trim()),
                        Calories = tr.SelectSingleNode(string.Format("./td[not(contains(@class, 'delete'))][{0}]", orderedColumnsList.IndexOf("Calories") + 2)).InnerText,
                        Protein = tr.SelectSingleNode(string.Format("./td[not(contains(@class, 'delete'))][{0}]", orderedColumnsList.IndexOf("Protein") + 2)).InnerText,
                        Fat = tr.SelectSingleNode(string.Format("./td[not(contains(@class, 'delete'))][{0}]", orderedColumnsList.IndexOf("Fat") + 2)).InnerText,
                        Carbs = tr.SelectSingleNode(string.Format("./td[not(contains(@class, 'delete'))][{0}]", orderedColumnsList.IndexOf("Carbs") + 2)).InnerText,
                        Fiber = tr.SelectSingleNode(string.Format("./td[not(contains(@class, 'delete'))][{0}]", orderedColumnsList.IndexOf("Fiber") + 2)).InnerText,
                    });
                list.AddRange(dataRows);
            }
        }

结果:

同样为了获取列顺序获取列headers的InnerText按顺序,然后使用IndexOf函数获取给定列名的索引,并使用该索引获取值,例如

var orderedColumnsList = doc.DocumentNode.SelectNodes("//tr[@class='labels']/th").Select(td => td.InnerText.Trim()).ToList();
var carbsValue = doc.DocumentNode.SelectSingleNode(string.Format("//tr[@class='resultsTotals']/td[{0}]", orderedColumnsList.IndexOf("Carbs") + 1)).InnerText;