Как парсить HTML на Java с помощью Jsoup
Если вы пишете робота на Java для разбора контента с каких-либо сайтов (т.н. «краулер»), то вы можете встретиться с некоторыми сложностями. Язык HTML хоть и формализован, однако допускает ошибки в разметке без нарушения отображения, в отличие от более строгого XML. Самой частой ошибкой является незакрытый тэг.
Страница в браузере может выглядеть корректно, но при попытке разобрать вёрстку вы потерпите неудачу. Кроме html 5-ой версии, существует ещё несколько стандартов вёрстки.
Чтобы не изобретать велосипед, можно воспользоваться готовой библиотекой Jsoup, которая позволяет легко парсить исходный html и выбирать оттуда отдельные элементы в простом декларативном синтаксисе. Библиотека поддерживает выбор как в формате CSS (более привычный на frontend), так и в XPath.
Подключаем Jsoup и парсим HTML
Для подключения достаточно добавить в maven следующую зависимость:
Скачивать html содержимое можно силами самой библиотеки:
try <
var document = Jsoup.connect( «https://devmark.ru/» ).get();
// бизнес-логика
> catch (Exception e) <
// обработка ошибок парсинга
>
Если же вы используете асинхронную модель, когда робот сначала скачивает содержимое множества страниц, а затем в отдельном процессе выполняет обработку, то библиотека поддерживает также парсинг строки и чтение из файла:
var htmlFile = new File( «/home/user/Documents/index.html» );
var document = Jsoup.parse(htmlFile, StandardCharsets. UTF_8 .name());
Выбор элементов и их атрибутов
После получения объекта Document, вы можете запрашивать у него различные элементы и их атрибуты. Далее будем запрашивать элементы в формате CSS. Если известно, что элемент должен быть на странице в единственном экземпляре (например, тэг title), то используйте метод selectFirst():
var titleElement = document.selectFirst( «title» );
System.out.printf( «Title: %s%n» , titleElement.text());
Но более безопасно использовать общий подход, когда элементов может быть несколько. Например, для получения всех гиперссылок на странице, используйте метод select():
var anchors = document.body().select( «a» );
System.out.printf( «Anchor count: %s%n» , anchors.size());
Мы получаем список объектов Element, у каждого из которых нужно взять значение атрибута href с помощью метода attr():
var links = anchors.stream()
.map(anchor ->
anchor.attr( «href» )
).toList();
System.out.println(links);
В результате в консоль будут выведены все гиперссылки, которые есть на странице.
Выводы
Как видите, Jsoup предоставляет простое и понятное API для выбора элементов и их атрибутов, которые содержатся в скачанном html-документе. Обладая этими знаниями, вы уже можете написать собственный краулер, а то и новый поисковик.
Руководство по веб-скрапингу с помощью Java
В этом руководстве вы узнаете, как настроить проект Gradle и установить зависимость HtmlUnit. В процессе обучения вы узнаете все, что вам нужно знать о HtmlUnit, и изучите некоторые из его расширенных возможностей.
3 min read
Vikram Aruchamy
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)
Затем будет напечатан заголовок страницы:
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+ Listh2 = (List)(Object) page.getByXPath(xPath); //print the first heading text content System.out.println((h2.get(0)).getTextContent()); > catch (FailingHttpStatusCodeException | IOException e)
Текстовое содержимое первого H2-элемента будет выведено следующим образом:
Benefits[edit]
Аналогично, вы можете получить элементы по их ID с помощью метода getElementById() , или же по их имени благодаря методу getElementBy Name ().
В следующем разделе вы будете использовать эти методы для скрапинга динамической веб-страницы.
Скрапинг динамической веб-страницы с помощью HtmlUnit
В этом разделе вы познакомитесь с возможностями HtmlUnit по заполнению форм и нажатию кнопок, заполнив и отправив форму для входа в систему. Вы также узнаете, как с помощью этого браузера перемещаться по веб-страницам.
Чтобы продемонстрировать динамический веб-скрапинг, давайте воспользуемся сайтом Hacker News. Вот как выглядит страница для входа в систему:

Следующий код — это HTML-форма для предыдущей страницы. Вы можете получить его, щелкнув правой кнопкой мыши по ярлыку Login Label и выбрав Inspect:
| username: | password: |
Чтобы заполнить форму с помощью 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)
После отправки формы и успешного входа в систему вы попадете на домашнюю страницу пользователя, где имя пользователя отображается в правом углу:

Элемент имени пользователя имеет идентификатор (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 < Listlinks = (List)(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)
На этом этапе у вас должна быть вторая страница в объекте 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 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) первой записи:
Этот код показывает, как заполнять форму, нажимать на кнопки и перемещаться по веб-страницам с помощью HtmlUnit.
Заключение
В этой статье вы изучили, как с помощью HtmlUnit выполнять сканирование статических и динамических веб-сайтов. Вы также узнали о некоторых расширенных возможностях HtmlUnit по скрапингу веб-страниц и преобразованию их в структурированные данные.
При работе с IDE, например IntelliJ IDEA, необходимо вручную находить атрибуты элементов, а затем с нуля прописывать использующие их функции веб-скрапинга. Для сравнения, Web Scraper IDE от Bright Data предоставляет легко обходящую любые гео-ограничения и блокировки надежную прокси-инфраструктуру, удобные функции веб-скрапинга и готовые шаблоны кода для популярных веб-сайтов. Эффективная прокси-инфраструктура — жизненно необходима для веб-скрапинга без каких-либо проблем с блокировкой IP-адресов и ограничением скорости. Использование прокси также помогает эмулировать пользователей с разной геолокацией.
Парсинг при помощи JAVA

Всем привет, данная статья является — маленьким туториалом, для примера были взяты XML данные с сайта Центр Банка.
В статье будут использованы — Spring Boot, PostgreSQL и Hibernate.
Ингредиенты:
- Создание Spring Boot проект, проще всего это сделать через Spring Initializr. (в качестве системы сборки будет использоваться Gradle).
- PostgreSQL (для комфортной работы, я использую — DBeaver).
- Postman — для отправки запросов на сервер.
Прошу пишите в комментариях возникшие проблемы, на всякий случай — вот мой git и ТГ
Начинаем с чистки ингредиентов:
Первостепенно нужно настроить build.gradle со всеми зависимостями.
buildscript < repositories < mavenCentral() >> plugins < id 'org.springframework.boot' version '2.5.2' id 'io.spring.dependency-management' version '1.0.11.RELEASE' id 'java' >group = 'com.parsing' version = '0.0.1-SNAPSHOT' sourceCompatibility = '11' configurations < compileOnly < extendsFrom annotationProcessor >> repositories < mavenCentral() >dependencies < implementation 'org.springframework.boot:spring-boot-starter-web:2.6.2' implementation 'org.springframework.boot:spring-boot-starter-data-jpa' implementation group: 'org.apache.httpcomponents', name: 'httpclient', version: '4.5' implementation 'org.postgresql:postgresql:42.3.1' implementation 'org.projectlombok:lombok:1.18.22' compileOnly 'org.projectlombok:lombok:1.18.22' annotationProcessor 'org.projectlombok:lombok:1.18.22' testImplementation 'org.springframework.boot:spring-boot-starter-test' >test
Теперь настройки application.properties
# connected to JDBC # setup spring.datasource spring.datasource.driver-class-name=org.postgresql.Driver spring.datasource.url=jdbc:postgresql://localhost:5432/change-me spring.datasource.username=change-me spring.datasource.password=change-me spring.datasource.hikari.connection-timeout=30000 spring.datasource.hikari.idle-timeout=60000 spring.datasource.hikari.maximum-pool-size=120 # setup hibernate spring.jpa.show-sql=true spring.jpa.generate-ddl=true spring.jpa.hibernate.ddl-auto=create-drop spring.jpa.properties.hibernate.show_sql=true spring.jpa.properties.hibernate.format_sql=true spring.jpa.properties.hibernate.proc.param_null_passing=true spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.PostgreSQL94Dialect
Хорошо, после настроек нашего проекта, давайте обговорим его структуру:

- controller — обрабатывает запрос пользователя;
- model — описывает модель данных;
- repository — логика работы с БД;
- service — основная бизнес логика проекта.
Нарезаем овощи и смешиваем:
Перед описанием моделей, посмотрим на фрагмент данных:
036 AUD 1 Австралийский доллар 55,1633 944 AZN 1 Азербайджанский манат 45,1385 826 GBP 1 Фунт стерлингов Соединенного королевства 103,9997
— Сначала опишем общую модель курса валют, так как по схеме XML видно, что у нас должен быть общий список элементов валют (ValCurs), внутри которого элементы (Valute)
@XmlRootElement(name = "ValCurs") @XmlAccessorType(XmlAccessType.FIELD) public class CourseDto implements Serializable < @XmlElement(name = "Valute") private Listvalute; @XmlElement(name = "Date") private LocalDate date; public List getValute() < return valute; >@Override public String toString() < return "CourseDtoCourseDtoOnce @Entity @XmlRootElement(name = "Valute") @XmlAccessorType(XmlAccessType.FIELD) public class CourseDtoOnce implements Serializable < @Id @GeneratedValue(strategy = GenerationType.AUTO) private Long id; @XmlElement(name = "NumCode") private String numCode; @XmlElement(name = "CharCode") private String charCode; @XmlElement(name = "Nominal") private int nominal; @XmlElement(name = "Name") private String name; @XmlElement(name = "Value") @JsonIgnore @Transient private String _Value; private double value; public String get_Value() < return _Value; >public void setValue(double value) < this.value = value; >@Override public String toString() < return "\n" + name + " (" + "numCode = " + numCode + ", " + "charCode = " + charCode + ", " + "nominal = " + nominal + ", " + "value = " + value + ")"; >>
Теперь для работы с БД, напишем repository
- Создадим интерфейс, который наследует от JpaRepository методы, для работы с записями в БД.
public interface CourseEntityRepository extends JpaRepository
Переходим к созданию бизнес логики приложения
- CourseClient - работа с внешним ресурсом, обработка и выдача результата.
@Component public class CourseClient < public static final String URL = "https://cbr.ru/scripts/XML_daily.asp?date_req=23/01/2022.xml"; final RestTemplate restTemplate = new RestTemplate(); public ListgetCourses() < CourseDto response = restTemplate.getForObject(URL, CourseDto.class); if (response != null) < response .getValute() .forEach(x ->< x.setValue(Double.parseDouble(x.get_Value().replace(",", "."))); System.out.println(x); >); return response.getValute(); > return null; > >
- CourseService - бизнес логика проекта.
@Service @RequiredArgsConstructor public class CourseService < private final CourseEntityRepository courseEntityRepository; private final CourseClient courseClient; public ListfindCourseInfo() < return courseEntityRepository.saveAll(courseClient.getCourses()); >>
Теперь создадим путь, для обращения к сервису из вне
- Пишем контроллер, который используется, для получения списка сохраненных в БД записей
@RestController @RequiredArgsConstructor public class CourseParsingController < private final CourseService courseService; /** * Возвращает список курсов валют * * @return список курсов валют */ @GetMapping(value = "/getCourse") public String getListInformation() < return courseService.findCourseInfo().toString(); >>
В конце класс, который собственно и запускает все наше приложение
ParsingProjectApplication
@SpringBootApplication public class ParsingProjectApplication < public static void main(String[] args) < SpringApplication.run(ParsingProjectApplication.class, args); >>
Мы сделали салат, теперь заправляем его
Логи запуска сервиса
. ____ _ __ _ _ /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \ ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \ \\/ ___)| |_)| | | | | || (_| | ) ) ) ) ' |____| .__|_| |_|_| |_\__, | / / / / =========|_|==============|___/=/_/_/_/ :: Spring Boot :: (v2.5.2) 2022-01-24 04:46:48.318 INFO 42654 --- [ main] com.ParsingProjectApplication : No active profile set, falling back to default profiles: default 2022-01-24 04:46:49.028 INFO 42654 --- [ main] .s.d.r.c.RepositoryConfigurationDelegate : Bootstrapping Spring Data JPA repositories in DEFAULT mode. 2022-01-24 04:46:49.174 INFO 42654 --- [ main] .s.d.r.c.RepositoryConfigurationDelegate : Finished Spring Data repository scanning in 142 ms. Found 1 JPA repository interfaces. 2022-01-24 04:46:49.455 INFO 42654 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 8080 (http) 2022-01-24 04:46:49.460 INFO 42654 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat] 2022-01-24 04:46:49.460 INFO 42654 --- [ main] org.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/9.0.48] 2022-01-24 04:46:49.514 INFO 42654 --- [ main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext 2022-01-24 04:46:49.514 INFO 42654 --- [ main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 1158 ms 2022-01-24 04:46:49.677 INFO 42654 --- [ main] o.hibernate.jpa.internal.util.LogHelper : HHH000204: Processing PersistenceUnitInfo [name: default] 2022-01-24 04:46:49.696 INFO 42654 --- [ main] org.hibernate.Version : HHH000412: Hibernate ORM core version 5.4.32.Final 2022-01-24 04:46:49.746 INFO 42654 --- [ main] o.hibernate.annotations.common.Version : HCANN000001: Hibernate Commons Annotations 2022-01-24 04:46:49.790 WARN 42654 --- [ main] com.zaxxer.hikari.HikariConfig : HikariPool-1 - idleTimeout has been set but has no effect because the pool is operating as a fixed size pool. 2022-01-24 04:46:49.790 INFO 42654 --- [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Starting. 2022-01-24 04:46:49.951 INFO 42654 --- [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Start completed. 2022-01-24 04:46:49.960 INFO 42654 --- [ main] org.hibernate.dialect.Dialect : HHH000400: Using dialect: org.hibernate.dialect.PostgreSQL94Dialect Hibernate: drop table if exists course_dto_once cascade 2022-01-24 04:46:50.236 WARN 42654 --- [ main] o.h.engine.jdbc.spi.SqlExceptionHelper : SQL Warning Code: 0, SQLState: 00000 2022-01-24 04:46:50.236 WARN 42654 --- [ main] o.h.engine.jdbc.spi.SqlExceptionHelper : table "course_dto_once" does not exist, skipping Hibernate: drop sequence if exists hibernate_sequence 2022-01-24 04:46:50.237 WARN 42654 --- [ main] o.h.engine.jdbc.spi.SqlExceptionHelper : SQL Warning Code: 0, SQLState: 00000 2022-01-24 04:46:50.237 WARN 42654 --- [ main] o.h.engine.jdbc.spi.SqlExceptionHelper : sequence "hibernate_sequence" does not exist, skipping Hibernate: create sequence hibernate_sequence start 1 increment 1 Hibernate: create table course_dto_once ( id int8 not null, char_code varchar(255), name varchar(255), nominal int4 not null, num_code varchar(255), value float8 not null, primary key (id) ) 2022-01-24 04:46:50.318 INFO 42654 --- [ main] o.h.e.t.j.p.i.JtaPlatformInitiator : HHH000490: Using JtaPlatform implementation: [org.hibernate.engine.transaction.jta.platform.internal.NoJtaPlatform] 2022-01-24 04:46:50.322 INFO 42654 --- [ main] j.LocalContainerEntityManagerFactoryBean : Initialized JPA EntityManagerFactory for persistence unit 'default' 2022-01-24 04:46:50.480 WARN 42654 --- [ main] JpaBaseConfiguration$JpaWebConfiguration : spring.jpa.open-in-view is enabled by default. Therefore, database queries may be performed during view rendering. Explicitly configure spring.jpa.open-in-view to disable this warning 2022-01-24 04:46:50.661 INFO 42654 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path '' 2022-01-24 04:46:50.668 INFO 42654 --- [ main] com.ParsingProjectApplication : Started ParsingProjectApplication in 2.682 seconds (JVM running for 3.006)
- Запускаем DBeaver, после запуска приложения, создастся таблица с полями:

- Запускаем Postman, прописываем в поле для url - http://localhost:8080/getCourse:

- Нажимаем синию кнопку "Send" и получаем в ответ данные о курсе валют, на 23/01/2022:

- Так же курсами валют заполнилась и БД:

Логи сервиса, после отправки запроса
. ____ _ __ _ _ /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \ ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \ \\/ ___)| |_)| | | | | || (_| | ) ) ) ) ' |____| .__|_| |_|_| |_\__, | / / / / =========|_|==============|___/=/_/_/_/ :: Spring Boot :: (v2.5.2) 2022-01-24 04:46:48.318 INFO 42654 --- [ main] com.ParsingProjectApplication : No active profile set, falling back to default profiles: default 2022-01-24 04:46:49.028 INFO 42654 --- [ main] .s.d.r.c.RepositoryConfigurationDelegate : Bootstrapping Spring Data JPA repositories in DEFAULT mode. 2022-01-24 04:46:49.174 INFO 42654 --- [ main] .s.d.r.c.RepositoryConfigurationDelegate : Finished Spring Data repository scanning in 142 ms. Found 1 JPA repository interfaces. 2022-01-24 04:46:49.455 INFO 42654 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 8080 (http) 2022-01-24 04:46:49.460 INFO 42654 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat] 2022-01-24 04:46:49.460 INFO 42654 --- [ main] org.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/9.0.48] 2022-01-24 04:46:49.514 INFO 42654 --- [ main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext 2022-01-24 04:46:49.514 INFO 42654 --- [ main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 1158 ms 2022-01-24 04:46:49.677 INFO 42654 --- [ main] o.hibernate.jpa.internal.util.LogHelper : HHH000204: Processing PersistenceUnitInfo [name: default] 2022-01-24 04:46:49.696 INFO 42654 --- [ main] org.hibernate.Version : HHH000412: Hibernate ORM core version 5.4.32.Final 2022-01-24 04:46:49.746 INFO 42654 --- [ main] o.hibernate.annotations.common.Version : HCANN000001: Hibernate Commons Annotations 2022-01-24 04:46:49.790 WARN 42654 --- [ main] com.zaxxer.hikari.HikariConfig : HikariPool-1 - idleTimeout has been set but has no effect because the pool is operating as a fixed size pool. 2022-01-24 04:46:49.790 INFO 42654 --- [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Starting. 2022-01-24 04:46:49.951 INFO 42654 --- [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Start completed. 2022-01-24 04:46:49.960 INFO 42654 --- [ main] org.hibernate.dialect.Dialect : HHH000400: Using dialect: org.hibernate.dialect.PostgreSQL94Dialect Hibernate: drop table if exists course_dto_once cascade 2022-01-24 04:46:50.236 WARN 42654 --- [ main] o.h.engine.jdbc.spi.SqlExceptionHelper : SQL Warning Code: 0, SQLState: 00000 2022-01-24 04:46:50.236 WARN 42654 --- [ main] o.h.engine.jdbc.spi.SqlExceptionHelper : table "course_dto_once" does not exist, skipping Hibernate: drop sequence if exists hibernate_sequence 2022-01-24 04:46:50.237 WARN 42654 --- [ main] o.h.engine.jdbc.spi.SqlExceptionHelper : SQL Warning Code: 0, SQLState: 00000 2022-01-24 04:46:50.237 WARN 42654 --- [ main] o.h.engine.jdbc.spi.SqlExceptionHelper : sequence "hibernate_sequence" does not exist, skipping Hibernate: create sequence hibernate_sequence start 1 increment 1 Hibernate: create table course_dto_once ( id int8 not null, char_code varchar(255), name varchar(255), nominal int4 not null, num_code varchar(255), value float8 not null, primary key (id) ) 2022-01-24 04:46:50.318 INFO 42654 --- [ main] o.h.e.t.j.p.i.JtaPlatformInitiator : HHH000490: Using JtaPlatform implementation: [org.hibernate.engine.transaction.jta.platform.internal.NoJtaPlatform] 2022-01-24 04:46:50.322 INFO 42654 --- [ main] j.LocalContainerEntityManagerFactoryBean : Initialized JPA EntityManagerFactory for persistence unit 'default' 2022-01-24 04:46:50.480 WARN 42654 --- [ main] JpaBaseConfiguration$JpaWebConfiguration : spring.jpa.open-in-view is enabled by default. Therefore, database queries may be performed during view rendering. Explicitly configure spring.jpa.open-in-view to disable this warning 2022-01-24 04:46:50.661 INFO 42654 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path '' 2022-01-24 04:46:50.668 INFO 42654 --- [ main] com.ParsingProjectApplication : Started ParsingProjectApplication in 2.682 seconds (JVM running for 3.006) 2022-01-24 04:52:00.787 INFO 42654 --- [nio-8080-exec-1] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring DispatcherServlet 'dispatcherServlet' 2022-01-24 04:52:00.788 INFO 42654 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : Initializing Servlet 'dispatcherServlet' 2022-01-24 04:52:00.788 INFO 42654 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : Completed initialization in 0 ms Австралийский доллар (numCode = 036, charCode = AUD, nominal = 1, value = 55.1633) Азербайджанский манат (numCode = 944, charCode = AZN, nominal = 1, value = 45.1385) Фунт стерлингов Соединенного королевства (numCode = 826, charCode = GBP, nominal = 1, value = 103.9997) Армянских драмов (numCode = 051, charCode = AMD, nominal = 100, value = 15.9539) Белорусский рубль (numCode = 933, charCode = BYN, nominal = 1, value = 29.8058) Болгарский лев (numCode = 975, charCode = BGN, nominal = 1, value = 44.4607) Бразильский реал (numCode = 986, charCode = BRL, nominal = 1, value = 14.1505) Венгерских форинтов (numCode = 348, charCode = HUF, nominal = 100, value = 24.35) Гонконгских долларов (numCode = 344, charCode = HKD, nominal = 10, value = 98.4699) Датская крона (numCode = 208, charCode = DKK, nominal = 1, value = 11.6842) Доллар США (numCode = 840, charCode = USD, nominal = 1, value = 76.6903) Евро (numCode = 978, charCode = EUR, nominal = 1, value = 86.9054) Индийских рупий (numCode = 356, charCode = INR, nominal = 10, value = 10.2985) Казахстанских тенге (numCode = 398, charCode = KZT, nominal = 100, value = 17.5815) Канадский доллар (numCode = 124, charCode = CAD, nominal = 1, value = 61.2102) Киргизских сомов (numCode = 417, charCode = KGS, nominal = 100, value = 90.427) Китайский юань (numCode = 156, charCode = CNY, nominal = 1, value = 12.0972) Молдавских леев (numCode = 498, charCode = MDL, nominal = 10, value = 42.4055) Норвежских крон (numCode = 578, charCode = NOK, nominal = 10, value = 86.796) Польский злотый (numCode = 985, charCode = PLN, nominal = 1, value = 19.2264) Румынский лей (numCode = 946, charCode = RON, nominal = 1, value = 17.5847) СДР (специальные права заимствования) (numCode = 960, charCode = XDR, nominal = 1, value = 107.5497) Сингапурский доллар (numCode = 702, charCode = SGD, nominal = 1, value = 56.9722) Таджикских сомони (numCode = 972, charCode = TJS, nominal = 10, value = 67.8976) Турецких лир (numCode = 949, charCode = TRY, nominal = 10, value = 57.1544) Новый туркменский манат (numCode = 934, charCode = TMT, nominal = 1, value = 21.9429) Узбекских сумов (numCode = 860, charCode = UZS, nominal = 10000, value = 70.7797) Украинских гривен (numCode = 980, charCode = UAH, nominal = 10, value = 27.0692) Чешских крон (numCode = 203, charCode = CZK, nominal = 10, value = 35.819) Шведских крон (numCode = 752, charCode = SEK, nominal = 10, value = 83.537) Швейцарский франк (numCode = 756, charCode = CHF, nominal = 1, value = 83.8787) Южноафриканских рэндов (numCode = 710, charCode = ZAR, nominal = 10, value = 50.6156) Вон Республики Корея (numCode = 410, charCode = KRW, nominal = 1000, value = 64.3418) Японских иен (numCode = 392, charCode = JPY, nominal = 100, value = 67.3165) Hibernate: select nextval ('hibernate_sequence') Hibernate: select nextval ('hibernate_sequence') Hibernate: select nextval ('hibernate_sequence') Hibernate: select nextval ('hibernate_sequence') Hibernate: select nextval ('hibernate_sequence') Hibernate: select nextval ('hibernate_sequence') Hibernate: select nextval ('hibernate_sequence') Hibernate: select nextval ('hibernate_sequence') Hibernate: select nextval ('hibernate_sequence') Hibernate: select nextval ('hibernate_sequence') Hibernate: select nextval ('hibernate_sequence') Hibernate: select nextval ('hibernate_sequence') Hibernate: select nextval ('hibernate_sequence') Hibernate: select nextval ('hibernate_sequence') Hibernate: select nextval ('hibernate_sequence') Hibernate: select nextval ('hibernate_sequence') Hibernate: select nextval ('hibernate_sequence') Hibernate: select nextval ('hibernate_sequence') Hibernate: select nextval ('hibernate_sequence') Hibernate: select nextval ('hibernate_sequence') Hibernate: select nextval ('hibernate_sequence') Hibernate: select nextval ('hibernate_sequence') Hibernate: select nextval ('hibernate_sequence') Hibernate: select nextval ('hibernate_sequence') Hibernate: select nextval ('hibernate_sequence') Hibernate: select nextval ('hibernate_sequence') Hibernate: select nextval ('hibernate_sequence') Hibernate: select nextval ('hibernate_sequence') Hibernate: select nextval ('hibernate_sequence') Hibernate: select nextval ('hibernate_sequence') Hibernate: select nextval ('hibernate_sequence') Hibernate: select nextval ('hibernate_sequence') Hibernate: select nextval ('hibernate_sequence') Hibernate: select nextval ('hibernate_sequence') Hibernate: insert into course_dto_once (char_code, name, nominal, num_code, value, id) values (?, ?, ?, ?, ?, ?) Hibernate: insert into course_dto_once (char_code, name, nominal, num_code, value, id) values (?, ?, ?, ?, ?, ?) Hibernate: insert into course_dto_once (char_code, name, nominal, num_code, value, id) values (?, ?, ?, ?, ?, ?) Hibernate: insert into course_dto_once (char_code, name, nominal, num_code, value, id) values (?, ?, ?, ?, ?, ?) Hibernate: insert into course_dto_once (char_code, name, nominal, num_code, value, id) values (?, ?, ?, ?, ?, ?) Hibernate: insert into course_dto_once (char_code, name, nominal, num_code, value, id) values (?, ?, ?, ?, ?, ?) Hibernate: insert into course_dto_once (char_code, name, nominal, num_code, value, id) values (?, ?, ?, ?, ?, ?) Hibernate: insert into course_dto_once (char_code, name, nominal, num_code, value, id) values (?, ?, ?, ?, ?, ?) Hibernate: insert into course_dto_once (char_code, name, nominal, num_code, value, id) values (?, ?, ?, ?, ?, ?) Hibernate: insert into course_dto_once (char_code, name, nominal, num_code, value, id) values (?, ?, ?, ?, ?, ?) Hibernate: insert into course_dto_once (char_code, name, nominal, num_code, value, id) values (?, ?, ?, ?, ?, ?) Hibernate: insert into course_dto_once (char_code, name, nominal, num_code, value, id) values (?, ?, ?, ?, ?, ?) Hibernate: insert into course_dto_once (char_code, name, nominal, num_code, value, id) values (?, ?, ?, ?, ?, ?) Hibernate: insert into course_dto_once (char_code, name, nominal, num_code, value, id) values (?, ?, ?, ?, ?, ?) Hibernate: insert into course_dto_once (char_code, name, nominal, num_code, value, id) values (?, ?, ?, ?, ?, ?) Hibernate: insert into course_dto_once (char_code, name, nominal, num_code, value, id) values (?, ?, ?, ?, ?, ?) Hibernate: insert into course_dto_once (char_code, name, nominal, num_code, value, id) values (?, ?, ?, ?, ?, ?) Hibernate: insert into course_dto_once (char_code, name, nominal, num_code, value, id) values (?, ?, ?, ?, ?, ?) Hibernate: insert into course_dto_once (char_code, name, nominal, num_code, value, id) values (?, ?, ?, ?, ?, ?) Hibernate: insert into course_dto_once (char_code, name, nominal, num_code, value, id) values (?, ?, ?, ?, ?, ?) Hibernate: insert into course_dto_once (char_code, name, nominal, num_code, value, id) values (?, ?, ?, ?, ?, ?) Hibernate: insert into course_dto_once (char_code, name, nominal, num_code, value, id) values (?, ?, ?, ?, ?, ?) Hibernate: insert into course_dto_once (char_code, name, nominal, num_code, value, id) values (?, ?, ?, ?, ?, ?) Hibernate: insert into course_dto_once (char_code, name, nominal, num_code, value, id) values (?, ?, ?, ?, ?, ?) Hibernate: insert into course_dto_once (char_code, name, nominal, num_code, value, id) values (?, ?, ?, ?, ?, ?) Hibernate: insert into course_dto_once (char_code, name, nominal, num_code, value, id) values (?, ?, ?, ?, ?, ?) Hibernate: insert into course_dto_once (char_code, name, nominal, num_code, value, id) values (?, ?, ?, ?, ?, ?) Hibernate: insert into course_dto_once (char_code, name, nominal, num_code, value, id) values (?, ?, ?, ?, ?, ?) Hibernate: insert into course_dto_once (char_code, name, nominal, num_code, value, id) values (?, ?, ?, ?, ?, ?) Hibernate: insert into course_dto_once (char_code, name, nominal, num_code, value, id) values (?, ?, ?, ?, ?, ?) Hibernate: insert into course_dto_once (char_code, name, nominal, num_code, value, id) values (?, ?, ?, ?, ?, ?) Hibernate: insert into course_dto_once (char_code, name, nominal, num_code, value, id) values (?, ?, ?, ?, ?, ?) Hibernate: insert into course_dto_once (char_code, name, nominal, num_code, value, id) values (?, ?, ?, ?, ?, ?) Hibernate: insert into course_dto_once (char_code, name, nominal, num_code, value, id) values (?, ?, ?, ?, ?, ?)
Вот и все, надеюсь, что у всех получилось повторить туториал с первого
раза, в будущем будет еще много интересного, всем спасибо.
- spring-boot
- Spring boot парсинг
- java парсинг
- парсинг xml
- парсинг json
- spring boot postgresql
- парсинг на java
- парсинг с помощью spring boot
- парсинг spring boot postgresql
- тестовое задание
Парсинг строк в Java


Перед программистами часто стоят задачи, решение которых не всегда очевидно. Одна из таких задач — парсинг строк. Он используется при чтении данных с консоли, файла и других источников. Большинство данных, которые передаются через интернет, тоже находятся в строчном виде. К сожалению, производить математические операции со строками невозможно. Поэтому, каждому программисту необходимо точно знать, как производить преобразование строки в число в Java. В строках могут содержаться различные числовые типы:
- byte;
- short;
- int;
- long;
- float;
- double.
Для извлечения из строки числового значения необходимого типа, нужно воспользоваться его классом-оберткой:
byte a = Byte.parseByte("42"); short b = Short.parseShort("42"); int c = Integer.parseInt("42"); long d = Long.parseLong("42"); float e = Float.parseFloat("42.0"); double f = Double.parseDouble("42.0");
- Если в метод передать строку, которая не является целочисленным значением, будет получена ошибка java.lang.NumberFormatException , которая будет сообщать, что полученная строка не является целочисленным значением.
- NumberFormatException произойдет и в том случае, если переданная строка будет содержать пробел.
- parseInt() — может работать с отрицательными числами. Для этого строка должна начинаться с символа “-”.
- parseInt() — не может распарсить строку, если числовое значение выходит за пределы типы int (-2147483648 .. 2147483647).
