2011-10-17 2 views
2

URL을 함수에 제공하는 HTML 페이지에서 태그를 긁어서 태그를 작성하려고 시도했지만 정상적으로 작동합니다. <h3><table> 요소의 시퀀스를 얻을 때 select 함수를 사용하여 결과 시퀀스에서 테이블 또는 h3 태그 만 추출하려고 할 때 get() 또는 해당 태그를 매핑하려고하면 (nil nil nil ...)).데이터를 확대하여 Rescraping

이 문제를 해결하거나 내가 뭘 잘못하고 있는지 설명해 주시겠습니까?

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

가 내가 뭘 잘못 말해 주 시겠어요 :

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

이 줄은 나에게 두통을 제공합니다 : 여기

코드인가?

내 질문에 명확히하기 위해 : (h3 + table url)의 결과에서 테이블 태그 만 추출하려면 확대 기능을 사용할 수 있습니까?

답변

2

@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 기능은 이전과 거의 같았습니다. 선택기를 약간 단순화하여 세트로 만들었습니다. 의도 한대로 작업 한 것처럼 맵에 넣을 지 확실하지 않습니다.

해피 스크래핑!

+0

답장을 보내 주셔서 감사합니다. 나는 아직도 Clojure 세계의 초보자입니다. 특히이 특정 프레임 워크에서는 멀티 메소드를 아직 사용하지 않았습니다. 귀하의 솔루션은 훌륭합니다. 지금 테스트 중이에요. 내 프로젝트의 다른 유스 케이스에서 (물론 약간 수정 된) 자신의 방식을 확실히 사용할 것이다. 다시 한 번 감사드립니다! : D –

+0

안녕하세요. 대단히 환영합니다. 나는 오래 전에 당신의 입장에있었습니다. 만약 당신이 그것에 충실하다면 당신은 클로이가 당신을 꽤 빨리 자랍니다. 언어는 매우 작고 추상화는 강력합니다. 정말 좋아합니다.) – Paul

1

귀하의 질문은 이해하기 어렵다,하지만 난 당신의 마지막 줄은 단순히

(h3+table url) 

이것은 당신이 다음 일반 Clojure의 순서 API를에 뛰어 수 긁어 HTML을 포함하는 중첩 데이터 구조를 반환해야한다고 생각 . 행운을 빕니다.

+0

안녕하세요. 답장을 보내 주셔서 감사합니다. 내 질문을 명확히하려고합니다 : (h3 + table url)의 결과에서 table 태그 만 추출하기 위해 enlive의 select 함수를 사용할 수 있습니까? –

+0

이전 주석에 더하여 : (map : table (h3 + table url))'나는 (nil nil nil ...)을 얻거나,'(select (h3 + table url)) [: table])'나는(). 그것이 나의 주요 문제이다. 이걸 좀 도와 주시겠습니까? –

+2

(h3 + table url)은 깊이 중첩 된 클로저 데이터 구조를 생성합니다. 그것을 들여다 보면 그것이 원하는대로 있는지보십시오. 그렇다면 표준 Clojure 시퀀스 API를 통해 원하는 것을 얻으려면 별도로 시작하십시오. 이 시점에서 더 이상 필요가 없습니다. 또는 원하는 html 범위를 좁히기 위해 스크래핑 선택기를 개선하십시오. 나는 당신이 REPL에서 실험을해야한다고 생각합니다. 그러면 일이 분명해질 것입니다. –

관련 문제