【问题标题】:Using Text.XML.Cursor to get specific HTML table column使用 Text.XML.Cursor 获取特定的 HTML 表格列
【发布时间】:2017-10-27 23:38:56
【问题描述】:

我正在尝试使用 Haskell 和 Text.XML.Cursor 包解析 HTML 页面。我的目标是返回列表中的第三列值。我花了最后 4 个小时试图让它工作。但是,我似乎无法理解 XPath 和 Text.XML.Cursor 在概念上的工作原理。

所以任务是:

  1. 在文档的某处找到<table class="forumTable">标签
  2. 对于其中的每个<tr> 标签,取第三个 <td> 标签
  3. 在单元格内有一个<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

我正在寻求解决我的问题以及解释这一切如何运作的方法。谢谢。

【问题讨论】:

    标签: html parsing haskell xpath xml-parsing


    【解决方案1】:

    我想通了!

    考虑以下输入文件:

    <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)
    

    您需要一些时间才能了解所有这些运算符($.//&amp;/ 等)的工作原理,但在阅读了几次 documentation 并使用它们之后,情况会变得更好。另外,我建议仔细阅读解释 CursorAxis 是什么的部分。

    上面的代码工作如下(教学)。首先,我们使用提供的库函数将maincursor 放入文档的根目录。然后我们将cursorfindNodescontent 组合在一起。运算符$.// 意味着findNodes 必须在当前游标(cursor) 及其所有后代的上下文中运行。换句话说,它仅仅意味着findNodes 将可以访问整个 HTML 树。试一试并使用$/ 而不是$.//。您会注意到findNodes 使用element "table" 找不到&lt;table&gt; 元素,因为&lt;table&gt; 不在&lt;html&gt; 的正下方(这是cursor 当前指向的位置)。然后,在findNodes 中,我们使用&gt;=&gt; 运算符组合element "table"attributeIs "class" "forumTable",因为右侧(rhs)中的函数必须在左侧(lhs)返回的Axis 上执行。

    之后,我们使用&amp;// 运算符过滤&lt;table&gt; 内的后代&lt;tr&gt;s。和以前一样,如果我们只使用&amp;/,将找不到&lt;tr&gt;s,因为有一个封闭的&lt;tbody&gt; 标签,而&amp;/ 只查看直接后代(或子代)。请注意,当我们在 lhs 中有 Cursor 时,运算符以 $ 开头,而当我们有 Axis 时,它们以 &amp; 开头。最后,我们有select3rd 轴,它允许在节点的第三个子节点存在时对其进行命名选择。我们使用它来选择每个&lt;tr&gt; 中的第三个&lt;td&gt;。 Hopepully,到目前为止,应该很容易理解它是如何工作的。

    您问题中的代码在findNodes 的末尾调用了child,它使用&gt;=&gt; 运算符。只要您还将content 之前的&amp;/ 更改为&gt;=&gt;,就可以保留该调用(两种实现是等效的)。

    【讨论】:

    • 谢谢!没想到您可以像使用列表一样使用轴!
    猜你喜欢
    • 1970-01-01
    • 2020-01-06
    • 1970-01-01
    • 2020-10-02
    • 1970-01-01
    • 2021-05-31
    • 1970-01-01
    • 1970-01-01
    • 2013-12-04
    相关资源
    最近更新 更多