正如@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->string
需要一個數據結構,並收集其內容轉換爲字符串,返回是s o您可以將此應用於可能仍包含要忽略的嵌套子標記(如<br/>
)的內容。
派生語句建立了一個特殊的層次結構,我們稍後將在多方法分析節點中使用它。這很方便,因爲我們不知道我們將遇到哪些數據結構,並且稍後我們可以輕鬆地添加更多的案例。
tag-type
函數實際上是一個模仿層次結構語句的黑客 - AFAIK你不能從非命名空間限定的關鍵字創建一個層次結構,所以我這樣做了。
多方法parse-node
調度節點的類,如果節點是tag-type
附加地圖。
現在我們要做的就是定義合適的方法:如果我們在終端節點上,我們將內容轉換爲字符串,否則我們會在內容上重複或映射集合上的parse-node函數正在處理。 ::String
的方法實際上甚至沒有使用,但爲了安全起見,我將它放在了上面。
h3+table
函數幾乎是你以前的東西,我簡化了選擇器並將它們放入一個集合中,不確定是否將它們放入地圖中,就像您按照預期工作一樣。
快樂刮!
感謝您的回覆。我仍然是Clojure世界的新手,特別是在這個特定的框架中,我還沒有使用multimethods。您的解決方案非常好,現在我正在測試它。我肯定會用你的方式(當然稍作修改),在我的項目的另一個用例中。再次感謝 !!! :D –
嘿,非常歡迎。我不久前就在你的位置,如果你堅持下去,我想你會發現clojure很快就會在你身上增長。語言相當緊湊,抽象性強,我非常喜歡;) – Paul