вырезать данные со страницы, которая использует поиск .JSF, используя R - PullRequest
0 голосов
/ 08 октября 2018

Я пытаюсь получить информацию из Швейцарского административного суда для исследований в университете .

URL: https://jurispub.admin.ch/publiws/pub/search.jsf, и я заинтересован в данных, перечисленных втаблица, которая появляется после выполнения поиска.

К сожалению, файла .robots.txt нет.Тем не менее, все указы на этой веб-странице открыты для общественности.

У меня есть некоторый опыт работы с html-скребком, и я просмотрел следующие ресурсы: http://www.rladiesnyc.org/post/scraping-javascript-websites-in-r/

https://www.r -bloggers.com / web-scraping-javascript-rendered-sites /

Очистка веб-сайта, содержащего код JS / jquery с R

Мой подход

Я думаю использовать PhantomJS дляСкачайте html-версию страницы и используйте rvest для очистки загруженного веб-сайта.

Мои проблемы

Однако я не знаю, как получить URLстраницы, которая появляется, если «пустой» поиск выполняется по https://jurispub.admin.ch/publiws/ (нажав «suchen» без какой-либо информации в маске поиска), что дает 57 294 результатов.Я думал о чем-то вроде:

GET(url = "https://jurispub.admin.ch/publiws/",
      query=list(searchQuery="")) 

Однако, это не работает.

Более того, я не знаю, чтобы позволить PhantomJS "нажать" на маленькую кнопку со стрелкой, чтобы загрузить следующуюстр.

Ответы [ 2 ]

0 голосов
/ 12 октября 2018

Получить работу Selenium может быть проще (в долгосрочной перспективе), чем пытаться выяснить нюансы, необходимые для получения и поддержки сеансов:

library(wdman) # for managing the Selenium server d/l
library(RSelenium) # for getting a connection to the Selenium server
library(seleniumPipes) # for better navigation & scraping idioms

Это должно установить jar и запустить сервер:

selServ <- selenium() 

Нам нужен порт #, поэтому сделайте это и найдите порт в сообщениях

selServ$log()$stderr 

Теперь нам нужно подключиться к нему, и нам нужно использовать порт # из ^^,В моем случае это было 4567:

sel <- remoteDr(browserName = "chrome", port = 4567) 

Теперь перейдите по основному URL:

sel %>% 
  go("https://jurispub.admin.ch/publiws/pub/search.jsf")

Запустите процесс очистки, нажав кнопку первоначальной отправки

sel %>% 
  findElement("name", "form:searchSubmitButton") %>%  # find the submit button 
  elementClick() # click it

Теперь мы на следующей странице, поэтому возьмите столбцы, как в примере с другим ответом:

sel %>% 
  getPageSource() %>% # like read_html()
  html_node("table.iceDatTbl") -> dtbl  # this is the data table

html_nodes(dtbl, xpath=".//td[@class='iceDatTblCol1']/a") %>% # get doc ids
  html_text()

html_nodes(dtbl, xpath=".//td[@class='iceDatTblCol2']/a[contains(@href, 'publiws')]") %>% 
  html_attr("href") # get pdf links

И т.д.… для других столбцов, как в другом ответе

Теперьполучите информацию о нумерации страниц, как в другом ответе:

sel %>% 
  getPageSource() %>% 
  html_node("span.iceOutFrmt") %>% 
  html_text() # the total items / pagination info

Найдите кнопку следующей страницы, нажмите ее и перейдите на следующую страницу

sel %>%
  findElement("xpath", ".//img[contains(@src, 'arrow-next')]/../../a") %>% 
  elementClick() # go to next page

Повторите приведенный выше захват таблицы.Вы должны поместить все это в цикл for, основанный на общей информации о предметах / нумерации страниц, как в предложении другого ответа.

Когда вы все закончите, не забудьте позвонить:

selServ$stop()
0 голосов
/ 08 октября 2018

Добавление внешних зависимостей - это хорошо, но на самом деле это должно быть последнее средство (IMO).

Если вы не знакомы с представлением «Инструменты разработчика» в браузерах, пожалуйста, поинтересуйтесь этим перед тем, как приступить к ответу.Перед тем, как перейти на страницу поиска, вам нужно запустить его в новом сеансе браузера, чтобы действительно увидеть поток.

GET не работал, потому что это HTML-форма и <form> элементы используют POST запросы (которые отображаются как XHR запросов в большинстве панелей инструментов разработчика Network).Тем не менее, это плохо созданный сайт, который слишком сложен для собственной пользы ( почти хуже, чем сайт Microsoft SharePoint), и существует некоторая начальная настройка состояния, когда вы переходите на начальную страницу поиска и поддерживаетсяво всем остальном потоке.

Я использовал curlconverter для сортировки запросов POST XHR.Для этого TLDR щелкните правой кнопкой мыши на любом запросе POST XHR, найдите пункт меню «Копировать как cURL» и выберите его.Затем, оставив это в буфере обмена, следуйте инструкциям на README и страницах справочника curlconverter, чтобы вернуть действительные функции httr.Я не могу обещать, что проведу вас через эту часть или отвечу на curlconverter вопросы здесь.

В любом случае, чтобы получить httr / curl, чтобы сохранить некоторые файлы cookie для вас и чтобы получить ключевую переменную сеанса, вам нужно будет передавать каждый вызов, который мы должны начать с нового сеанса R, и «прокачать» процесс очистки с помощью GET для основного URL-адреса поиска:

library(stringi) # Iprefer this for extracting matched strings
library(rvest)
library(httr)

primer <- httr::GET("https://jurispub.admin.ch/publiws/pub/search.jsf")

Теперь нам нужно извлечь строку сеанса в javascript на этой странице:

httr::content(primer, as="text") %>%
  stri_match_first_regex("session: '([[:alnum:]]+)'") %>% 
  .[,2] -> ice_session

Теперь мы притворяемся, что отправляем форму.Все эти скрытые переменные могут не понадобиться, но это то, что отправил браузер.Я обычно стараюсь сводить их к тому, что нужно, но это ваш проект, поэтому получайте удовольствие, если хотите:

httr::POST(
  url = "https://jurispub.admin.ch/publiws/block/send-receive-updates",
  body = list(
    `$ice.submit.partial` = "true",
    ice.event.target = "form:_id64",
    ice.event.captured = "form:_id63first",
    ice.event.type = "onclick",
    ice.event.alt = "false",
    ice.event.ctrl = "false",
    ice.event.shift = "false",
    ice.event.meta = "false",
    ice.event.x = "51", 
    ice.event.y = "336",
    ice.event.left = "true",
    ice.event.right = "false",
    form = "form", 
    icefacesCssUpdates = "",
    `form:_id63` = "first",
    `form:_idcl` = "form:_id63first",
    ice.session = ice_session,
    ice.view = "1", 
    ice.focus = "form:_id63first",
    rand = "0.38654987905551663\\n\\n"
  ),
  encode = "form"
) -> first_pg

Теперь, когда у нас есть первая страница, нам нужны данные с нее.Я не собираюсь решать это полностью, но вы должны быть в состоянии экстраполировать из того, что ниже.Запрос POST возвращает XML, который javascript на странице превращает в ужасно выглядящую таблицу.Мы собираемся извлечь эту таблицу:

httr::content(first_pg) %>% 
  xml_find_first("//updates/update/content") %>% 
  xml_text() %>% 
  read_html() -> pg_tbl

data_tbl <- html_node(pg_tbl, xpath=".//table[contains(., 'Dossiernummer')]")

Однако, это ужасное использование HTML (у программистов не было УДАЛЕНО подсказки, как правильно делать веб-вещи), и вы не можете просто использовать html_table()на нем (и вы бы не захотели в любом случае, так как вы, вероятно, хотите ссылки на PDF-файлы или что-то нет).Итак, мы можем извлекать столбцы по желанию:

html_nodes(data_tbl, xpath=".//td[1]/a") %>% 
  html_text()
## [1] "A-3930/2013" "D-7885/2009" "E-5869/2012" "C-651/2011"  "F-2439/2017" "D-7416/2009"
## [7] "D-838/2011"  "C-859/2011"  "E-1927/2017" "E-2606/2011"

html_nodes(data_tbl, xpath=".//td[2]/a") %>% 
  html_attr("href")
##  [1] "/publiws/download?decisionId=0002b1f8-ea53-40bb-8e38-402d9f3fdfa9"
##  [2] "/publiws/download?decisionId=0002da8f-306e-4395-8eed-0b168df8634b"
##  [3] "/publiws/download?decisionId=0003ec45-50be-45b2-8a56-5c0d866c2603"
##  [4] "/publiws/download?decisionId=000508c2-c852-4aef-bc32-3385ddbbe88a"
##  [5] "/publiws/download?decisionId=0006fbb9-228a-4bdc-ac8c-52db67df3b34"
##  [6] "/publiws/download?decisionId=0008a971-6795-434d-90d4-7aeb1961606b"
##  [7] "/publiws/download?decisionId=00099619-519c-4c8f-9cea-a16ed9ab9fd8"
##  [8] "/publiws/download?decisionId=0009ac38-f2b0-4733-b379-05682473b5d9"
##  [9] "/publiws/download?decisionId=000a4e0f-b2a2-483b-a49f-6ad12f4b7849"
## [10] "/publiws/download?decisionId=000be307-37b1-4d46-b651-223ceec9e533"

Мыть, полоскать, повторять для любых других столбцов, но вам может потребоваться проделать некоторую работу, чтобы получить их так же хорошо, и это упражнение осталось для вас (т.е. я не буду отвечать на вопросы о нем).

И вы захотите узнать, где вы находитесь в процессе очистки, поэтому нам нужно взять эту строку в нижней части таблицы:

html_node(pg_tbl, xpath=".//span[contains(@class, 'iceOutFrmt')]") %>% 
  html_text()
## [1] "57,294 Entscheide gefunden, zeige 1 bis 10. Seite 1 von 5,730. Resultat sortiert nach: Relevanz"

Синтаксис этого в # результата и на той странице, на которой вы находитесь, является упражнением, оставленным читателю.

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

Следующая страница (первая итерация):

httr::POST(
  url = "https://jurispub.admin.ch/publiws/block/send-receive-updates",
  body = list(
    `$ice.submit.partial` = "true",
    ice.event.target = "form:_id67",
    ice.event.captured = "form:_id63next",
    ice.event.type = "onclick",
    ice.event.alt = "false",
    ice.event.ctrl = "false",
    ice.event.shift = "false",
    ice.event.meta = "false",
    ice.event.x = "330", 
    ice.event.y = "559",
    ice.event.left = "true",
    ice.event.right = "false",
    form = "", 
    icefacesCssUpdates = "",
    `form:_id63` = "next",
    `form:_idcl` = "form:_id63next",
    iceTooltipInfo = "tooltip_id=form:resultTable:7:tt_ps; tooltip_src_id=form:resultTable:7:_id57; tooltip_state=hide; tooltip_x=846; tooltip_y=433; cntxValue=",
    ice.session =  ice_session,
    ice.view = "1", 
    ice.focus = "form:_id63next",
    rand = "0.17641832791084566\\n\\n"
  ),
  encode = "form"
) -> next_pg

httr::content(next_pg) %>% 
  xml_find_first("//updates/update/content") %>% 
  xml_text() %>% 
  read_html() -> pg_tbl

data_tbl <- html_node(pg_tbl, xpath=".//table[contains(., 'Dossiernummer')]")

html_nodes(data_tbl, xpath=".//td[1]/a") %>% 
  html_text()
##  [1] "D-4059/2011" "D-4389/2006" "E-4019/2006" "D-4291/2008" "E-5642/2012" "E-7752/2010"
##  [7] "D-7010/2014" "D-1551/2013" "C-7715/2010" "E-3187/2013"

html_nodes(data_tbl, xpath=".//td[2]/a") %>% 
  html_attr("href")
##  [1] "/publiws/download?decisionId=000bfd02-4da5-4bb2-a5d0-e9977bf8e464"
##  [2] "/publiws/download?decisionId=000e2be1-6da8-47ff-b707-4a3537320a82"
##  [3] "/publiws/download?decisionId=000fa961-ecb4-47d2-8ca3-72e8824c2c6b"
##  [4] "/publiws/download?decisionId=0010a089-4f19-433e-b106-6d75833fae9a"
##  [5] "/publiws/download?decisionId=00111bfc-3522-4a32-9e7a-fa2d9f171427"
##  [6] "/publiws/download?decisionId=00126b65-b345-4988-826b-b213080caa45"
##  [7] "/publiws/download?decisionId=00127944-5c88-43f6-9ef1-3c822288b0c7"
##  [8] "/publiws/download?decisionId=00135a17-f1eb-4b61-9171-ac1d27fd3910"
##  [9] "/publiws/download?decisionId=0014c6ea-c229-4129-bbe0-7411d34d9743"
## [10] "/publiws/download?decisionId=00167998-54d2-40a5-b02b-0c4546ac4760"

html_node(pg_tbl, xpath=".//span[contains(@class, 'iceOutFrmt')]") %>% 
  html_text()
## [1] "57,294 Entscheide gefunden, zeige 11 bis 20. Seite 2 von 5,730. Resultat sortiert nach: Relevanz"

Обратите внимание, что значения столбцов различны иТекст прогресса отличается.Также обратите внимание, что нам повезло, и у некомпетентных программистов на сайте действительно было «следующее» событие против нас, заставляющих нас выяснить числа нумерации страниц и координаты X / Y.

Следующая страница (второй и последний пример итерации):

httr::POST(
  url = "https://jurispub.admin.ch/publiws/block/send-receive-updates",
  body = list(
    `$ice.submit.partial` = "true",
    ice.event.target = "form:_id67",
    ice.event.captured = "form:_id63next",
    ice.event.type = "onclick",
    ice.event.alt = "false",
    ice.event.ctrl = "false",
    ice.event.shift = "false",
    ice.event.meta = "false",
    ice.event.x = "330", 
    ice.event.y = "559",
    ice.event.left = "true",
    ice.event.right = "false",
    form = "", 
    icefacesCssUpdates = "",
    `form:_id63` = "next",
    `form:_idcl` = "form:_id63next",
    iceTooltipInfo = "tooltip_id=form:resultTable:7:tt_ps; tooltip_src_id=form:resultTable:7:_id57; tooltip_state=hide; tooltip_x=846; tooltip_y=433; cntxValue=",
    ice.session =  ice_session,
    ice.view = "1", 
    ice.focus = "form:_id63next",
    rand = "0.17641832791084566\\n\\n"
  ),
  encode = "form"
) -> next_pg

httr::content(next_pg) %>% 
  xml_find_first("//updates/update/content") %>% 
  xml_text() %>% 
  read_html() -> pg_tbl

data_tbl <- html_node(pg_tbl, xpath=".//table[contains(., 'Dossiernummer')]")

html_nodes(data_tbl, xpath=".//td[1]/a") %>% 
  html_text()
##  [1] "D-3974/2010" "D-5847/2009" "D-4241/2015" "E-3043/2010" "D-602/2016"  "C-2065/2008"
##  [7] "D-2753/2007" "E-2446/2010" "C-1124/2015" "B-7400/2006"

html_nodes(data_tbl, xpath=".//td[2]/a") %>% 
  html_attr("href")
##  [1] "/publiws/download?decisionId=00173ef1-2900-49d4-b7d3-39246e552a70"
##  [2] "/publiws/download?decisionId=001a344c-86b7-4f32-97f7-94d30669a583"
##  [3] "/publiws/download?decisionId=001ae810-300d-4291-8fd0-35de720a6678"
##  [4] "/publiws/download?decisionId=001c2025-57dd-4bc6-8bd6-eedbd719a6e3"
##  [5] "/publiws/download?decisionId=001c44ba-e605-455d-9609-ed7dffb17adc"
##  [6] "/publiws/download?decisionId=001c6040-4b81-4137-a6ee-bad5a5019e71"
##  [7] "/publiws/download?decisionId=001d0811-a5c2-4856-aef3-51a44f7f2b0e"
##  [8] "/publiws/download?decisionId=001dbf61-b1b8-468d-936e-30b174a8bec9"
##  [9] "/publiws/download?decisionId=001ea85a-0765-4a1f-9b81-3cecb9f36b31"
## [10] "/publiws/download?decisionId=001f2e34-9718-4ef7-a60c-e6bbe208003b"

html_node(pg_tbl, xpath=".//span[contains(@class, 'iceOutFrmt')]") %>% 
  html_text()
## [1] "57,294 Entscheide gefunden, zeige 21 bis 30. Seite 3 von 5,730. Resultat sortiert nach: Relevanz"

В идеале вы должны заключить POST в функцию, которую вы можете вызвать, и вернуть кадры данных, которые вы можете rbind или bind_rows, в большой кадр данных.

Если вы сделали это далеко, альтернативой является использование RSelenium для организации щелчков по страницам в селекторе «следующая страница» и получения HTML-кода назад (таблица все равно будет ужасна, и вам нужно будет использовать таргетинг на столбцы илинекоторая другая магия выбора HTML, чтобы получить полезную информацию из-за вышеупомянутых неумелых программистов).RSelenium вводит внешнюю зависимость, которая - как вы увидите, если будете выполнять поиск по SO - многим пользователям R трудно работать, особенно в такой же унылой унаследованной операционной системе, как Windows.Если вы можете запустить Selenium и RSelenium работать с ним, в долгосрочной перспективе может быть проще, если все вышеперечисленное кажется пугающим (в какой-то момент вам все равно придется прогуливать Developer Tools, так что вышеописанное может стоить боли в любом случае)и вам понадобится целевой объект HTML-селектора для различных кнопок для Selenium).

Я бы серьезно избегал фантомов, поскольку теперь он находится в состоянии «наилучшего усилия», и вам придется выяснить, каксделать вышеупомянутое с JavaScript против R.

...