Граббер сайта на OwnLang за пять минут
от aNNiMON
В этой статье я покажу как можно быстро добавить любую Java библиотеку для последующего взаимодействия с ней в OwnLang (Desktop), как вызывать Java-код, на примере библиотеки Jsoup, а также сохранять данные в json и SQLite.
Подключаем Java-библиотеки
Начну, пожалуй, с самого нуля - загружаем последнюю версию (на момент написания статьи это OwnLang Desktop 1.3.0) со страницы релизов на GitHub. Распаковав zip-архив, мы видим две папки:
- modules - папка дополнительных модулей
- libs - папка Java-библиотек.
Мы будем работать с библиотекой Jsoup, поэтому качаем последнюю версию и копируем jar-файл в папку libs. Теперь при запуске интерпретатора, мы сможем работать с классами этой библиотеки.
Выбираем цель
Для примера будем получать информацию об Android-библиотеках с сайта https://android-arsenal.com/
Прежде, чем мы начнём, необходимо взглянуть на HTML-код страницы, чтобы узнать, где находятся интересующие нас данные.
Как видно, краткая информация о библиотеке находится в блоке <div class="project-info">.
На странице https://try.jsoup.org/ можно попробовать получить данные онлайн и подобрать селекторы.
Импортируем страницу:
Получаем:
Список библиотек: .project-info
Название: .project-info .title a:first-child
Описание: .project-info .desc
Ссылка на готовый пример из try jsoup
Работаем с Java-кодом из OwnLang
Для взаимодействия с Java-кодом воспользуемся модулем java. Для начала проверим, доступна ли нам библиотека Jsoup:
Если всё нормально, при запуске мы получим строку org.jsoup.Jsoup.
Парсим страницу при помощи Jsoup
Только что мы загрузили класс Jsoup и теперь можем вызывать все его статические методы. Давайте получим код главной страницы:
Сайт блокирует подключение, если не указан User-Agent, поэтому пришлось притвориться браузером Google Chrome.
Теперь получим список из названий библиотек на странице:
BreadcrumbsView
android-easy-crash-handle
Twitter Kit for Android
Вместо вывода на консоль, давайте соберём всю информацию в массив:
Получили:
[{desc=A customizable Android view..., url=/details/1/4475, tag=Bread Crumbs, title=BreadcrumbsView}, {desc=This is An Easy Android Crash Handle Library..., url=/details/1/4474, tag=Crash Reports, title=android-easy-crash-handle}, ...]
Сохраняем данные в json
Вместо вывода на консоль, можно сохранить данные в виде файла в формате json:
Работаем с базой данных SQLite
Можно поступить иначе, сохранять в базу данных, разбив данные на отдельные таблицы. В нашем случае таблиц можно сделать две: теги (категории) и библиотеки. OwnLang поддерживает MySQL и SQLite, но добавив драйверы других БД, можно расширить этот список. Мы же воспользуемся СУБД SQLite.
Подключаем модуль для работы с базами данных:
Создаём подключение к файлу:
Создаём структуру таблиц:
Выполняем специальную SQLite команду, говорящую, что сейчас будет множественная вставка данных (batch insert):
Чтобы не создавать каждый раз Statement для добавления данных, будем переиспользовать PreparedStatement. Заодно получим безопасность типизации (это в sqlite-то? ):
Перебираем список библиотек и заносим данные по своим таблицам:
Выполняем множественное добавление и закрываем PreparedStatement:
Выполняем специальную SQLite команду, говорящую, что множественная вставка данных завершена:
Выведем количество добавленных библиотек:
Наконец, закроем соединение с БД:
Вот и всё. Запустив программу, мы увидим:
Libs: 30Теперь можно посмотреть данные в sqlitebrowser или где-нибудь ещё:
Добавляем парсинг страниц
Всё это время мы парсили только одну страницу, так что обернём код в функцию parsePage и будем вызывать для каждой страницы:
После трёхминутного парсинга 148 страниц, получаем сообщение:
Libs: 4399и 835 кб файла базы данных.
Полный код:
android_arsenal.own
Подключаем Java-библиотеки
Начну, пожалуй, с самого нуля - загружаем последнюю версию (на момент написания статьи это OwnLang Desktop 1.3.0) со страницы релизов на GitHub. Распаковав zip-архив, мы видим две папки:
- modules - папка дополнительных модулей
- libs - папка Java-библиотек.
Мы будем работать с библиотекой Jsoup, поэтому качаем последнюю версию и копируем jar-файл в папку libs. Теперь при запуске интерпретатора, мы сможем работать с классами этой библиотеки.
Выбираем цель
Для примера будем получать информацию об Android-библиотеках с сайта https://android-arsenal.com/
Прежде, чем мы начнём, необходимо взглянуть на HTML-код страницы, чтобы узнать, где находятся интересующие нас данные.
Как видно, краткая информация о библиотеке находится в блоке <div class="project-info">.
На странице https://try.jsoup.org/ можно попробовать получить данные онлайн и подобрать селекторы.
Импортируем страницу:
Получаем:
Список библиотек: .project-info
Название: .project-info .title a:first-child
Описание: .project-info .desc
Ссылка на готовый пример из try jsoup
Работаем с Java-кодом из OwnLang
Для взаимодействия с Java-кодом воспользуемся модулем java. Для начала проверим, доступна ли нам библиотека Jsoup:
- use "java"
- Jsoup = newClass("org.jsoup.Jsoup")
- print Jsoup.name
Если всё нормально, при запуске мы получим строку org.jsoup.Jsoup.
Парсим страницу при помощи Jsoup
Только что мы загрузили класс Jsoup и теперь можем вызывать все его статические методы. Давайте получим код главной страницы:
- use "java"
- Jsoup = newClass("org.jsoup.Jsoup")
- doc = Jsoup.connect("https://android-arsenal.com/")
- .userAgent("Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.143 Safari/537.36")
- .get()
- print doc
Теперь получим список из названий библиотек на странице:
- elements = doc.select(".pi").toArray()
- for element : elements {
- aElement = element.select(".title a:first-child")
- title = aElement.text()
- println title
- }
android-easy-crash-handle
Twitter Kit for Android
Вместо вывода на консоль, давайте соберём всю информацию в массив:
- elements = doc.select(".pi").toArray()
- use "std"
- libs = []
- for element : elements {
- aElement = element.select(".title a:first-child")
- title = trim(aElement.text())
- // Пропускаем рекламу
- if (title == "") continue
- lib = {}
- lib.title = title
- lib.url = aElement.attr("href")
- lib.tag = trim(element.select(".tags").text())
- lib.desc = trim(element.select(".desc").text())
- // Добавляем в массив
- libs += lib
- }
- println libs
Получили:
[{desc=A customizable Android view..., url=/details/1/4475, tag=Bread Crumbs, title=BreadcrumbsView}, {desc=This is An Easy Android Crash Handle Library..., url=/details/1/4474, tag=Crash Reports, title=android-easy-crash-handle}, ...]
Сохраняем данные в json
Вместо вывода на консоль, можно сохранить данные в виде файла в формате json:
- use "json"
- use "files"
- f = fopen("android-libraries.json", "w")
- writeText(f, jsonencode(libs))
- flush(f)
- fclose(f)
Работаем с базой данных SQLite
Можно поступить иначе, сохранять в базу данных, разбив данные на отдельные таблицы. В нашем случае таблиц можно сделать две: теги (категории) и библиотеки. OwnLang поддерживает MySQL и SQLite, но добавив драйверы других БД, можно расширить этот список. Мы же воспользуемся СУБД SQLite.
Подключаем модуль для работы с базами данных:
- use "jdbc"
Создаём подключение к файлу:
- conn = getConnection("jdbc:sqlite:android-arsenal.db")
Создаём структуру таблиц:
- st = conn.createStatement()
- st.executeUpdate("drop table if exists tags")
- st.executeUpdate(
- "create table tags (
- id integer primary key,
- name string
- )")
- st.executeUpdate("drop table if exists libs")
- st.executeUpdate(
- "create table libs (
- id integer primary key,
- title string,
- url string,
- desc string,
- tag integer
- )")
Выполняем специальную SQLite команду, говорящую, что сейчас будет множественная вставка данных (batch insert):
- st.execute("begin")
Чтобы не создавать каждый раз Statement для добавления данных, будем переиспользовать PreparedStatement. Заодно получим безопасность типизации (это в sqlite-то? ):
- stTags = conn.prepareStatement("insert into tags(id, name) values(?, ?)")
- stLibs = conn.prepareStatement("insert into libs(id, title, url, desc, tag) values(?, ?, ?, ?, ?)")
Перебираем список библиотек и заносим данные по своим таблицам:
- tagids = {}
- tagsIndex = 0
- libsIndex = 0
- for lib : libs {
- libsIndex++
- // Теги
- if !arrayKeyExists(lib.tag, tagids) {
- // Добавляем новый тег
- tagsIndex++
- stTags.setInt(1, tagsIndex)
- stTags.setString(2, lib.tag)
- stTags.addBatch()
- tagids[lib.tag] = tagsIndex
- }
- // Добавляем библиотеку
- stLibs.setInt(1, libsIndex)
- stLibs.setString(2, lib.title)
- stLibs.setString(3, lib.url)
- stLibs.setString(4, lib.desc)
- stLibs.setString(5, tagids[lib.tag])
- stLibs.addBatch()
- }
Выполняем множественное добавление и закрываем PreparedStatement:
- stTags.executeBatch()
- stLibs.executeBatch()
- stTags.close()
- stLibs.close()
Выполняем специальную SQLite команду, говорящую, что множественная вставка данных завершена:
- st = conn.createStatement()
- st.execute("end")
Выведем количество добавленных библиотек:
- rs = st.executeQuery("select count(*) from libs")
- println "Libs: " + rs.getInt(1)
Наконец, закроем соединение с БД:
- conn.close()
Вот и всё. Запустив программу, мы увидим:
Libs: 30Теперь можно посмотреть данные в sqlitebrowser или где-нибудь ещё:
Добавляем парсинг страниц
Всё это время мы парсили только одну страницу, так что обернём код в функцию parsePage и будем вызывать для каждой страницы:
- libs = []
- page = 1
- while (true) {
- println "Page " + page
- pageLibs = parsePage(page)
- if (length(pageLibs) == 0) break
- libs <<= pageLibs
- page++
- sleep(1000)
- }
После трёхминутного парсинга 148 страниц, получаем сообщение:
Libs: 4399и 835 кб файла базы данных.
Полный код:
android_arsenal.own