我正在尝试使用Haskell和Text.XML.Cursor
包解析HTML页面。我的目标是返回列表中的第三列值。我花了4个小时试图让它发挥作用。但是,似乎我无法理解XPath和Text.XML.Cursor
在概念上如何运作。
所以任务是:
<table class="forumTable">
标记<tr>
代码,请使用第三个 <td>
代码<a>
标记,content
我要将其添加到列表中。不是href
属性,而是<a>
和</a>
具体来说,我正在解析this link,我试图获取具有论坛名称的列的值(在主题之后的下一个)。
这是我能得到的。似乎要返回表格中<a>
个标签的所有内容
findNodes :: Cursor -> [Cursor]
findNodes = element "table" >=> attributeIs "class" "forumTable" &// element "tr" &/ element "td" &/ element "a" >=> child
extractData = T.concat . content
...
let cursor = fromDocument $ parseLBS $ simpleHTTP "http://www.sql.ru/forum/sqlru-3-days/2"
let lines = cursor $// findNodes &| extractData
我正在寻找解决我的问题的方法,以及解释这一切是如何运作的。谢谢。
答案 0 :(得分:2)
我已经明白了!
考虑以下输入文件:
<html>
<head>
<title>Haskell XML Parsing Example</title>
</head>
<body>
<table class="forumTable">
<tbody>
<tr>
<td> <a href="#1">1</a> </td>
<td> <a href="#2">2</a> </td>
<td> <a href="#3">3</a> </td>
<td> <a href="#4">4</a> </td>
</tr>
<tr>
<td> <a href="#5">5</a> </td>
<td> <a href="#6">6</a> </td>
</tr>
<tr>
<td> <a href="#7">7</a> </td>
<td> <a href="#8">8</a> </td>
<td> <a href="#9">9</a> </td>
<td> <a href="#10">10</a> </td>
<td> <a href="#11">11</a> </td>
</tr>
</tbody>
</table>
</body>
</html>
和Haskell代码:
{-# LANGUAGE OverloadedStrings #-}
import qualified Data.Text.IO as T
import Text.XML.Cursor
import Text.XML
select3rd :: Name -> Axis
select3rd name =
helper . (child >=> element name)
where
helper (_:_:x:_) = [x]
helper _ = []
findNodes :: Cursor -> [Cursor]
findNodes =
element "table" >=> attributeIs "class" "forumTable"
&// element "tr" >=> select3rd "td"
&/ element "a"
main :: IO ()
main = do
doc <- Text.XML.readFile def "res/test.html"
let cursor = fromDocument doc
mapM_ T.putStrLn (cursor $.// findNodes &/ content)
需要一些时间才能了解所有这些运算符($.//
,&/
等)是如何工作的,但在阅读了documentation几次并使用它们之后,事情变得更好。此外,我建议您仔细阅读解释Cursor
和Axis
的内容的部分。
上面的代码如下(教学上)。首先,我们使用提供的库函数将main
cursor
放入文档的根目录。然后我们将cursor
,findNodes
和content
组合在一起。运算符$.//
表示findNodes
必须在当前游标(cursor
)及其所有后代的上下文中运行。换句话说,它只是意味着findNodes
将访问整个HTML树。试一试,使用$/
代替$.//
。您会注意到findNodes
使用<table>
找不到element "table"
元素,因为<table>
不在<html>
的正下方(cursor
1}}目前指向)。然后,在findNodes
中,我们使用element "table"
运算符组合attributeIs "class" "forumTable"
和>=>
,因为右侧的函数(rhs)必须在Axis
上执行由左手边(lhs)返回。
之后,我们使用&//
运算符来过滤<tr>
中的后代<table>
。与以前一样,如果我们仅使用&/
,则找不到<tr>
,因为有一个封闭的<tbody>
标记,&/
只查看直接后代(或儿童)。请注意,当我们在lhs中有Cursor
时,运算符以$
开头,而当我们有Axis
时,它们以&
开头。最后,我们有select3rd
轴,它允许在节点存在时对节点的第三个子节点进行命名选择。我们使用它来选择每个<td>
中的第三个<tr>
。希望,到现在为止,应该很容易理解它是如何运作的。
您的问题中的代码在使用child
运算符的findNodes
末尾呼叫>=>
。只要您还将&/
更改为content
到>=>
,您就可以保留该调用(两种实现都是等效的。)