Что нам стоит сайт распарсить. Основы webdriver API
Поиск жилья, информации о товарах, вакансий, знакомств, сравнение товаров фирмы с конкурентами, исследование отзывов в сети.В интернет опубликовано много полезной информации и умение извлекать данные поможет в жизни и работе. Научимся получать информацию с помощью webdriver API. В публикации приведу два примера, код которых доступен на github. В конце статьи скринкаст про то, как программа управляет браузером.
Программа или скрипт с помощью вебдрайвера управляет вашим браузером — выполняет ввод текста, переход по ссылкам, щелчок по элементу, извлекает данные из страницы и её элементов, делает скриншоты сайта и т.п.
Для работы webdriver нужно два компонента: браузер/сервер протокола и клиентская часть в виде библиотеки для вашего языка программирования.
Вы можете использовать webdriver API из разных языков программирования и виртуальных машин: официальные клиенты webdriver есть для C#, Ruby, Python, Javascript(Node), а так же клиенты от сообщества для Perl, Perl 6, PHP, Haskell, Objective-C, Javascript, R, Dart, Tcl.
На данный момент Webdriver является стандартом W3C, над которым все еще идет работа. Изначально Webdriver API появился в проекте selenium для целей тестирования, в результате эволюции Selenium-RC API.
В качестве сервера используется отдельный процесс «понимающий» язык протокола. Этот процесс управляет вашим браузером. Есть следующие драйвера:
- AndroidDriver
- ChromeDriver
- FirefoxDriver
- InternetExplorerDriver
- SafariDriver
Из этого списка выделяются два драйвера:
- HtmlUnitDriver — является оберткой для библиотеки HtmlUnit-эмулятора браузера, которая работает в том же java процессе, что и клиентская часть
- PhantomJSDriver — это браузер на основе webkit без графической части (headless) и драйвер в одном процессе с сервером.
Минимум теории для дальнейшей работы. Последовательность действий в клиенте
Итак, наш выбор phantomjs. Это полноценный браузер, который управляется по протоколу webdriver. Можно запускать множество его процессов одновременно, не требуется графическая подсистема, внутри полноценно исполняется javascript (в контраст с ограничениями htmlunit). Если писать сценарии на javascript и передавать его как параметр при старте, то phantomJS может их выполнять и без вебдрайвер протокола и даже доступна отладка с помощью другого браузера.
Описанное ниже относится по большей части к API для java/groovy. В клиентах других языков список функций и параметров должен быть похож.
Получаем сервер с вебдрайвером.
String phantomJsPath = PhantomJsDowloader.getPhantomJsPath()
Загружает phantomjs из репозитария maven, распаковывает и возвращает путь к этому браузеру. Для использования нужно подключить к проекту библиотеку из maven: com.github.igor-suhorukov:phantomjs-runner:1.1.
Этот шаг можно пропустить, если вы предварительно установили в локальную файловую систему вебдрайвер для вашего браузера.
Создаем клиент, соединяемся с сервером
WebDriver driver = new PhantomJSDriver(settings)
Конфигурирует порт для взаимодействию по webdriver протоколу, запускает процесс phantomjs и подключается к нему.
Открываем в браузере нужную страницу
driver.get(url)
Открывает в браузере страницу для заданного адреса.
Получаем интересующу нас информацию
WebElement leftmenu = driver.findElement(By.id("leftmenu")) List<WebElement> linkList = leftmenu.findElements(By.tagName("a"))
У экземпляра драйвера driver и у элемента, полученного из него, есть два полезных метода: findElement, findElements. Первый возвращает элемент или бросает исключение NoSuchElementException, если элемент не найден. Второй — возвращает коллекцию элементов.
Элементы можно выбирать по следующим запросам org.openqa.selenium.By:
- id
- name
- tagName
- xpath
- className
- cssSelector
- linkText
- partialLinkText
Я буду активно использовать id, tagName и xpath. Для не знакомых с xpath — рекомендую разобраться на примерах или по статьям, а только потом перейти к чтению спецификации.
Выполняем действия с элементами на странице и со страницей
С элементом можно вытворять следующее:
- menuItem.click() — посылает элементу событие клик
- inputField.sendKeys(«blah-blah») — посылает элементу события нажатия клавиш
- formButton.submit() — отправляет данные формы, вызывая событие submit
driver.getScreenshotAs(type)
Делает снимок окна браузера. Полезным дополнением к стандартной функции снимка рекомендую библиотеку aShot — она позволяет делать снимок только определенного элемента в окне и позволяет сравнивать элементы как изображения.
Скриншоты можно получить как:
- OutputType.BASE64 — строку в этом формате, можно например встроить в HTML embedded картинку
- OutputType.BYTES — массив байт и вертись с ним как умеешь
- OutputType.FILE — временный файл, для многих инструментов самый удобный способ
Закрываем соединение с браузером
driver.quit()
Закрывает соединение по протоколу и в нашем случае останавливает процесс phantomjs.
Пример 1: Гуляем groovy скриптом по профилям социальной сети
Запустим команду:
java -jar groovy-grape-aether-2.4.5.1.jar crawler.groovy http://??.com/catalog.php
Ссылки на необходимые для запуска файлы:
Скрипт в консоле напечатает путь к html файлу, что создал на основе информации из социальной сети. В странице вы увидите имя пользователя, момент последнего посещения соцсети и скриншот всей страницы пользователя.
Зачем запускать груви скрипт с помощью groovy-grape-aether-2.4.5.1
crawler.groovy
package com.github.igorsuhorukov.phantomjs @Grab(group='commons-io', module='commons-io', version='2.2') import org.apache.commons.io.IOUtils @Grab(group='com.github.detro', module='phantomjsdriver', version='1.2.0') import org.openqa.selenium.* import org.openqa.selenium.phantomjs.PhantomJSDriver import org.openqa.selenium.phantomjs.PhantomJSDriverService import org.openqa.selenium.remote.DesiredCapabilities @Grab(group='com.github.igor-suhorukov', module='phantomjs-runner', version='1.1') import com.github.igorsuhorukov.phantomjs.PhantomJsDowloader public class Crawler { public static final java.lang.String USER_AGENT = "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2228.0 Safari/537.36" public static void run(String baseUrl) { def phantomJsPath = PhantomJsDowloader.getPhantomJsPath() def DesiredCapabilities settings = new DesiredCapabilities() settings.setJavascriptEnabled(true) settings.setCapability("takesScreenshot", true) settings.setCapability("userAgent", com.github.igorsuhorukov.phantomjs.Crawler.USER_AGENT) settings.setCapability(PhantomJSDriverService.PHANTOMJS_EXECUTABLE_PATH_PROPERTY, phantomJsPath) def WebDriver driver = new PhantomJSDriver(settings) def randomUrl = null def lastVisited=null def name=null boolean pass=true while (pass){ try { randomUrl = getUrl(driver, baseUrl) driver.get(randomUrl) def titleElement = driver.findElement(By.id("title")) lastVisited = titleElement.findElement(By.id("profile_time_lv")).getText() name = titleElement.findElement(By.tagName("a")).getText() pass=false } catch (NoSuchElementException e) { System.out.println(e.getMessage()+". Try again.") } } String screenshotAs = driver.getScreenshotAs(OutputType.BASE64) File resultFile = File.createTempFile("phantomjs", ".html") OutputStreamWriter streamWriter = new OutputStreamWriter(new FileOutputStream(resultFile), "UTF-8") IOUtils.write("""<html><head><meta http-equiv="content-type" content="text/html; charset=UTF-8"></head><body> <p>${name}</p><p>${lastVisited}</p> <img alt="Embedded Image" src="data:image/png;base64,${screenshotAs}"></body> </html>""", streamWriter) IOUtils.closeQuietly(streamWriter) println "html ${resultFile} created" driver.quit(); } static String getUrl(WebDriver driver, String baseUrl) { driver.get(baseUrl) def elements = driver.findElements(By.xpath("//div[@id='content']//a")) def element = elements.get((int) Math.ceil(Math.random() * elements.size())) String randomUrl = element.getAttribute("href") randomUrl.contains("catalog") ? getUrl(driver, randomUrl) : randomUrl } } Crawler.run(this.args.getAt(0))
Хорошо знающие груви, заметят что Geb более подходящее решение. Но поскольку оно прячет за своим DSL всю работу с webdriver, то в наших учебных целях Geb не подходит. Из эстетических же соображений я с вами соглашусь!
Пример 2: Извлекаем данные о проектах из java-source программой на java
Пример доступен тут. Для запуска нужна java8, так как используются стримы и try-with-resources.
git clone https://github.com/igor-suhorukov/java-webdriver-example.git mvn clean package -Dexec.args="http://java-source.net"
В этом примере использую xpath и axis для извлечения информации из страницы. Как пример, фрагмент класса Project.
WebElement main = driver.findElement(By.id("main")); name = main.findElement(By.tagName("h3")).getText(); description = main.findElement(By.xpath("//h3/following-sibling::table/tbody/tr/td[1]")).getText(); link = main.findElement(By.xpath("//td[text()='HomePage']/following-sibling::*")).getText(); license = main.findElement(By.xpath("//td[text()='License']/following-sibling::*")).getText();
Часть извлеченных из сайта данных. Файла projects.xml - результат работы программы
Вот так этот же пример работает с драйвером ChromeDriver (org.seleniumhq.selenium:selenium-chrome-driver:2.48.2). В отличии от PhantomJS в этом случае видно что происходит во время запуска программы: переход по ссылкам, отрисовка страницы.
Вывод
Webdriver API можно использовать из разных языков программирования. Написать скрипт или программу для управления браузером и извлечения информации из страниц достаточно просто: данные из страницы удобно получать по Id тега, CSS селектору или XPath выражению. Возможно делать снимки страницы и отдельных элементов на ней. На основе примеров и документации можно разработать сценарии почти любой сложности для работы с сайтом. Для разработки и отладки лучше использовать обычный браузер и вебдрайвер для него. Для полностью автоматической работы лучше подходит PhantomJS.
Удачи в извлечении открытой и полезной информации из веб!
Sent from my iPhone
Комментариев нет:
Отправить комментарий