使用HtmlAgilityPack解析HTML读取选项标记内容

3
我想使用HtmlAgilityPack来解析HTML,但是遇到了问题。
示例HTML文档:
<tr>
  <td class="css_lokalita" colspan="4">
    <select id="region" name="region">
      <option value="0"  selected>Všetky regiony</option>
      <optgroup>Banskobystrický kraj</optgroup>
      <option value="k_1"  style="color: #000000; font-weight:bold;">Banskobystrický kraj</option>
      <option value="1">&nbsp;&nbsp;&nbsp;Banská Bystrica</option>
          .
          .
          .
      <option value="174">&nbsp;&nbsp;&nbsp;CZ - Ústecký kraj</option>
      <option value="175">&nbsp;&nbsp;&nbsp;CZ - Zlínský kraj</option>     
    </select>
  </td>
</tr>

<tr>
  <td class="css_sfotkou"  colspan="4">
    <input type="checkbox" name="foto" value="1" id="foto" />
    <label for="foto">Iba používatelia s fotkou</label>
  </td>
</tr>

<tr>
  <td class="css_miestnost" colspan="4">
    <select name="akt-miest" id="onoffaci">
      <option value="a_0">Všetci</option>
          .
          .
          .
      <optgroup label="Záľuby a záujmy">
        <option value="m_1419307">&nbsp;&nbsp;&nbsp;Bez Lásky</option>
          .
          .
          .
        <option value="m_1108016">&nbsp;&nbsp;&nbsp;Drum N Bass</option>
      </optgroup>
    </select>
  </td>
</tr>

我需要从<select name="akt-miest" id="onoffaci">中解析值。

例如:

<option value="**a_0**">**Všetci**</option>

我需要获取值为**a_0**和文本为**Všetci**

因此,我首先尝试通过ID访问选择器:

var selectNode = htmlDoc.GetElementbyId("onoffaci");

然后使用Xpath选择所有选项节点。
var nodes = selectNode.SelectNodes("//option");

获取数值:

foreach (var node in nodes)
{
    string roomName = node.NextSibling.InnerText;
    string roomId = node.Attributes["value"].Value;
    rooms.Add(new Room { RoomId = roomId, RoomName = roomName });
}

但是我从另一个选择器中获取值 (<select id="region" name="region">),这个选择器位于HTML代码的顶部。

编辑后:

我采用了Darin Dimitrov的建议并尝试了这个方法:

HtmlNode selectNode = htmlDoc.GetElementbyId("onoffaci");

var nodes = selectNode.SelectNodes("option");

foreach (var node in nodes)
{
    string roomName = node.NextSibling.InnerText;
    string roomId = node.Attributes["value"].Value;
    rooms.Add(new Room { RoomId = roomId, RoomName = roomName });
}

return rooms;

我只解析了前三个选项元素,因为我认为问题在于选择包含optgroup标签。
<select name="akt-miest" id="onoffaci">
  <option value="a_0">Všetci</option>
  <option value="a_1">Iba prihlásení</option>
  <option value="a_5" selected="selected">Teraz na Pokeci</option>
  <optgroup label="Hlavné miestnosti">
    <option value="m_13">&nbsp;&nbsp;&nbsp;Bez záväzkov</option>
    <option value="m_9">&nbsp;&nbsp;&nbsp;Do pohody</option>
    <option value="m_39">&nbsp;&nbsp;&nbsp;Dámsky klub</option>
  </optgroup>
  .
  .
  .

我尝试使用以下方法选择所有后续节点:

var nodes = selectNode.SelectNodes("option::*");

但是我遇到了这个错误:xpath有一个无效的标记。

我想要访问selectNode的所有子节点:

HtmlNode selectNode = htmlDoc.GetElementbyId("onoffaci");

编辑 #2:

这是一个完整的HTML文件,我需要解析其中的选项标签。

http://hotfile.com/dl/98442053/577b556/source.html


@user572844:请检查我的答案,其中包含解决方案和说明。 - user357812
3个回答

24
默认情况下,Html Agility Pack将<OPTION>标签视为“Empty”,这意味着它不需要一个闭合的</OPTION>标签。在这种情况下,关闭标签将被丢弃。您可以使用HtmlNode.ElementFlags集合更改此行为。
以下是一个应该满足您需求的代码:
HtmlDocument doc = new HtmlDocument();
HtmlNode.ElementsFlags.Remove("option");
doc.LoadHtml(yourHtml);

foreach (HtmlNode node in doc.DocumentNode.SelectNodes("//select[@id='onoffaci']//option"))
{
    Console.WriteLine("Value=" + node.Attributes["value"].Value);
    Console.WriteLine("InnerText=" + node.InnerText);
    Console.WriteLine();
}

你好,我尝试了你的代码,但它只选择了前三个选项标签,可能是因为我没有显示所有的HTML文件。我在底部编辑了我的问题,附上了HTML文件。 - user572844
谢谢你的回答并点赞!你知道这为什么是默认行为吗?还有其他标签会发生这种情况吗? - Adam Rackis
@AdamRackis - 是的,我有,更多信息请参见SO https://dev59.com/mm855IYBdhLWcg3wp2J0#4237224 和 https://dev59.com/d2035IYBdhLWcg3wNdPg#5557297(要获取ElementFlags的完整列表,请查看源代码,它是开源的) - Simon Mourier
@AdamRackis - 很好的问题。这实际上取决于所涉及的HTML,所以我没有一个明确的答案 :-) - Simon Mourier
谢谢Simon。我已经移除了选项标志,所以我的选项关闭标签被保留了。看起来它不会剥离td元素的结束标签(我检查过,因为有人告诉我它们的结束标签也是可选的),所以我能看到的唯一变化是输入img和br以及可能还有其他一些标签的/>被改成了>,这应该没有任何影响。还有其他需要注意的地方吗? - Adam Rackis
显示剩余2条评论

1

您的XPath表达式:

//option

这是一个绝对路径:它遍历整个树,从根开始

你需要一个相对XPath表达式:

descendant::option

或者使用简写形式

.//option

请注意:这是唯一一种情况,在此使用以 . self::node()简写)开头的路径是有用的。


0

你应该使用:

selectNode.SelectNodes("option");

改为:

selectNode.SelectNodes("//option");

如果您的XPath表达式是从HTML文档的根开始的。


我从选择节点开始,但是遇到了问题,因为选择标签中还包含optgroup标签。 - user572844

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接