среда, 9 декабря 2015 г.

Что нам стоит сайт распарсить. Основы webdriver API / Хабрахабр

Что нам стоит сайт распарсить. Основы 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

Комментариев нет:

Отправить комментарий