scraping asp javascript разбитые таблицы за поиском с R

Я пытаюсь вытащить контент на https://www.askebsa.dol.gov/epds/default.asp либо с помощью rvest либо с помощью RSelenium но не нахождением инструкций, когда страница javascript начинается с окна поиска? было бы здорово просто получить весь этот контент в простой CSV файл.

после этого вытащить данные из отдельных заявок, например https://www.askebsa.dol.gov/mewaview/View/Index/6219, возможно, но я также ценю чистую рекомендацию для этого. Спасибо

Ответ 1

Чтобы получить результаты, вам нужно будет заполнить форму и отправить ее. Вы можете найти имена URL и полей, проверив html.

url <- "https://www.askebsa.dol.gov/epds/m1results.asp"

post_data <- list(
    m1year = 'ALL',         # Year
    m1company = '',         # Name of MEWA (starts with)
    m1ein = '',             # EIN
    m1state = 'ALL',        # State of MEWA Headquarters
    m1coverage = 'ALL',     # State(s) where MEWA offers coverage
    m1filingtype = 'ALL',   # Type of filing
    cmdSubmitM1 = 'Search',
    # hidden fields
    auth = 'Y', 
    searchtype = 'Q', 
    sf = 'EIN', 
    so = 'A'
)

Теперь мы можем отправить форму и собрать ссылки. Мы можем очистить ссылки с помощью этой table.table.table-condensed td a селекторов. table.table.table-condensed td a.

html <- read_html(POST(url, body = post_data, encode = "form"))
links <- html_nodes(html, 'table.table.table-condensed td a') %>% html_attr("href") 
links <- paste0("https://www.askebsa.dol.gov", links) 

Это создает все ссылки первой страницы.

Проверяя HTTP-трафик, я заметил, что следующая страница загружается, отправив ту же форму с дополнительными полями (m1formid, allfilings, page). Мы можем получить следующие страницы, увеличив значение страницы в цикле.

library(httr)
library(rvest)

url <- "https://www.askebsa.dol.gov/epds/m1results.asp"
post_data <- list(
    m1year='ALL', m1company='', m1ein='', m1state='all', 
    m1coverage='all', m1filingtype='ALL', cmdSubmitM1 = 'Search',
    auth='Y', searchtype='Q', sf='EIN', so='A', 
    m1formid='', allfilings='', page=1
)
links = list()

while (TRUE) {
    html <- read_html(POST(url, body = post_data, encode = "form"))
    page_links <- html_nodes(html, 'table.table.table-condensed td a') %>% html_attr("href") %>% paste0("https://www.askebsa.dol.gov/", .) 
    links <- c(links, page_links)
    last <- html_text(tail(html_nodes(html, 'div.textnorm > a'), n=2)[1])
    if (last != 'Last') {
        break
    }
    post_data['page'] <- post_data[['page']] + 1
}

print(links)

Для второй части вопроса я предполагаю, что целью является выбор элементов формы и их значений. Вы можете сделать это, выбрав все div.question-inline и следующий тег code для каждого элемента.

library(rvest)

url <- "https://www.askebsa.dol.gov/mewaview/View/Index/6219"
nodes <- html_nodes(read_html(url), 'div.question-inline, div.question')
data <- list()

for (i in nodes) {
    n = trimws(html_text(html_node(i, xpath='./text()')))

    if (length(html_nodes(i, 'code')) == 0) {
        text <- html_nodes(i, xpath = '../address/code/text()')
        v <- paste(trimws(text), collapse = '\r\n')
    } else {
        v <- html_text(html_nodes(i, 'code'))
    }
    data[[n]] = v
}

print(data)

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

Ответ 2

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

Вот мой подход -

# open a html-session
web_session <- html_session("https://www.askebsa.dol.gov/epds/default.asp")
# get the form
test_search <- html_form(read_html("https://www.askebsa.dol.gov/epds/default.asp"))[[2]]

# set the required values for fields such as company_name, ein_number etc
# pass that info and submit the form - here i am getting an error 
# it cannot recognize the 'search button' name 
# if that is resolved it should work
set_values(test_search, 'm1company' = "Bend", 'm1ein' = '81-6268978' ) %>%
  submit_form(web_session, ., submit = "cmdSubmitM1") %>%
  read_html(.) -> some_html

Если я получу время, я постараюсь сделать еще несколько исследований и вернуться к вам. Я нашел пару учебных пособий и ответы на похожие темы здесь и здесь. Они немного стары, но все же полезны.

Для второй части это проще, поскольку у вас нет динамических элементов. Я смог получить все адреса в форме с помощью "селектор-гаджет" и скопировать все имена узлов в html_nodes().

# read the file and save it into a nested list
test_file_with_address <- read_html("https://www.askebsa.dol.gov/mewaview/View/Index/6219")

# copy paste all the css node names and get the text from the html file
test_file_with_address %>%
  html_nodes(".border-top:nth-child(19) code , .border-top:nth-child(18) code , .border-top:nth-child(14) code , .border-top:nth-child(13) code , .border-top:nth-child(12) code , .border-top:nth-child(11) code , .border-top:nth-child(9) code , .section-header+ .border-top code
") %>% html_text()

[1] "\r\n                Bend Chamber of Commerce Benefit Plan and Trust for Wood Products Employers\r\n                777 N.W. Wall Street, Suite 200\r\n                Bend,  OR  97703\r\n                \r\n                "
 [2] "(541) 382-3221"                                                                                                                                                                                                                
 [3] "81-6268978"                                                                                                                                                                                                                    
 [4] "501"                                                                                                                                                                                                                           
 [5] "\r\n                Bend Chamber of Commerce\r\n                777 N.W. Wall Street, Suite 200\r\n                Bend,  OR  97703\r\n                \r\n                "                                                   
 [6] "(541) 382-3221"                                                                                                                                                                                                                
 [7] "93-0331932"                                                                                                                                                                                                                    
 [8] "\r\n                Katy Brooks\r\n                Bend Chamber of Commerce\r\n                777 N.W. Wall Street, Suite 200\r\n                Bend,  OR  97703\r\n                \r\n                "                    
 [9] "(541) 382-3221"                                                                                                                                                                                                                
[10] "[email protected]"                                                                                                                                                                                                          
[11] "\r\n                Deb Oster\r\n                Scott Logging/Scott Transportation\r\n                400 S.W. Bluff Drive, #101\r\n                Bend,  OR  97702\r\n                \r\n                "                 
[12] "(541) 536-3636"                                                                                                                                                                                                                
[13] "[email protected]"                                                                                                                                                                                                       
[14] "\r\n                Karen Gibbons\r\n                Allen & Gibbons Logging\r\n                P.O. Box 754\r\n                Canyonville,  OR  97417\r\n                \r\n                "                               
[15] "(541) 839-4294"                                                                                                                                                                                                                
[16] "[email protected]"                                                                                                                                                                                                   
[17] "\r\n                Cascade East Benefits\r\n                dba Johnson Benefit Planning\r\n                777 N.W. Wall Street, Suite 100\r\n                Bend,  OR  97703\r\n                \r\n                "      
[18] "(541) 382-3571"                                                                                                                                                                                                                
[19] "[email protected]"                                                                                                                                                                                                
[20] "93-1130374"                                                                                                                                                                                                                    
[21] "\r\n                PacificSource Health Plans\r\n                P.O. Box 7068\r\n                Springfield,  OR  97475-0068\r\n                \r\n                "                                                       
[22] "(541) 686-1242"                                                                                                                                                                                                                
[23] "[email protected]"                                                                                                                                                                                              
[24] "93-0245545"                                                                                                                                                                                                                    
[25] "\r\n                PacificSource Health Plans\r\n                P.O. Box 7068\r\n                Springfield,  OR  97475-0068\r\n                \r\n                "                                                       
[26] "(541) 686-1242"                                                                                                                                                                                                                
[27] "[email protected]"                                                                                                                                                                                             
[28] "93-0245545"                                                                                                                                                                                                                    
[29] "N/A"

Для этого требуется еще несколько regex чтобы очистить их и получить их в data.frame но основные строительные блоки там есть.

Ответ 3

Вот пример использования RSelenium для получения ссылок на отдельные заявки. Остальное должно быть простым, как только вы получите ссылки. Вы можете перемещаться по этим URL-адресам с помощью rvest (как вы это делали ранее) и анализировать контент с помощью инструментов обработки строк, таких как stringr. Во второй части было бы оптимистично ожидать систематической структуры во всех формах. Попробуйте потратить некоторое время на создание определенного regular expression чтобы вытащить то, что вам нужно, из полученного текста.

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

Дополнительная информация: RSelenium: Основы

# devtools::install_github("ropensci/RSelenium")
library(RSelenium)

# launch a remote driver 
driver <- rsDriver(browser=c("chrome"))
remDr <- driver[["client"]]

# select an URL
url <- "https://www.askebsa.dol.gov/epds/default.asp"

# navigate to the URL
remDr$navigate(url)

# choose year - option[2] corresponds to 2017
year <- remDr$findElements(using = 'xpath',  '//*[@id="m1year"]/option[2]')
year[[1]]$clickElement()

# choose company
company <- remDr$findElements(using = 'xpath',  '//*[@id="m1company"]')
company[[1]]$sendKeysToElement(list("Bend"))

# enter ein
ein <- remDr$findElements(using = 'xpath',  '//*[@id="m1ein"]')
ein[[1]]$sendKeysToElement(list("81-6268978"))

# sumbit the form to get the results
submit <- remDr$findElements(using = 'xpath',  '//*[@id="cmdSubmitM1"]')
submit[[1]]$clickElement()

# get the total number of results
num_of_results <- remDr$findElements(using = 'xpath',  '//*[@id="block-system-main"]/div/div/div/div/div/div[1]/form/table[1]/tbody/tr/td/div/b[1]')
n <- as.integer(num_of_results[[1]]$getElementText()[[1]])

# loop through results and print the links
for(i in 1:n) {
  xpath <- paste0('//*[@id="block-system-main"]/div/div/div/div/div/div[1]/form/table[3]/tbody/tr[', i + 1, ']/td[1]/a')
  link <- remDr$findElements('xpath', xpath)
  print(link[[1]]$getElementAttribute('href'))
}

# [[1]]
# [1] "https://www.askebsa.dol.gov/mewaview/View/Index/5589"
# 
# [[1]]
# [1] "https://www.askebsa.dol.gov/mewaview/View/Index/6219"

Обратите внимание: если вы не сузите свой поиск, вы получите более 50 результатов и, следовательно, более одной страницы результатов. В этом случае вам потребуются дополнительные корректировки кода (структура xpath внутри цикла может измениться, вам может потребоваться перейти на дополнительные страницы, цикл должен быть ограничен 50 итерациями и т.д.).

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