При работе с RSelenium нередко случается так, что скрипт скрапера пытается обратится к элементу страницы, который ещё не сгенерирован. Необходимо выждать паузу, дать JavaScript завершить формирование элемента и лишь затем извлекать его. Как этого добиться? Я знаю три способа.

Пусть RSelenium загружен и драйвер (по имени объекта remoteDriver) уже создан и открыт:

library(RSelenium)
startServer()
rd <- remoteDriver(remoteServerAddr = "localhost",
                   port = 4444,
                   browserName="firefox")
rd$open()

Приступим...

Способ 1: пауза на поиск элемента

rd$setImplicitWaitTimeout(milliseconds = 10000)

Метод setImplicitWaitTimeout() объекта remoteDriver устанавливает время, которое драйвер будет ожидать поиска элементов страницы. Если разыскивается один элемент, то драйвер будет ждать пока объект не будет найден или пока не истечёт указанное время задержки (по умолчанию -- 10 секунд). Если разыскивается несколько элементов, то драйвер дожидается, пока не будет найден последний из элементов или, опять-таки, пока не закончится лимит времени, отведенного на поиск.

Установка неявного времени задержки действует только на методы findElement() и findElements(), так что другие операции (например, сохранение содержимого страницы) выполняются без проволочек. Это удобно, поскольку можно установить время задержки один раз, глобально, а не добавлять каждый раз перед критическими по времени выполнения фрагментами кода.

Если выход из поиска элементов осуществляется по истечении времени задержки, то возвращается пустой список найденных элементов.

Важно: если метод setImplicitWaitTimeout() не вызывался ни разу, то драйвер будет считать что время задержки на поиск составляет 0ms.

Способ 2: пауза при загрузке страницы

rd$setTimeout(type = "page load", milliseconds = 10000)

Метод setTimeout() управляет настройкой времени ожидания окончания нескольких типов операций

  • type: "script" -- ожидание на выполнение скрипта, "implicit" -- корректировка неявного времени ожидания (см. предыдущий способ) и "page load" -- ожидание загрузки страницы. По умолчанию установлен тип события "page load".
  • milliseconds: время ожидания в миллисекундах.

Если за установленное время событие, заданное в type, не завершится, то будет выдано сообщение об ошибке.

Удобнее всего использовать setTimeout() один раз, перед загрузкой страницы (navigate())

Способ 3: просто подождать

Sys.sleep(1)

Если проблема не связана ни с один из указанных выше событий, то остаётся просто приостановить выполнение R-скрипта (в примере -- на 1 секунду).

В качестве практического примера использования описанных способов рассмотрим поиск сообщений о защитах по заданной специальности на сайте ВАК. Здесь устанавливается время, отведенное на поиски элемента, и делается пауза в ожидании, пока не сработает команда, запущенная нажатием кнопки "Следующая страница".

spec_names <- "01.02.04"    # номер специальности
btime <- "01.01.2016"       # начало периода
etime <- "31.12.2016"       # конец периода

library("RSelenium")
startServer()
rd <- remoteDriver(remoteServerAddr = "localhost",
                   port = 4444,
                   browserName="firefox")
rd$open()
rd$setImplicitWaitTimeout(milliseconds = 10000)
rd$navigate("http://vak.ed.gov.ru/dis-list")

spec <- rd$findElement(using = "xpath", ".//*[@id='filter_def']/select[1]")

begin <- rd$findElement(using = "xpath", ".//*[@id='filter_def']/input[1]")
begin$clearElement()
begin$sendKeysToElement( list(btime,"\uE007") )

end <- rd$findElement(using = "xpath", ".//*[@id='filter_def']/input[2]")
end$clearElement()
end$sendKeysToElement( list(etime,"\uE007") )

links <- list()             # список ссылок на страницы с объявлениями о защите

spec$sendKeysToElement( list(spec_names,"\uE007") )

repeat {

  res <- rd$findElements(using = "xpath", ".//*[@id='filtred']/table//a")

  if (length(res) > 0)  {
    cur <- sapply(res, function(x){x$getElementAttribute("href")})
    links <- append(links,cur)
    cur <- list()
  }

  next_btn <- rd$findElement(using = "xpath", ".//*[@id='filtred']/button[3]")

  if (!next_btn$isElementDisplayed()[[1]]) break
  next_btn$clickElement()

  Sys.sleep(1)

}


Комментарии

comments powered by Disqus