2

URLを関数に提供するHTMLページからスクレイピングとタグを作成する関数を作成しようとしましたが、これは正常に機能します。<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) の結果からテーブルタグのみを抽出することは可能ですか?

4

2 に答える 2

2

@Julienが指摘したよう(html/select raw-html selectors) に、生のhtmlに適用することで得られる、深くネストされたツリー構造を操作する必要があります。複数回適用しようとしているようですが、うまくいきhtml/selectません。html/selecthtmlをclojureデータ構造に解析するため、そのデータ構造に再度適用することはできません。

Webサイトの解析は実際には少し複雑であることがわかりましたが、マルチメソッドの良いユースケースになるかもしれないと思ったので、何かを一緒にハッキングしました。

(ここのコードは醜いです。この要点をチェックアウトすることもできます)

(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<br/>データ構造を取得し、そのコンテンツを文字列に収集して返します。これを、無視したいネストされたサブタグ(など)がまだ含まれている可能性のあるコンテンツに適用できるようにします。

派生ステートメントは、後でマルチメソッド解析ノードで使用するアドホック階層を確立します。これは、どのデータ構造に遭遇するかがまったくわからず、後で簡単にケースを追加できるため便利です。

このtag-type関数は、実際には階層ステートメントを模倣するハックです。名前空間で修飾されていないキーワードから階層を作成することはできないので、このようにしました。

マルチメソッドparse-nodeは、ノードのクラスにディスパッチし、ノードがマップである場合はさらににディスパッチしますtag-type

ここで行う必要があるのは、適切なメソッドを定義することだけです。ターミナルノードにいる場合は、コンテンツを文字列に変換します。それ以外の場合は、コンテンツを繰り返し処理するか、処理しているコレクションに解析ノード関数をマップします。 。の方法::Stringは実際には使われていませんが、安全のために残しておきました。

関数は以前とほぼ同じです。h3+tableセレクターを少し簡略化してセットに入れましたが、意図したとおりにマップに入れるかどうかはわかりません。

ハッピースクレイピング!

于 2011-10-18T11:31:50.780 に答える
1

あなたの質問はわかりにくいですが、最後の行は単に

(h3+table url)

これにより、スクレイピングされた HTML を含む深くネストされたデータ構造が返され、通常の Clojure シーケンス API を使用して掘り下げることができます。幸運を。

于 2011-10-17T23:03:00.087 に答える