【发布时间】:2014-07-17 04:18:20
【问题描述】:
我正在尝试解析来自 NOAA 网站 (weather.noaa.gov) 的电台列表。如果您查看页面的来源,例如Belarus Stations,您可以看到可用电台列表显示为:
<select name="cccc">
<option selected>Select a location
<OPTION VALUE="UMBB"> Brest
<OPTION VALUE="UMGG"> Gomel'
<OPTION VALUE="UMMG"> Grodno
<OPTION VALUE="UMMM"> Loshitsa / Minsk International 1
<OPTION VALUE="UMMS"> Minsk
<OPTION VALUE="UMII"> Vitebsk
</select>
您可以看到“OPTION”标签没有关闭。 HtmlAgilityPack 中的默认选项关闭标签,如下所示:
<select name="cccc">
<option selected>Select a location
<OPTION VALUE="UMBB"> Brest
<OPTION VALUE="UMGG"> Gomel'
<OPTION VALUE="UMMG"> Grodno
<OPTION VALUE="UMMM"> Loshitsa / Minsk International 1
<OPTION VALUE="UMMS"> Minsk
<OPTION VALUE="UMII"> Vitebsk
</OPTION></OPTION></OPTION></OPTION></OPTION></OPTION></OPTION>
</select>
这使得解析或遍历变得很痛苦。我想出了以下方法来递归每个标签,但我想知道是否有更优雅的方法,也许使用 LINQ?
我的方法:
private static void GetStations(HtmlNode node, ref Dictionary<string, string> stations)
{
// the HTML is malformed, such that the <option> elements are
// not properly closed, so we have to parse manually
string name = node.GetAttributeValue("value", string.Empty).Trim();
string value = node.InnerHtml.Substring(0, node.InnerHtml.IndexOf("\n")).Trim();
if (!string.IsNullOrEmpty(name) &&
name.Length == 4 &&
char.IsUpper(name[0]))
{
stations.Add(name, value);
}
// due to not closing the <option> elements
// we have to recurse into child nodes until
// we get them all
if (node.HasChildNodes)
{
GetStations(node.LastChild, ref stations);
}
}
这样称呼:
Dictionary<string, string> sites = new Dictionary<string, string>();
...
foreach (HtmlNode option in select.ChildNodes)
{
if ((option.Name == "option") && (option.HasAttributes))
{
GetStations(option, ref sites);
}
}
我觉得我正在使用蛮力方法来获取电台列表,我可能会错过 HtmlAgilityPack 库的一些功能。有没有更好的办法?是否有可能使这成为非问题的设置? LINQ 可以更轻松地处理这个问题吗?
我正在尝试 XPATH,因为它似乎是获取标签子集的最简单机制。但是,由于标签没有被关闭,我得到了页面上的每个选项标签,而我只想要“选择”标签内的标签。因此,如您所见,一个限定符是我想要的“选项”标签有一个@value=“XXXX”,其中“XXXX”是一个 4 个字符的大写电台 ID。有没有办法指定我只需要文档中的选项标记,这些选项标记具有名为“值”的属性和大写的 4 字符值?我可以将比较函数传递给 xpath 语句吗?
【问题讨论】:
标签: c# linq html-agility-pack