HtmlUnit — браузер без графической оболочки, позволяющий моделировать HTML-страницы. После программного моделирования страницы вы можете взаимодействовать с ней, выполняя такие задачи, как заполнение форм, их отправка и перемещение между страницами. Его можно использовать для веб-скрапинга и создания автоматизированных тестов, позволяющих проверить, что программа создает веб-страницы в соответствии с вашими ожиданиями.
Веб-скрапинг с помощью HtmlUnit
Для реализации веб-скрапинга с помощью HtmlUnit и Gradle будет использоваться IntelliJ IDEA, однако вы можете использовать любую другую IDE или редактор кода, который предпочитаете.
IntelliJ поддерживает полнофункциональную интеграцию с Gradle. IDE можно загрузить на сайте JetBrains. Gradle — инструмент автоматизации сборки, который поддерживает сборку и создание пакетов для вашего приложения. Он также позволяет легко добавлять и управлять зависимостями. В последней версии IntelliJ IDEA Gradle и его расширения установлены и включены по умолчанию.
Весь код для данного руководства можно найти в этом репозитории GitHub.
Создайте проект Gradle
Чтобы создать новый проект Gradle в IntelliJ IDE, выберите в меню File > New > Project, после чего откроется мастер создания нового проекта. Введите имя проекта и выберите его местоположение:
Вам необходимо выбрать язык Java, поскольку вы собираетесь создать приложение для веб-скрапинга на Java с использованием HtmlUnit. Кроме того, выберите систему сборки Gradle. Затем нажмите кнопку Create. В результате будет создан проект Gradle со структурой по умолчанию и всеми необходимыми файлами. Например, файл build.gradle
содержит все зависимости, необходимые для сборки этого проекта:
Установите HtmlUnit
Чтобы установить HtmlUnit
в качестве зависимости, откройте окно Dependencies, выбрав View > Tool Windows > Dependencies.
Затем найдите «htmlunit» и выберите Add:
Вы должны увидеть, что HtmlUnit был установлен в разделе зависимостей файла build.gradle
:
Теперь, когда вы установили HtmlUnit, настало время собирать данные со статических и динамических веб-страниц.
Веб-скрапинг статической страницы
В этом разделе вы узнаете, как выполнить веб-скрапинг такой статической веб-страницы, как HtmlUnit Wiki. Она содержит такие элементы, как заголовок, оглавление, список подзаголовков и содержание каждого подзаголовка.
Каждый элемент веб-страницы HTML имеет различные атрибуты — например, ID
или Name
. Хотя оба из них используются для идентификации элемента в HTML-документе, Name
, в отличие от ID
, не обязательно является уникальным атрибутом и может принадлежать другим элементам веб-страницы. Элементы HTML-документа могут быть идентифицированы с помощью любого из атрибутов.
В качестве альтернативы вы можете использовать для идентификации элементов XPath. Помимо идентификации, он позволяет выполнять точную навигацию по DOM-элементам HTML-страницы.
В следующих примерах вы будете использовать для идентификации элементов HTML-страницы оба этих метода.
Чтобы выполнить скрапинг веб-страницы, необходимо создать HtmlUnit WebClient. WebClient представляет собой браузер внутри вашего Java-приложения. Инициализация WebClient аналогична запуску браузера для просмотра веб-страницы.
Чтобы инициализировать WebClient, используйте следующий код:
WebClient webClient = new WebClient(BrowserVersion.CHROME);
Этот код инициализирует браузер Chrome. Другие браузеры также поддерживаются.
Получить веб-страницу можно с помощью метода getPage()
, доступного в объекте webClient
. Сделав это, вы с помощью разных методов можете извлекать из нее данные.
Чтобы получить заголовок страницы, используйте метод getTitleText()
, как показано в следующем коде:
String webPageURl = "https://en.wikipedia.org/wiki/HtmlUnit";
try {
HtmlPage page = webClient.getPage(webPageURl);
System.out.println(page.getTitleText());
} catch (FailingHttpStatusCodeException | IOException e) {
e.printStackTrace();
}
Затем будет напечатан заголовок страницы:
HtmlUnit - Wikipedia
А теперь давайте извлечем все имеющиеся на веб-странице H2-элементы. В данном случае они есть всего в двух разделах:
- В левом сайдбаре, где отображается контент: Как видите, заголовок раздела Contents является H2-элементом.
- В основном теле страницы: Все подзаголовки являются H2-элементами.
Чтобы получить все H2-элементы веб-страницы, можно воспользоваться их XPath. Чтобы найти XPath, щелкните правой кнопкой мыши на нужном H2-элементе и выберите Inspect. Затем щелкните правой кнопкой мыши на выделенном элементе и выберите Copy > Copy full XPath:
Это скопирует XPath в буфер обмена. Например, XPath H2-элемента внутри тега body — /html/body/div[1]/div/div[3]/main/div[2]/div[3]/div[1]/h2
.
Чтобы получить все H2-элементы с помощью их XPath, можно использовать метод getByXpath()
:
String xPath = "/html/body/div[1]/div/div[3]/main/div[2]/div[3]/div[1]/h2";
String webPageURL = "https://en.wikipedia.org/wiki/HtmlUnit";
try {
HtmlPage page = webClient.getPage(webPageURL);
//Get all the headings using its XPath+
List<HtmlHeading2> h2 = (List<HtmlHeading2>)(Object) page.getByXPath(xPath);
//print the first heading text content
System.out.println((h2.get(0)).getTextContent());
} catch (FailingHttpStatusCodeException | IOException e) {
e.printStackTrace();
}
Текстовое содержимое первого H2-элемента будет выведено следующим образом:
Benefits[edit]
Аналогично, вы можете получить элементы по их ID с помощью метода getElementById()
, или же по их имени благодаря методу getElementBy
Name
().
В следующем разделе вы будете использовать эти методы для скрапинга динамической веб-страницы.
Скрапинг динамической веб-страницы с помощью HtmlUnit
В этом разделе вы познакомитесь с возможностями HtmlUnit по заполнению форм и нажатию кнопок, заполнив и отправив форму для входа в систему. Вы также узнаете, как с помощью этого браузера перемещаться по веб-страницам.
Чтобы продемонстрировать динамический веб-скрапинг, давайте воспользуемся сайтом Hacker News. Вот как выглядит страница для входа в систему:
Следующий код — это HTML-форма для предыдущей страницы. Вы можете получить его, щелкнув правой кнопкой мыши по ярлыку Login Label и выбрав Inspect:
<form action="login" method="post">
<input type="hidden" name="goto" value="news">
<table border="0">
<tbody>
<tr><td>username:</td><td><input type="text" name="acct" size="20" autocorrect="off" spellcheck="false" autocapitalize="off" autofocus="true"></td></tr>
<tr><td>password:</td><td><input type="password" name="pw" size="20"></td></tr></tbody></table><br>
<input type="submit" value="login"></form>
Чтобы заполнить форму с помощью HtmlUnit, получите веб-страницу с помощью объекта webClient
. Страница содержит две формы: Login и Create Account. Форму для входа можно получить благодаря методу getForms()
.get(0)
. В качестве альтернативы разрешается использовать метод getFormByName()
, если формы имеют уникальное имя.
Далее необходимо получить входные данные (имя пользователя и пароль) с помощью метода getInputByName()
и атрибута name
.
Установите имя пользователя и пароль в соответствующие поля с помощью метода setValueAttribute()
и получите кнопку Submit, используя для этого метод getInputByValue()
. Нажать на нее можно благодаря методу click()
.
После нажатия кнопки и в случае успешного входа в систему целевая страница кнопки Submit будет возвращена в виде объекта HTMLPage
, который можно использовать для дальнейших операций.
Следующий код демонстрирует, как получить форму, заполнить ее и отправить:
HtmlPage page = null;
String webPageURl = "https://en.wikipedia.org/wiki/HtmlUnit";
try {
// Get the first page
HtmlPage signUpPage = webClient.getPage(webPageURL);
// Get the form using its index. 0 returns the first form.
HtmlForm form = signUpPage.getForms().get(0);
//Get the Username and Password field using its name
HtmlTextInput userField = form.getInputByName("acct");
HtmlInput pwField = form.getInputByName("pw");
//Set the User name and Password in the appropriate fields
userField.setValueAttribute("draftdemoacct");
pwField.setValueAttribute("test@12345");
//Get the submit button using its Value
HtmlSubmitInput submitButton = form.getInputByValue("login");
//Click the submit button, and it'll return the target page of the submit button
page = submitButton.click();
} catch (FailingHttpStatusCodeException | IOException e) {
e.printStackTrace();
}
После отправки формы и успешного входа в систему вы попадете на домашнюю страницу пользователя, где имя пользователя отображается в правом углу:
Элемент имени пользователя имеет идентификатор (ID) «me». Вы можете получить имя пользователя с помощью метода getElementById()
и передать идентификатор «me», как показано в следующем коде:
System.out.println(page.getElementById("me").getTextContent());
Имя пользователя извлекается из веб-страницы и выводится:
draftdemoacct
Далее необходимо перейти на вторую страницу сайта Hacker News, нажав на кнопку More в конце страницы:
Чтобы получить объект кнопки More, найдите XPath кнопки More, используя опцию Inspect, и отыщите объект первой ссылки, используя индекс 0
:
Кликните по ссылке More с помощью метода click()
. Благодаря его использованию целевая страница ссылки будет возвращена в виде объекта HtmlPage
:
HtmlPage nextPage = null;
try {
List<HtmlAnchor> links = (List<HtmlAnchor>)(Object)page.getByXPath("html/body/center/table/tbody/tr[3]/td/table/tbody/tr[92]/td[2]/a");
HtmlAnchor anchor = links.get(0);
nextPage = anchor.click();
} catch (IOException e) {
throw new RuntimeException(e);
}
На этом этапе у вас должна быть вторая страница в объекте HtmlPage
.
Вы можете вывести URL HtmlPage
, чтобы проверить, успешно ли загружена вторая страница:
System.out.println(nextPage.getUrl().toString());
Ниже приведен URL второй страницы:
https://news.ycombinator.com/news?p=2
Каждая страница сайта Hacker News содержит тридцать записей. Поэтому записи на второй странице начинаются с порядкового номера 31.
Давайте получим ID первой записи на второй странице и посмотрим, равна ли она 31. Как и ранее, найти XPath первой записи можно с помощью опции Inspect.
Затем возьмите первую запись из списка и отобразите ее текстовое содержимое:
String firstItemId = null;
List<Object> entries = nextPage.getByXPath("/html/body/center/table/tbody/tr[3]/td/table/tbody/tr[1]/td[1]/span");
HtmlSpan span = (HtmlSpan) (entries.get(0));
firstItemId = span.getTextContent();
System.out.println(firstItemId);
Теперь отображается идентификатор (ID) первой записи:
31.
Этот код показывает, как заполнять форму, нажимать на кнопки и перемещаться по веб-страницам с помощью HtmlUnit.
Заключение
В этой статье вы изучили, как с помощью HtmlUnit выполнять сканирование статических и динамических веб-сайтов. Вы также узнали о некоторых расширенных возможностях HtmlUnit по скрапингу веб-страниц и преобразованию их в структурированные данные.
При работе с IDE, например IntelliJ IDEA, необходимо вручную находить атрибуты элементов, а затем с нуля прописывать использующие их функции веб-скрапинга. Для сравнения, Web Scraper IDE от Bright Data предоставляет легко обходящую любые гео-ограничения и блокировки надежную прокси-инфраструктуру, удобные функции веб-скрапинга и готовые шаблоны кода для популярных веб-сайтов. Эффективная прокси-инфраструктура — жизненно необходима для веб-скрапинга без каких-либо проблем с блокировкой IP-адресов и ограничением скорости. Использование прокси также помогает эмулировать пользователей с разной геолокацией.