Вот перед вами встала задача получения информации с каких-нибудь сторонних сайтов для работы вашего приложения. Если вы знаете Python, то наверня-ка слышали о Scrapy. Если нет, то ничего страшного. На Java для решения этой проблемы существует довольно удобная и очень простая библиотека Jsoup, которая позволяет вам вытащить из HTML-документа любую интересующую вас информацию.
Знакомство с библиотекой мы построим, возможно несколько необычным для некоторых читателей образом: мы не будем писать отдельную тестовую программу — а напишем набор тестов, где в каждом тестовом методе продемонстрируем некоторые основные аспекты использования данной библиотеки.
Кому интересно, Добро Пожаловать под кат.
Создаем тестовый HTML-документ.
Мы будем тестировать как обработку локального HTML-документа, так и подключение к удаленному ресурсу (Википедия). Локальный документ можно либо сохранить в отдельном файле, как в примере нашего теста, либо просто присвоить строке. Второй вариант менее гибкий, но большой роли на самом деле не играет.
Вот как у меня выйдет содержимое папки test в моем проекте:
Содержимое простого HTML-документа для тестов:
1 2 3 4 5 6 7 8 |
<html> <head> <title>Jsoup test!</title> </head> <body id="content"> <a href="http://example.com">This is reference</a> </body> </html> |
Тестовые данные подготовлены — теперь давайте напишем сам тест и вместе с этим изучим работу библиотеки Jsoup.
Пишем тесты.
Создадим класс теста и реализуем метод, загружающий наш тестовый HTML-документ. Напомним, что метод, который должен быть вызван до выполнения всех тестовых методов, в JUnit помечается аннотацией @Before. Краткое введение в JUnit находится здесь.
Получение документа.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
public class JsoupTest { private String htmlText; @Before public void setUp() throws URISyntaxException { File file = new File(getClass().getClassLoader().getResource("jsoup_test.html").toURI()); try (BufferedReader reader = new BufferedReader(new FileReader(file))) { StringBuilder builder = new StringBuilder(); String line; while ((line = reader.readLine()) != null) { builder.append(line); } this.htmlText = builder.toString(); } catch (Exception e) { e.printStackTrace(); } } @Test public void testParseDocument() { Document document = Jsoup.parse(htmlText); assertNotNull(document); } |
В методе setUp() мы загружаем тестовые данные и сохраняем их в переменную htmlText. Первый тестовый метод — testParseDocument() показывает основу работы с Jsoup. Статический метод Jsoup.parse() принимает на вход текст нашего документа и создает объект типа Document (библиотека Jsoup на самом деле строит на основе полученного документа модель DOM — дерево элементов HTML).
А как распарсить удаленную страничку? Напишем второй тестовый метод!
1 2 3 4 5 |
@Test public void testParseRemoteDocument() throws Exception { Document document = Jsoup.connect("https://www.wikipedia.org").get(); assertNotNull(document); } |
Здесь при помощи методов Jsoup.connect() и get() мы делаем обычный GET-запрос и получаем страничку. Если страницы не существует или возникнет какая-то ошибка, объект Document не будет создан.
Поиск элементов по id.
Теперь давайте посмотрим на то, как искать элементы по их идентификаторам. В нашем документе элемент <body> имеет идентификатор content. С Jsoup мы легко получим этот элемент:
1 2 3 4 5 6 |
@Test public void testGetElementById() { Document document = Jsoup.parse(htmlText); Element body = document.getElementById("content"); assertNotNull(body); } |
Метод getElementById() возвращает объект Element, представляющий какой-то компонент страницы HTML. Внутри каждого элемента, например, внутри элемента <body> могут быть вложенные элементы, поэтому объект Element содержит такие же методы поиска по идентификатору, тегу и атрибутам, как и объект Document.
Поиск элементов с нужными тегами.
А теперь получим внутри тела документа все ссылки — значит нам нужно произвести поиск всех элементов, представленных тегом <a>:
1 2 3 4 5 6 7 |
@Test public void testGetElementByTag() { Document document = Jsoup.parse(htmlText); Element body = document.getElementById("content"); Elements links = body.getElementsByTag("a"); assertEquals(1L, links.size()); } |
Внутри тега <body> у нас содержится только одна ссылка, поэтому получив массив элементов по заданному тегу, мы проверяем, что он содержит единственный элемент.
Получаем значения ссылок в документе.
Давайте теперь вытащим саму ссылку из нашего элемента. Каждый тег <a> содержит атрибут href, задающим саму ссылку:
1 |
<a href="http://example.com">This is reference</a> |
Алгоритм действий тот же — получаем объект Document, тело документа и ссылки внутри него(представленные тегом <a>).
1 2 3 4 5 6 7 8 |
@Test public void testGetLinkValue() { Document document = Jsoup.parse(htmlText); Element body = document.getElementById("content"); Elements links = body.getElementsByTag("a"); Element link = links.get(0); assertEquals(HREF_VALUE, link.attr("href")); } |