2017-10-14 85 views
0

我正在尝试使用rvest来扫描在cgi-bin中弹出的webform的结果。但是,当我运行该脚本时,我会在200英里内返回0个结果。以下是我的代码,我感谢任何反馈和帮助。主要网站是http://www.zmax.com/,它具有启动cgi-bin的搜索框。我该如何用rvest和R刮一个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() 

我不确定我在哪里在返回那是表单上的数据会错的。

回答

0

奇怪的是,他们用于出口查找的主站点和提供者都不会阻止C或REP进行搜索。 ¯\_(ツ)_/¯

你真的应该熟悉的浏览器开发者工具,你就已经能够看出,主站点发出HTTP POST请求查找网站的VS要求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 

reshttrresponse对象和一个会通常只是做:

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()删除空行,并做一些改写(munging)获取数据到一个像样的形式制作的数据帧。这是超级脆弱代码,因为这个特定的片段可以处理丢失的电话号码数据,但这就是它。如果有第二个地址线,你需要修改这个方法,或者使用不同的方法:

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
  • 斯马什所有文本合并为一个字符串,将每个部分与|
  • 分离出所有单个比特分开

这可能有助于解释脆弱性。当然,你只想要“如何到达内容”部分,但希望这会为你节省更多时间。

+0

哇!对于所有步骤来说,这是多么美妙的解释您还教会了我一种更有效的处理嵌套数据的新方法。我一直在处理这样的数据结构,而这只是清理时间的一小部分。我是否需要从一个循环中打印值,从一系列邮政编码中运行此问题?通常我只会用列表命名一个对象。 – Turbogoon

+0

如果你可以在一个新问题中做出一个可重复的例子,它会使我和其他人都能够提供帮助。我很高兴,这解决了你的一些问题,寿。 – hrbrmstr

+0

它做的比这更多。数据困难巨大。我在这里发布了一个新问题https://stackoverflow.com/questions/46759058/how-to-pass-multiple-values-in-a-rvest-submission-form再次感谢您的帮助。我提高了但是新的我不认为它做了很多。 – Turbogoon