Как я могу очистить CGI-Bin с помощью rvest и R?

Я пытаюсь использовать rvest для очистки результатов веб-формы, которые появляются в cgi-bin. Однако, когда я запускаю скрипт, я получаю 0 результатов в пределах 200 миль. Ниже мой код. Я ценю любые отзывы и помощь. Главный веб-сайт - http://www.zmax.com/, на котором есть поле поиска, запускающее cgi -bin.

library(rvest); 
library(purrr) ;
library(plyr) ;
library(dplyr) ;

x<-read_html('http://www.nearestoutlet.com/cgi-bin/smi/findsmi.pl') 

y<-x%>% html_node('table')%>% html_table(fill=true)

также я пробовал

y<-x%>% 
html_node('td div td, p')
%>% html_text()

Я не уверен, где я ошибаюсь, возвращая данные из формы.


person Turbogoon    schedule 14.10.2017    source источник


Ответы (1)


Как ни странно, ни основной сайт, ни провайдер, которого они используют для поиска торговых точек, не предотвращают очистку T&C или REP. ¯\_(ツ)_/¯

Вам действительно следует ознакомиться с инструментами разработчика браузера, поскольку вы могли бы видеть, что основной сайт делает POST HTTP-запрос к сайту поиска, а не GET запросы, которые обычно выполняются браузерами и которые read_html() делает. Вот что вам нужно сделать, чтобы получить успешные запросы (мы выберем почтовый индекс рядом с вами):

library(httr)
library(rvest)

POST(
  url = "http://www.nearestoutlet.com/cgi-bin/smi/findsmi.pl", 
  body = list(zipcode = "48127"), 
  encode = "form"
) -> res

res - это объект httr response, и обычно можно просто сделать:

content(res, as="parsed")

чтобы подготовить проанализированный объект для анализа XML / HTML. Но на этом сайте есть странные проблемы с кодировкой (по крайней мере, для меня), заставляющие нас делать:

content(res, as="raw") %>% read_html() -> pg

Вы должны cat(as.character(pg)) увидеть, насколько уродливым является HTML. Это вложенные таблицы, но не в хорошем смысле. Записи, которые вы видите, содержат все <tr> элементов без <table> разрывов. К счастью? в каждом из этих <tr> элементов есть только единичные <td> элементов. Итак, мы можем захватить их все одним махом, выбрав правильный <table>:

rows <- html_nodes(pg, "table[width='300'] > tr > td")
rows
## {xml_nodeset (60)}
##  [1] <td width="300" height="19" bgcolor="#8B0101"><p align="left"><font face="Tahoma" color="#FFFFFF" style="font-size: 11px"><b>O\u0092REILLY AUTO PARTS</b></fo ...
##  [2] <td width="300" height="2"><font face="Tahoma" style="font-size: 11px">6938 NORTH TELEGRAPH ROAD</font></td>
##  [3] <td width="300" height="2"><font face="Tahoma" style="font-size: 11px">Dearborn Heights, MI  48127</font></td>
##  [4] <td width="300" height="2"><font face="Tahoma" style="font-size: 11px">(313) 792-9134</font></td>
##  [5] <td width="300" height="2"><font face="Tahoma" style="font-size: 11px"><a href="#" onclick="window.open('http://maps.google.com/maps?q=6938+NORTH+TELEGRAPH+R ...
##  [6] <td width="300" height="6"></td>
##  [7] <td width="300" height="19" bgcolor="#8B0101"><p align="left"><font face="Tahoma" color="#FFFFFF" style="font-size: 11px"><b>Advance Auto Parts</b></font></p ...
##  [8] <td width="300" height="2"><font face="Tahoma" style="font-size: 11px">8120 North Telegraph Road</font></td>
##  [9] <td width="300" height="2"><font face="Tahoma" style="font-size: 11px">Dearborn Heights, MI  48127</font></td>
## [10] <td width="300" height="2"><font face="Tahoma" style="font-size: 11px">(313) 528-4920</font></td>
## [11] <td width="300" height="2"><font face="Tahoma" style="font-size: 11px"><a href="#" onclick="window.open('http://maps.google.com/maps?q=8120+North+Telegraph+R ...
## [12] <td width="300" height="6"></td>
## [13] <td width="300" height="19" bgcolor="#8B0101"><p align="left"><font face="Tahoma" color="#FFFFFF" style="font-size: 11px"><b>Pep Boys</b></font></p></td>
## [14] <td width="300" height="2"><font face="Tahoma" style="font-size: 11px">8955 TELEGRAPH RD</font></td>
## [15] <td width="300" height="2"><font face="Tahoma" style="font-size: 11px">Redford, MI  48239</font></td>
## [16] <td width="300" height="2"><font face="Tahoma" style="font-size: 11px">(313) 532-5750</font></td>
## [17] <td width="300" height="2"><font face="Tahoma" style="font-size: 11px"><a href="#" onclick="window.open('http://maps.google.com/maps?q=8955+TELEGRAPH+RD+Redf ...
## [18] <td width="300" height="6"></td>
## [19] <td width="300" height="19" bgcolor="#8B0101"><p align="left"><font face="Tahoma" color="#FFFFFF" style="font-size: 11px"><b>O\u0092REILLY AUTO PARTS</b></fo ...
## [20] <td width="300" height="2"><font face="Tahoma" style="font-size: 11px">27207 PLYMOUTH ROAD</font></td>
## ...

Существует много подходов, которые можно использовать для создания фрейма данных из этого беспорядка. Один простой заключается в использовании того факта, что заголовки магазинов имеют заданный цвет фона, а другие - нет. Это делает код немного хрупким, но мы можем сделать его менее хрупким, просто проверив наличие цвета фона. Зачем нам это вообще нужно? Что ж, нам нужно отметить начало и конец записей, и один простой способ сделать это - использовать тот факт, что мы можем cumsum() логический вектор, зная, что это FALSE == 0. Почему это имеет значение? Таким образом мы можем создать столбец неявной группировки:

data_frame(
  record = !is.na(html_attr(rows, "bgcolor")),
  text = html_text(rows, trim=TRUE)
) %>% 
  mutate(record = cumsum(record)) -> xdf
#3 # A tibble: 60 x 2
#3    record                        text
#3     <int>                       <chr>
#3  1      1  "O\u0092REILLY AUTO PARTS"
#3  2      1   6938 NORTH TELEGRAPH ROAD
#3  3      1 Dearborn Heights, MI  48127
#3  4      1              (313) 792-9134
#3  5      1                0 miles away
#3  6      1                            
#3  7      2          Advance Auto Parts
#3  8      2   8120 North Telegraph Road
#3  9      2 Dearborn Heights, MI  48127
#3 10      2              (313) 528-4920
#3 # ... with 50 more rows

Теперь нам нужно удалить пустые строки с filter() и немного поработать, чтобы привести данные в подходящую форму для создания фрейма данных. Это очень хрупкий код в том смысле, что этот конкретный фрагмент может обрабатывать отсутствующие данные о номере телефона, но не более того. Если есть вторая адресная строка, вам нужно изменить этот подход или использовать другой подход:

filter(xdf, text != "") %>% 
  group_by(record) %>% 
  summarise(x = paste0(text, collapse="|")) %>% 
  separate(x, c("store", "address1", "city_state_zip", "phone_and_or_distance"), sep="\\|", extra="merge")
## # A tibble: 10 x 5
##    record                      store                  address1              city_state_zip       phone_and_or_distance
##  *  <int>                      <chr>                     <chr>                       <chr>                       <chr>
##  1      1 "O\u0092REILLY AUTO PARTS" 6938 NORTH TELEGRAPH ROAD Dearborn Heights, MI  48127 (313) 792-9134|0 miles away
##  2      2         Advance Auto Parts 8120 North Telegraph Road Dearborn Heights, MI  48127 (313) 528-4920|0 miles away
##  3      3                   Pep Boys         8955 TELEGRAPH RD          Redford, MI  48239 (313) 532-5750|2 miles away
##  4      4 "O\u0092REILLY AUTO PARTS"       27207 PLYMOUTH ROAD          Redford, MI  48239 (313) 937-1787|2 miles away
##  5      5 "O\u0092REILLY AUTO PARTS"      14975 TELEGRAPH ROAD          Redford, MI  48239 (313) 538-3584|2 miles away
##  6      6                   AutoZone           24250 FIVE MILE          Redford, MI  48239 (313) 527-6877|2 miles away
##  7      7 "O\u0092REILLY AUTO PARTS"        5940 MIDDLEBELT RD      Garden City, MI  48135 (734) 525-1607|3 miles away
##  8      8                   AutoZone        6228 MIDDLEBELT RD      Garden City, MI  48135 (734) 513-2233|3 miles away
##  9      9         Advance Auto Parts       3845 S Telegraph Rd         Dearborn, MI  48124 (313) 274-6549|3 miles away
## 10     10 "O\u0092REILLY AUTO PARTS"     27565 MICHIGAN AVENUE          Inkster, MI  48141 (313) 724-8544|3 miles away 

На всякий случай, если процесс был неочевидным, мы:

  • сгруппируйте строки по только что созданному record столбцу
  • объединить весь текст в одну строку, каждая часть разделена |
  • отделить все отдельные биты

Надеюсь, это поможет объяснить хрупкость.

Конечно, вам нужна была только часть «как добраться до контента», но, надеюсь, это сэкономит вам немного времени.

person hrbrmstr    schedule 14.10.2017
comment
Ух ты! Какое фантастическое объяснение всех шагов. Вы также научили меня новому способу обработки вложенных данных, который намного эффективнее. Я все время работаю с такой структурой данных, и на очистку у меня ушла лишь часть времени. У меня возникают проблемы с запуском этого из списка почтовых индексов. Мне нужно распечатать значения из цикла? Обычно я просто называю объект своим списком. - person Turbogoon; 15.10.2017
comment
Если бы вы могли привести воспроизводимый пример в новом вопросе, это позволило бы мне и другим помочь. Я ++ рад, что это немного разрешило твои проблемы. - person hrbrmstr; 15.10.2017
comment
Он сделал больше, чем это. Обмен данными был огромным. Я разместил здесь новый вопрос stackoverflow.com/questions/46759058/ еще раз спасибо за вашу помощь. Я проголосовал за, но, будучи новичком, не думаю, что это помогло. - person Turbogoon; 15.10.2017