我正在处理非常原始的HTML结构,如下所示:
<a NAME="header1"></a><b><font face="Verdana, Serif"><font color="#000000"><font size=+1>Hygiene</font></font></font></b>
<p><b><font face="Verdana, Serif"><font color="#000000">Shampoo</font></b>
<p><b><font face="Verdana, Serif"><font color="#000000"></font>Soap</font></b>
<p><b><font face="Verdana, Serif"><font color="#000000">Deodorant</font></b>
<p><b><font face="Verdana, Serif"><font color="#000000">Toothpaste</font></b>
<p><b><font face="Verdana, Serif"><font color="#000000"></font>Brush</font></b>
<a NAME="header2"></a><b><font face="Verdana, Serif"><font color="#000000"><font size=+1>Food</font></font></font></b>
<p><b><font face="Verdana, Serif"><font color="#000000">Meat</font></b>
<p><b><font face="Verdana, Serif"><font color="#000000">Vegetables</font></b>
<p><b><font face="Verdana, Serif"><font color="#000000">Fruit</font></b>
现在的事情是,我想从Hygiene标题(顶部)获得所有物品,这些物品是洗发水,肥皂,除臭剂,牙膏,刷子(并且现在将它们放在HashMap&gt;中)。
我使用这个XPath来获取标题(卫生和食物):
//html/body//b/font/font/font
它工作正常,我得到了我需要的东西。
然后我使用此XPath来收集项目:
//html/body//p/b/font/font
所有项目。所以这个(最后一个)XPath将返回所有项目的列表,如[洗发水,肥皂,除臭剂,牙膏,刷子,肉类,蔬菜,水果]。问题是我不知道何时停止在第一个列表中放置项目(例如,当另一个标题开始时,在这种情况下是Food,创建新列表并将Food项目放在那里)。我可以用这个XPath获得的是标题(卫生,食物)和两个列表中的所有项目(不是单独的)的值。
我需要得到类似的东西:
所有项目都是这样抛出的,并且它们不在单独的div或跨度中,以便我能够识别新标题何时出现。
谢谢!
答案 0 :(得分:1)
我首先考虑(a)使用(比如)TagSoup将其转换为XML,然后(b)使用XSLT 2.0转换,将其转换为更卫生的XML结构。
我不确定TagSoup到底会做些什么,但是如果我们假设它唯一能做的就是关闭p标签(</p>
后面出现</b>
),然后步骤(b)很简单:
<xsl:for-each-group select="//body/*" group-starting-with="a">
<section name="current-group()[self::b]">
<xsl:for-each select="current-group()[self::p]">
<item><xsl:value-of select="."/></item>
</xsl:for-each>
</section>
</xsl:for-each-group>
这会给你类似的东西
<section name="Hygiene">
<item>Shampoo</item>
<item>Soap</item>
<item>Toothpaste</item>
</section>
<section name="Food">
<item>Meat</item>
<item>Veg</item>
</section>
然后更容易玩。
一般来说,当您输入结构不合理的输入时,最好先使用管道方法进行清理,然后查询它以获取所需信息。
答案 1 :(得分:0)
解析这个HTML并不容易,因为它不适合解析(从<font>
标签来判断你可能会使用一些关于它的彩色语言)。
AFAIK没有办法在XPath中表达“跟随兄弟姐妹直到X”的情况,所以这里有另一种选择:使用一个 XPath表达式来匹配标题和项目,例如使用这个特定的标记你可以使用
//body//font/child::text()
将选择所有文本节点(“Hygiene”,“Shampoo”,“Soap”,......)。
节点将按文档顺序返回(这是非常重要),因此您可以迭代结果并对每个节点执行测试,以确定它是标题还是项目(在在这种情况下,您可以检查父项是否为具有<font>
属性的size
元素。
通过这种方式,您可以保留对找到的最后一个“标题”的引用,并将所有后续“项目”添加到其下的相应数据结构中,直到您遇到下一个标题等。