Освой Android играючи

Сайт Александра Климова

Шкодим

/* Моя кошка замечательно разбирается в программировании. Стоит мне объяснить проблему ей - и все становится ясно. */
John Robbins, Debugging Applications, Microsoft Press, 2000

Библиотека jsoup - Суп с котом

Суп с котом

Общая информация
Подключаем библиотеку
Первый пример для знакомства
Извлекаем заголовок страницы
Извлекаем ссылки
Разбор текста с сайта
Разбор текста из файла

Вместо предисловия

Первоначально статья писалась, когда деревья были большими, коты были котятами, Android был версии 2.3, а библиотека jsoup была версии 1.6.1.

С тех пор утекло много воды. Хорошая новость - библиотека подросла до версии 1.13.1, стала чуть меньше размером, стала быстрее работать (почти в два раза). Плохая новость - мои примеры, связанные с интернетом, перестали работать в Android 4.0, так как теперь явно запретили использовать сетевые операции в основном потоке.

Я оставлю старую версию статьи здесь. Если вы пишете программы под старые устройства, то всё остаётся без изменений. Примеры под новые устройства находятся в закрытой зоне 4 курса.

Общая информация

Рассмотрим примеры работы с библиотекой jsoup. Java-библиотека jsoup предназначена для разбора HTML-страниц (парсинг), позволяя извлечь необходимые данные, используя DOM, CSS и методы в стиле jQuery.

Библиотека поддерживает спецификации HTML5 и позволяет парсить страницы, как это делают современные браузеры.

Библиотеке можно подсунуть для анализа URL, файл или строку.

Официальная страница библиотеки: jsoup Java HTML Parser, with best of DOM, CSS, and jquery

Подключаем библиотеку

В Android Studio пропишите в файле build.gradle строку в блоке зависимостей.


implementation 'org.jsoup:jsoup:1.13.1'

Создаём новый проект JsoupDemo. Добавляем на форму кнопку и TextView.

После установки библиотеки вам нужно получить документ для разбора текста. Это может быть страница на сайте или локальный файл на устройстве. Таким образом вам надо подключиться к нужной странице и получить объект класса Document. При импортировании обращайте внимание на полное название класса org.jsoup.nodes.Document, так как многие пакеты имеют в своём составе одноимённый класс.


Document doc  = Jsoup.connect(URL).get();

Получив документ в своё распоряжение, вы можете извлекать требуемую информацию. Например, вы можете получить все теги meta:


Elements metaElements = doc.select("meta");

Метод select() позволяет получить нужные теги.

Если нужно получить атрибут тега, то используйте метод attr():


String name = metaElement.attr("name");

Можно выбрать теги с заданным классом. Например, на странице встречается тег типа <h2 class="main">. Тогда код будет следующим.


Elements mainHeaderElements = doc.select("h2.main");

Первый пример для знакомства

Для первого знакомства разберём простой пример. А потом будем его усложнять. Создадим переменную, содержащий html-текст. Далее вызываем библиотеку jsoup и смотрим на результат.


// Kotlin
// Если этот код работает, его написал Александр Климов,
// а если нет, то не знаю, кто его писал.
package ru.alexanderklimov.jsoupdemo

import android.os.Bundle
import android.widget.Button
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity
import org.jsoup.Jsoup


class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val textView: TextView = findViewById(R.id.textView)
        val button: Button = findViewById(R.id.button)

        button.setOnClickListener {
            val html = ("<html><head><title>Коты учатся кодить</title>"
                    + "<body><p>Коты умеют <del>ш</del>кодить.<br> Они великие программисты." +
                    "<p>А еще они умеют мяукать.</p>" +
                    "<a href='http://developer.alexanderklimov.ru'>Подробности здесь</a>" +
                    "</body></html>")

            val doc = Jsoup.parse(html)
            textView.text = doc.html()
        }
    }
}

// Java package ru.alexanderklimov.jsoupdemo; import org.jsoup.Jsoup; import org.jsoup.nodes.Document; import android.app.Activity; import android.os.Bundle; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; import android.widget.TextView; public class JsoupDemoActivity extends Activity { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); final Button button = findViewById(R.id.button); final TextView txtView = findViewById(R.id.textView); button.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { String html = "<html><head><title>Коты учатся кодить</title>" + "<body><p>Коты умеют <del>ш</del>кодить.<br> Они великие программисты." + "<p>А еще они умеют мяукать.</p>" + "<a href='http://developer.alexanderklimov.ru'>Подробности здесь</a>" + "</body></html>"; Document doc = Jsoup.parse(html); textView.setText(doc.html()); } }); } }

Запустите проект и нажмите на кнопку. На экране отобразится наш текст. Но если вы присмотритесь внимательнее, то заметите некоторые отличия (скорее всего вы и не заметили). Я намеренно сделал две "ошибки". Во-первых, я не закрыл тег </head>, а также не закрыл тег </p> у первого параграфа. Однако библиотека сама подставила недостающие элементы. Именно так поступают и браузеры, если веб-мастер по невнимательности забывает ставить закрывающие парные теги.

Что мы сделали? Мы передали нужный html-текст библиотеке Jsoup и попросили его осуществить его разбор (метод parse()). В результате мы получаем экземпляр класса Document, из которого с помощью метода html() извлекаем уже обработанный текст, с которым можно работать дальше.

Если у вас всё получилось, то можно перейти к более сложным примерам. Подробная документация по методам и свойствам есть на сайте библиотеки. Вам нужно только пробовать.

Извлекаем заголовок страницы

Заголовок страницы находится в теге <title>. Чтобы получить текст заголовка, воспользуемся методом Document.title():


// Kotlin
textView.text = doc.title()

// Java textView.setText(doc.title()); // вернёт строку "Коты учатся кодить"

Извлекаем ссылки

Теперь попробуем поработать с ссылками. В нашем тексте есть ссылка, которую можно разбить на несколько логических элементов: адрес, на который ведёт ссылка, текст в ссылке и полная ссылка, которая объединяет оба элемента.

Начнём с адреса ссылки:


// Kotlin
val doc = Jsoup.parse(html)
val link = doc.select("a").first()
val linkHref = link.attr("href")
textView.text = linkHref

// Java Document doc = Jsoup.parse(html); Element link = doc.select("a").first(); String linkHref = link.attr("href"); textView.setText(linkHref); //http://developer.alexanderklimov.ru

Чтобы получить текст ссылки:


// Kotlin
val linkInnerH = link.html()
textView.text = linkInnerH

// Java String linkInnerH = link.html(); textView.setText(linkInnerH); //Подробности здесь

И, наконец, общий вариант:


// Kotlin
val linkOuterH = link.outerHtml()
textView.text = linkOuterH

// Java String linkOuterH = link.outerHtml(); tvInfo.setText(linkOuterH);

Разбор текста с сайта

Этот пример не работает на новых устройствах, так как запрещено работать с сетью в общем потоке!

Некоторые несознательные граждане могут меня обвинить в том, что я использовал синтетический пример, специально подготовленный для демонстрации. И хотят видеть пример с использованием ваших тырнетов. Ну что ж, вот вам пример.


Document doc = null;
try {
	doc = Jsoup.connect("http://developer.alexanderklimov.ru/android/").get();
} catch (IOException e) {
	e.printStackTrace();
}
String title = doc.title();
textView.setText(title);

Я подключаюсь к самой известной странице в мире http://developer.alexanderklimov.ru/android/ и получаю его заголовок.

Не забудьте установить разрешение на подключение к Интернету вашей программе. Я сам сначала долго тупил, не понимая, почему моя программа вылетала с ошибкой. Но, посмотрев в честные глаза своего кота, я понял в чем моя ошибка и исправил ее. Коты рулят.

Разбор текста из файла

Последний пример, который мы не разобрали - это разбор текста из файла. В этом случае используется метод Jsoup.parse(File in, String charsetName, String baseUri):


File input = new File("/tmp/input.html");
Document doc = Jsoup.parse(input, "UTF-8", "http://example.com/");

Попробуйте самостоятельно. Удачи в программировании! Да пребудет с вами кот!

Дополнительное чтение

Библиотека jsoup и многопоточность. Продолжение

Используем в корутинах

Реклама