【问题标题】:Rescraping data with Enlive使用 Enlive 重新抓取数据
【发布时间】:2023-03-28 02:07:01
【问题描述】:

我尝试创建函数来从 HTML 页面中抓取和标记,我将其 URL 提供给函数,这可以正常工作。我得到<h3><table> 元素的序列,当我尝试使用选择函数从结果序列中仅提取表或h3标签时, 我得到(),或者如果我尝试映射我得到的那些标签(nil nil nil ...)。

您能帮我解决这个问题,或者解释一下我做错了什么吗?

代码如下:

(ns Test2 
  (:require [net.cgrand.enlive-html :as html]) 
  (:require [clojure.string :as string])) 

(defn get-page 
  "Gets the html page from passed url" 
  [url] 
  (html/html-resource (java.net.URL. url))) 

(defn h3+table       
    "returns sequence of <h3> and <table> tags"
  [url] 
  (html/select (get-page url) 
{[:div#wrap :div#middle :div#content :div#prospekt :div#prospekt_container :h3] 
[:div#wrap :div#middle :div#content :div#prospekt :div#prospekt_container :table]} 
               )) 

(def url "http://www.belex.rs/trgovanje/prospekt/VZAS/show")

这行让我头疼:

(html/select (h3+table url) [:table])

你能告诉我我做错了什么吗?

只是为了澄清我的问题:是否可以使用 enlive 的 select 函数从 (h3+table url) 的结果中仅提取表标签?

【问题讨论】:

    标签: clojure enlive


    【解决方案1】:

    正如@Julien 指出的那样,您可能必须使用通过在原始html 上应用(html/select raw-html selectors) 获得的深度嵌套树结构。似乎您尝试多次应用html/select,但这不起作用。 html/select 将 html 解析为 clojure 数据结构,因此您无法再次将其应用于该数据结构。

    我发现解析网站实际上有点复杂,但我认为这可能是多方法的一个很好的用例,所以我一起破解了一些东西,也许这会让你开始:

    (这里的代码很丑,你也可以看看这个gist

    (ns tutorial.scrape1
      (:require [net.cgrand.enlive-html :as html]))
    
    (def *url* "http://www.belex.rs/trgovanje/prospekt/VZAS/show")
    
    (defn get-page [url] 
      (html/html-resource (java.net.URL. url))) 
    
    (defn content->string [content]
      (cond
       (nil? content)    ""
       (string? content) content
       (map? content)    (content->string (:content content))
       (coll? content)   (apply str (map content->string content))
       :else             (str content)))
    
    (derive clojure.lang.PersistentStructMap ::Map)
    (derive clojure.lang.PersistentArrayMap  ::Map)
    (derive java.lang.String                 ::String)
    (derive clojure.lang.ISeq                ::Collection)
    (derive clojure.lang.PersistentList      ::Collection)
    (derive clojure.lang.LazySeq             ::Collection)
    
    (defn tag-type [node]
      (case (:tag node) 
       :tr    ::CompoundNode
       :table ::CompoundNode
       :th    ::TerminalNode
       :td    ::TerminalNode
       :h3    ::TerminalNode
       :tbody ::IgnoreNode
       ::IgnoreNode))
    
    (defmulti parse-node
      (fn [node]
        (let [cls (class node)] [cls (if (isa? cls ::Map) (tag-type node) nil)])))
    
    (defmethod parse-node [::Map ::TerminalNode] [node]
      (content->string (:content node)))
    (defmethod parse-node [::Map ::CompoundNode] [node]
      (map parse-node (:content node)))
    (defmethod parse-node [::Map ::IgnoreNode] [node]
      (parse-node (:content node)))
    (defmethod parse-node [::String nil] [node]
      node)
    (defmethod parse-node [::Collection nil] [node]
      (map parse-node node))
    
    (defn h3+table [url] 
     (let [ws-content (get-page url)
           h3s+tables (html/select ws-content #{[:div#prospekt_container :h3]
                                                [:div#prospekt_container :table]})]
       (for [node h3s+tables] (parse-node node)))) 
    

    关于发生了什么的几句话:

    content-&gt;string 采用数据结构并将其内容收集到一个字符串中并返回,以便您可以将其应用于可能仍包含您想要忽略的嵌套子标签(如 &lt;br/&gt;)的内容。

    派生语句建立了一个临时层次结构,我们稍后将在多方法解析节点中使用它。这很方便,因为我们永远不知道我们会遇到哪些数据结构,我们可以在以后轻松添加更多案例。

    tag-type 函数实际上是一个模仿层次结构语句的 hack - AFAIK 你不能用非命名空间限定的关键字创建层次结构,所以我这样做了。

    多方法parse-node在节点的类上调度,如果该节点是一个映射额外在tag-type上。

    现在我们要做的就是定义适当的方法:如果我们在终端节点,我们将内容转换为字符串,否则我们要么在内容上递归,要么将解析节点函数映射到我们的集合上'重新处理。 ::String 的方法其实也没用过,但为了安全,我把它留了下来。

    h3+table 函数与您之前的功能差不多,我稍微简化了选择器并将它们放入一个集合中,不确定将它们放入地图中是否按预期工作。

    祝你刮得愉快!

    【讨论】:

    • 感谢保罗的回复。我仍然是 Clojure 世界的新手,尤其是在这个特定的框架中,而且我还没有使用过多方法。您的解决方案很棒,我现在正在测试它。在我项目的另一个用例中,我肯定会使用你的方式(当然稍微修改)。再次感谢 !!! :D
    • 嘿,非常欢迎。不久前我还在你的位置上,如果你坚持下去,我想你会发现clojure 很快就会在你身上生长。该语言相当紧凑,其抽象功能强大,我真的很喜欢它;)
    【解决方案2】:

    你的问题很难理解,但我认为你的最后一行应该是

    (h3+table url)
    

    这将返回一个深度嵌套的数据结构,其中包含抓取的 HTML,然后您可以使用常用的 Clojure 序列 API 深入研究该结构。祝你好运。

    【讨论】:

    • 您好,感谢您的回复。我将尝试澄清我的问题:是否可以使用 enlive 的 select 函数从 (h3+table url) 的结果中仅提取表标签?
    • 除了之前的评论:当我输入类似 (map :table (h3+table url)) 的内容时,我得到 (nil nil nil ...),或者当我尝试使用 (select (h3+table url) [:table]) 时,我得到 ()。这是我的主要问题。你能帮我解决这个问题吗?
    • (h3+table url) 产生一个深度嵌套的 clojure 数据结构。查看它,看看它是否符合您的喜好。如果是,请开始将其拆开,以通过标准 Clojure 序列 API 获得所需的内容。此时您不再需要激活。或者改进你的抓取选择器以缩小你想要的 html。我认为你应该在 REPL 进行实验,然后事情就会变得清晰。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-04-03
    • 2014-03-26
    • 2012-03-22
    • 2012-05-14
    • 2012-07-20
    • 1970-01-01
    相关资源
    最近更新 更多