#java #web-scraping #clojure #jsoup
#java #очистка веб-страниц #clojure #jsoup
Вопрос:
Использование JSoup для анализа html-строки с помощью Clojure, источник как следующий
Зависимости
:dependencies [[org.clojure/clojure "1.10.1"]
[org.jsoup/jsoup "1.13.1"]]
Исходный код
(require '[clojure.string :as str])
(def HTML (str "<html><head><title>Website title</title></head>
<body><p>Sample paragraph number 1 </p>
<p>Sample paragraph number 2</p>
</body></html>"))
(defn fetch_html [html]
(let [soup (Jsoup/parse html)
titles (.title soup)
paragraphs (.getElementsByTag soup "p")]
{:title titles :paragraph paragraphs}))
(fetch_html HTML)
Ожидаемый результат
{:title "Website title",
:paragraph ["Sample paragraph number 1"
"Sample paragraph number 2"]}
К сожалению, результат не соответствует ожиданиям
user ==> (fetch_html HTML)
{:title "Website title", :paragraph []}
Комментарии:
1. Вы пытались передать «p» вместо «a» методу getElementsByTag?
2. Не могли бы вы более конкретно рассказать об используемых версиях и т. Д. Ваш код WFM . Использование
str
there не требуется (как и импорт), но это не должно повредить результату.3. @cfrick: зависимости [[org.clojure / clojure «1.10.1»] [org.jsoup / jsoup «1.13.1»]] , спасибо
Ответ №1:
(.getElementsByTag …) возвращает последовательность элементов, вам нужно вызвать метод .text() для каждого элемента, чтобы получить текстовое значение. Я использую Jsoup версии 1.13.1.
(ns core
(:import (org.jsoup Jsoup))
(:require [clojure.string :as str]))
(def HTML (str "<html><head><title>Website title</title></head>
<body><p>Sample paragraph number 1 </p>
<p>Sample paragraph number 2</p>
</body></html>"))
(defn fetch_html [html]
(let [soup (Jsoup/parse html)
titles (.title soup)
paragraphs (.getElementsByTag soup "p")]
{:title titles :paragraph (mapv #(.text %) paragraphs)}))
(fetch_html HTML)
Также рассмотрите возможность использования Reaver, который представляет собой библиотеку Clojure, которая обертывает JSoup, или любые другие оболочки, как предлагали другие.
Ответ №2:
У меня есть оболочка Clojure для TagSoup, которая может быть полезна. Попробуйте запустить его в этом проекте шаблона. Для использования в вашем проекте добавьте строку:
[tupelo "21.01.05"]
к вашему :dependencies
входу project.clj
.
Пример кода:
(ns tst.demo.core
(:use demo.core tupelo.core tupelo.test)
(:require
[tupelo.parse.tagsoup :as tagsoup]
))
(dotest
(let [html "<html>
<head><title>Website title</title></head>
<body><p>Sample paragraph number 1 </p>
<p>Sample paragraph number 2</p>
</body></html>"]
(is= (tagsoup/parse html)
{:tag :html,
:attrs {},
:content [{:tag :head,
:attrs {},
:content [{:tag :title, :attrs {}, :content ["Website title"]}]}
{:tag :body,
:attrs {},
:content [{:tag :p, :attrs {}, :content ["Sample paragraph number 1 "]}
{:tag :p, :attrs {}, :content ["Sample paragraph number 2"]}]}]})))
Подробные сведения
Если вы посмотрите на исходный код, вы легко поймете, почему вы хотите использовать функцию-оболочку!
(ns tupelo.parse.tagsoup
(:use tupelo.core)
(:require
[schema.core :as s]
[tupelo.parse.xml :as xml]
[tupelo.string :as ts]
[tupelo.schema :as tsk]))
(s/defn ^:private tagsoup-parse-fn
[input-source :- org.xml.sax.InputSource
content-handler]
(doto (org.ccil.cowan.tagsoup.Parser.)
(.setFeature "http://www.ccil.org/~cowan/tagsoup/features/default-attributes" false)
(.setFeature "http://www.ccil.org/~cowan/tagsoup/features/cdata-elements" true)
(.setFeature "http://www.ccil.org/~cowan/tagsoup/features/ignorable-whitespace" true)
(.setContentHandler content-handler)
(.setProperty "http://www.ccil.org/~cowan/tagsoup/properties/auto-detector"
(proxy [org.ccil.cowan.tagsoup.AutoDetector] []
(autoDetectingReader [^java.io.InputStream is]
(java.io.InputStreamReader. is "UTF-8"))))
(.setProperty "http://xml.org/sax/properties/lexical-handler" content-handler)
(.parse input-source)))
; #todo make use string input: (ts/string->stream html-str)
(s/defn parse-raw :- tsk/KeyMap
"Loads and parse an HTML resource and closes the input-stream."
[html-str :- s/Str]
(xml/parse-raw-streaming
(org.xml.sax.InputSource.
(ts/string->stream html-str))
tagsoup-parse-fn))
; #todo make use string input: (ts/string->stream html-str)
(s/defn parse :- tsk/KeyMap
"Loads and parse an HTML resource and closes the input-stream."
[html-str :- s/Str]
(xml/enlive-remove-whitespace
(xml/enlive-normalize
(parse-raw
html-str))))
Комментарии:
1. Я попытаюсь внедрить в свой код. Спасибо за ваш ответ,
2. Пожалуйста, ознакомьтесь с обновлением. Не встраивайте, просто используйте библиотеку через ссылку в
project.clj