Пересечение данных с помощью Enlive - PullRequest
2 голосов
/ 18 октября 2011

Я попытался создать функцию для очистки и тегов со страницы HTML, URL-адрес которой я предоставляю функции, и это работает как надо.Я получаю последовательность элементов <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])

Не могли бы вы сказать, что я делаю не так?

Просто чтобы прояснить мой вопрос: возможно ли использовать функцию select вive для извлечения только табличных тегов из результата (h3 + URL таблицы)?

Ответы [ 2 ]

2 голосов
/ 18 октября 2011

Как отметил @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 почти такая же, как у вас раньше, я немного упростил селекторы и поместил их вустановить, не уверен, что если поместить их на карту так, как вы работали, как задумано.

Удачи!

1 голос
/ 18 октября 2011

Ваш вопрос трудно понять, но я думаю, что ваша последняя строка должна быть просто

(h3+table url)

Это вернет глубоко вложенную структуру данных, содержащую очищенный HTML-код, в который затем можно погрузиться с помощью обычных API-интерфейсов последовательности Clojure. Удачи.

...