我正在尝试使用Haskell和Text.XML.Cursor
包解析HTML页面。我的目标是返回列表中的第三列值。我花了最后4个小时尝试使其正常运行。但是,似乎我无法理解XPath和Text.XML.Cursor
在概念上如何工作。
因此任务是:
在文档中的某处找到<table class="forumTable">
标记
对于其中的每个<tr>
标签,请使用第三个<td>
标签
单元格内有一个<a>
标记,我要将其添加到列表中。不是content
属性,而是href
和<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
我正在寻找解决我的问题的方法,并寻求这一切如何工作的解释。谢谢。
最佳答案
我知道了!
考虑以下输入文件:
<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
当前指向的位置)。然后,在findNodes
中,我们使用element "table"
运算符组成attributeIs "class" "forumTable"
和>=>
,因为右侧(rhs)的函数必须在左侧(lhs)返回的Axis
上执行。之后,我们使用
&//
运算符过滤<tr>
内部的后代<table>
。和以前一样,如果只使用&/
,则不会找到<tr>
,因为其中有一个封闭的<tbody>
标记,并且&/
仅看起来是直接后代(或子代)。注意,当lhs中有一个Cursor
时,运算符以$
开头,而当我们有Axis
时,它们以&
开头。最后,我们有select3rd
轴,该轴允许在存在节点的第三个子节点时进行命名选择。我们用它来选择每个<td>
中的第三个<tr>
。希望到现在,应该很容易理解它是如何工作的。您问题中的代码在
child
末尾使用findNodes
运算符调用>=>
。只要将&/
之前的content
更改为>=>
,就可以保留该调用(两个实现都是等效的)。
关于html - 使用Text.XML.Cursor获取特定的HTML表列,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44212378/