@Julien이 지적했듯이, 원시 html에 (html/select raw-html selectors)
을 적용 할 때 깊이 중첩 된 트리 구조로 작업해야 할 것입니다. html/select
을 여러 번 적용하려고 시도했지만 작동하지 않는 것 같습니다. html/select
은 HTML을 클로저 데이터 구조로 파싱하므로 해당 데이터 구조에 HTML을 다시 적용 할 수 없습니다.
내가 웹 사이트를 분석하는 것은 실제로 거의 관여 것을 발견했다,하지만 난 그게 multimethods을위한 좋은 사용 사례가 될 수 있다는 생각, 그래서 함께 뭔가를 해킹, 어쩌면이 얻을 것이다 당신은 시작 :
(코드를 여기 추한, 당신은 또한 무슨 일이 일어나고 있는지에 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
에있는지도 인 경우
우리가해야할 일은 적절한 방법을 정의하는 것입니다. 우리가 터미널 노드에 있다면 우리는 내용을 문자열로 변환합니다. 그렇지 않으면 우리는 내용을 되풀이하거나 콜렉션에서 구문 분석 노드 함수를 매핑합니다. 상대하고있어. ::String
의 메소드는 실제로 사용되지 않지만 안전을 위해 남겨 두었습니다.
h3+table
기능은 이전과 거의 같았습니다. 선택기를 약간 단순화하여 세트로 만들었습니다. 의도 한대로 작업 한 것처럼 맵에 넣을 지 확실하지 않습니다.
해피 스크래핑!
답장을 보내 주셔서 감사합니다. 나는 아직도 Clojure 세계의 초보자입니다. 특히이 특정 프레임 워크에서는 멀티 메소드를 아직 사용하지 않았습니다. 귀하의 솔루션은 훌륭합니다. 지금 테스트 중이에요. 내 프로젝트의 다른 유스 케이스에서 (물론 약간 수정 된) 자신의 방식을 확실히 사용할 것이다. 다시 한 번 감사드립니다! : D –
안녕하세요. 대단히 환영합니다. 나는 오래 전에 당신의 입장에있었습니다. 만약 당신이 그것에 충실하다면 당신은 클로이가 당신을 꽤 빨리 자랍니다. 언어는 매우 작고 추상화는 강력합니다. 정말 좋아합니다.) – Paul