Как я могу очистить 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()
Я не уверен, в чем я ошибаюсь, возвращая данные, которые есть в форме.
1 ответ
Как ни странно, ни основной сайт, ни провайдер, которого они используют для поиска розеток, не предотвращают очистку T&C или REP. ¯\_(ツ)_/¯
Вы должны действительно ознакомиться с инструментами разработчика браузера, так как вы могли бы видеть, что основной сайт делает HTTP POST
запрос на поисковый сайт против 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>
брейки. К счастью? есть только единственное число <td>
элементы в каждом из этих <tr>
элементы. Таким образом, мы можем схватить их всех одним махом, выбрав правильный <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
колонка - смять весь текст в одну строку, каждая часть разделена
|
"s - выделить все отдельные биты
Надеюсь, это поможет объяснить хрупкость.
Конечно, вы хотели только часть "как добраться до контента", но, надеюсь, это сэкономило вам еще немного времени.