Парсинг и скрапинг данных на Kotlin: почему это стоит вашего внимания
Когда речь заходит о сборе данных из интернета, в голову чаще всего приходит Python с его BeautifulSoup и Scrapy. Но что, если ваш стек — это JVM, а душа лежит к лаконичному и безопасному коду? Знакомьтесь: Kotlin. Этот язык не просто «ещё один Java», а мощный инструмент для парсинга и скрапинга данных, который позволяет писать чистый, производительный и асинхронный код без лишнего шума.
В этой статье мы разберём, как превратить Kotlin в настоящий комбайн для сбора информации. Вы узнаете, какие библиотеки использовать, как не попасть под бан сайта и как обрабатывать тысячи страниц без головной боли. Поехали!
Что такое парсинг и скрапинг данных на Kotlin
Прежде чем нырять в код, давайте расставим точки над i.
- Веб-скрапинг (scraping) — это массовое извлечение данных с веб-страниц. Вы загружаете HTML, разбираете его и забираете нужные кусочки: цены, заголовки, описания товаров.
- Парсинг (parsing) — более узкое понятие. Это разбор структуры данных (HTML, JSON, XML) в удобный для программы вид. Парсинг — часть скрапинга.
В Kotlin парсинг и скрапинг данных реализуются элегантно благодаря функциональным возможностям, строгой типизации и превосходной поддержке корутин. Вы получаете код, который легко читать, тестировать и поддерживать.
Библиотеки для парсинга и скрапинга данных на Kotlin
Выбор библиотеки — 50% успеха. Вот основной джентльменский набор для Kotlin:
- Jsoup — золотой стандарт парсинга HTML. Работает на JVM, понимает даже кривую вёрстку.
- Ktor Client — асинхронный HTTP-клиент от JetBrains. Идеально для связки с корутинами.
- kotlinx.serialization — сериализация JSON без боли.
- OkHttp — если нужен более низкоуровневый контроль над запросами.
- HtmlUnit — для сайтов, которые рендерят контент через JavaScript (редко, но бывает).
В этой статье мы сфокусируемся на связке Ktor + Jsoup + kotlinx.serialization — это даёт максимум контроля и скорости.
Практический пример: парсинг данных с сайта на Kotlin
Лучший способ понять скрапинг данных на Kotlin — написать реальный скрипт. Представим, что мы собираем информацию о книгах с сайта-агрегатора (например, с хабра или книжного магазина). Наша цель: получить название, автора и цену каждой книги.
Шаг 1. Настройка проекта
Создайте новый Gradle-проект и добавьте зависимости в build.gradle.kts:
dependencies {
implementation("io.ktor:ktor-client-core:2.3.7")
implementation("io.ktor:ktor-client-cio:2.3.7")
implementation("org.jsoup:jsoup:1.17.2")
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.2")
implementation("ch.qos.logback:logback-classic:1.4.14")
}
Шаг 2. Пишем HTTP-клиент для загрузки страниц
Используем Ktor Client для асинхронной загрузки. Корутины позволяют делать это без блокировки потока.
import io.ktor.client.*
import io.ktor.client.request.*
import io.ktor.client.statement.*
import kotlinx.coroutines.runBlocking
class PageLoader(private val client: HttpClient) {
suspend fun loadPage(url: String): String {
val response: HttpResponse = client.get(url)
return response.bodyAsText()
}
}
fun main() = runBlocking {
val client = HttpClient {
// можно настроить таймауты, юзер-агенты и т.д.
}
val loader = PageLoader(client)
val html = loader.loadPage("https://example-books.com/catalog")
println(html.take(500)) // выведем первые 500 символов для проверки
client.close()
}
Шаг 3. Парсим HTML с помощью Jsoup
Теперь самое интересное — извлекаем данные. Допустим, каждая книга лежит в блоке <div class="book-item">.
import org.jsoup.Jsoup
import org.jsoup.nodes.Document
import org.jsoup.select.Elements
data class Book(
val title: String,
val author: String,
val price: Double
)
fun parseBooks(html: String): List<Book> {
val doc: Document = Jsoup.parse(html)
val items: Elements = doc.select("div.book-item")
return items.map { item ->
val title = item.select("h2.book-title").text()
val author = item.select("p.author").text()
val priceText = item.select("span.price").text()
.replace("₽", "").trim()
val price = priceText.toDoubleOrNull() ?: 0.0
Book(title, author, price)
}
}
Обратите внимание: Jsoup понимает CSS-селекторы, так что вы можете гибко настраивать извлечение. Это основа любого парсинга HTML на Kotlin.
Шаг 4. Собираем всё вместе и сохраняем результат
Добавим сохранение в JSON с помощью kotlinx.serialization.
import kotlinx.serialization.Serializable
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
@Serializable
data class BookDto(
val title: String,
val author: String,
val price: Double
)
fun List<Book>.toDtoList(): List<BookDto> = this.map {
BookDto(it.title, it.author, it.price)
}
fun saveToJson(books: List<BookDto>, filename: String) {
val json = Json { prettyPrint = true }
val jsonString = json.encodeToString(books)
java.io.File(filename).writeText(jsonString)
}
// В main:
fun main() = runBlocking {
val client = HttpClient()
val loader = PageLoader(client)
val html = loader.loadPage("https://example-books.com/catalog")
val books = parseBooks(html)
val dtos = books.toDtoList()
saveToJson(dtos, "books.json")
println("Сохранено ${books.size} книг")
client.close()
}
Вот так, меньше чем за 50 строк кода, мы реализовали полноценный скрапинг данных на Kotlin. Но это только начало!
Продвинутые техники: пагинация, многопоточность и обход блокировок
Пагинация: собираем все страницы
Редкий сайт показывает все данные на одной странице. Обычно есть пагинация. Давайте научимся её обрабатывать.
suspend fun scrapeAllPages(baseUrl: String, loader: PageLoader