Руководство по веб-скрапингу с помощью Java

В этом руководстве вы узнаете, как настроить проект Gradle и установить зависимость HtmlUnit. В процессе обучения вы узнаете все, что вам нужно знать о HtmlUnit, и изучите некоторые из его расширенных возможностей.
3 min read
Web Scraping with Java Guide_large

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-элементы. В данном случае они есть всего в двух разделах:

  1. В левом сайдбаре, где отображается контент: Как видите, заголовок раздела Contents является H2-элементом.  
  2. В основном теле страницы: Все подзаголовки являются 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() , или же по их имени благодаря методу getElementByName().  

В следующем разделе вы будете использовать эти методы для скрапинга динамической веб-страницы.

Скрапинг динамической веб-страницы с помощью 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-адресов и ограничением скорости. Использование прокси также помогает эмулировать пользователей с разной геолокацией.