从动态列 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;
我正在使用 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;