Как отметил @Julien, вам, вероятно, придется работать с глубоко вложенной древовидной структурой, которую вы получаете, применяя (html/select raw-html selectors)
к необработанному html.Кажется, вы пытаетесь применить 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
берет структуру данных и собирает ее содержимое в строку и возвращает ее, чтобы вы могли применить это к содержимому, которое все еще может содержать вложенные вложенные теги (например, * 1016).*) которую вы хотите игнорировать.
Операторы производного устанавливают специальную иерархию, которую мы позже будем использовать в мульти-методе parse-node.Это удобно, потому что мы никогда точно не знаем, с какими структурами данных мы столкнемся, и мы могли бы легко добавить больше случаев позже.
Функция tag-type
на самом деле является хаком, имитирующим утверждения иерархии.не могу создать иерархию из ключевых слов, не относящихся к пространству имен, поэтому я сделал это следующим образом.
Мульти-метод parse-node
отправляет классу узла и, если узел является картой, дополнительно кtag-type
.
Теперь все, что нам нужно сделать, это определить подходящие методы: если мы находимся в терминальном узле, мы преобразуем содержимое в строку, в противном случае мы либо возвращаемся к содержимому, либо отображаем анализ-узел в коллекции, с которой мы имеем дело.Метод ::String
на самом деле даже не используется, но я оставил его в целях безопасности.
Функция h3+table
почти такая же, как у вас раньше, я немного упростил селекторы и поместил их вустановить, не уверен, что если поместить их на карту так, как вы работали, как задумано.
Удачи!