| 参考:https://www.douban.com/note/618385613/,因为原文中没有转化,就自己操作一下,已记录学习的过程,向原作者致敬。这里将不能运行的代码修改了一下。
1,rvest包简介和安装
rvest包是hadley大神的又一力作,使用它能更方便地提取网页上的信息,包括文本、数字、表格等,本文对rvest包的运用做一个详细介绍,希望能够帮助你在网页抓取的武器库中新添一把利器。
从CRAN上安装发行版:
install.packages("rvest")
也可以从github上安装开发版本:
install.packages("devtools")
devtools::install_github("hadley/rvest")
2,rvest用法简介
下面对rvest包中的主要函数的功能做一下说明:
read_html(): 读取html文档的函数,其输入可以是线上的url,也可以是本地的html文件,甚至是包含html的字符串也可以。
html_nodes(): 选择提取文档中制定元素的部分。可以使用css selectors,例如html_nodes(doc, "table td");也可以使用xpath selectors,例如html_nodes(doc, xpath = "//table//td")。
html_tag(): 提取标签名称;
html_text(): 提取标签内的文本;
html_attr(): 提取指定属性的内容;
html_attrs(): 提取所有的属性名称及其内容;
html_table(): 解析网页数据表的数据到R的数据框中。
html_form(),set_values()和submit_form() 分别表示提取、修改和提交表单。
3,rvest抓取示例
网页抓取的一般步骤我总结有如下三步:
首先,明确所要抓取的内容
然后,通过html的标签名称、属性以及id等确切的描述定位到该内容的位置
最后,提取相关的内容信息,必要时再做一些数据处理
3.1 :提取新浪NBA新闻标题
网址:http://sports.sina.com.cn/nba/
使用SelectorGadget的谷歌插件,点击标题,复制nodes:
library(rvest)
library(stringr)
library(XML)
library(xml2)
url='http://sports.sina.com.cn/nba/'
web=read_html(url)
title <- html_nodes(web,".item p a")%>%html_text
head(title)
结果:
> head(title)
[1] "20中5三分全丢!毒瘤瓜再上线 想解毒必用这招?"
[2] "威少准三双雷霆惨遭逆转 替补逆天篮网两连胜"
[3] "全能球哥今日上线!末节细节俩操作是胜负手"
[4] "天赋大战!十个跑跳长手怪的比赛你要跪着看"
[5] "队史唯2表现换不到胜利 末节16分大帝真尽力了"
[6] "比尔34分奇才再下一城 布克缺阵太阳功亏一篑"
插件的使用参见
vignette("selectorgadget")
以上提取的主要是文本信息,对于网页中表格中的数据可以直接采用html_table()来获取。
3.2 提取网站中的表格信息
网址:http://hz.house.ifeng.com/detail/2014_10_28/50087618_1.shtml
因为网页里面有表格,这里直接读取表格,然后提取即可。
library(rvest)
library(magrittr)
url <- 'http://hz.house.ifeng.com/detail/2014_10_28/50087618_1.shtml'
content <- read_html(url)
trade <- html_table(content, header = TRUE)[[1]]
names(trade) <- trade[1,]
trade <- trade[-1,]
write.xlsx(trade,"d:/trade.xlsx")
结果:
获取节点信息
用%>%
符号进行层级划分。web就是之前存储网页信息的变量,所以我们从这里开始,然后html_nodes()函数获取网页里的相应节点。在下面代码里我简单的重现了原网页里的一个层级结构。可以看到,实际上我们要爬取的信息在25个class属性为pl的<p>标签里的文本。
# <p class=pl>
# [清] 曹雪芹 著 / 人民文学出版社 / 1996-12 / 59.70元
# </p>
而对于这样的结构,在htmlnodes()函数里的写法就是简单的 "p.pl",其中“.”表示class属性的值,如果是id属性则用“#”,如果大家学过CSS选择器就很好理解了,是完全一致的。
最后我们用html_text()函数表示获取文本信息,否则返回的是整个<p>标签。 总体上用以下一行代码就可以实现:
position<-web %>% html_nodes("p.pl") %>% html_text()
position
想要学习更多,我们可以在Rstudio里的命令行输入如下代码查询html_nodes()函数的相关用法:
?html_nodes
Rvest这个包的说明文档里给出了一些其他例子
library(rvest)
library(stringr)
library(xml2)
ateam <- read_html("http://www.boxofficemojo.com/movies/?id=ateam.htm")
# 然后下面代码分别获取了ateam这个网页里<center>标签里<td>的全部内容
ateam %>% html_nodes("center") %>% html_nodes("td")
# {xml_nodeset (7)}
# [1] <td align="center" colspan="2"><font size="4">Domestic Tot ...
# [2] <td valign="top">Distributor: <b><a href="/studio/chart/?s ...
# [3] <td valign="top">Release Date: <b><nobr><a href="/schedule ...
# [4] <td valign="top">Genre: <b>Action</b>\n</td>\n
# [5] <td valign="top">Runtime: <b>1 hrs. 57 min.</b>\n</td>
# [6] <td valign="top">MPAA Rating: <b>PG-13</b>\n</td>\n
# [7] <td valign="top">Production Budget: <b>$110 million</b>\n< ...
# 获取了ateam这个网页里<center>标签里<font>的全部内容
ateam %>% html_nodes("center") %>% html_nodes("font")
#[1] <font size="4">Domestic Total Gross: <b>$77,222,099</b></font>
# 接着官方例子中还给出了获取特定序位的html标签的方法,用到了magrittr包里的extract2函数:
library(magrittr)
#下面两行代码都可以获得该网页中第一个<table>标签(由extract2(1)或`[[`(1)获取)中的所有<img>标签里的内容,
ateam %>% html_nodes("table") %>% extract2(1) %>% html_nodes("img")
ateam %>% html_nodes("table") %>% `[[`(1) %>% html_nodes("img")
# 运行结果如下:
# {xml_nodeset (6)}
# [1] <img src="https://images-na.ssl-images-amazon.com/images/M/MV5BMTc4ODc4NTQ1N15BMl5B ...
# [2] <img src="http://www.assoc-amazon.com/e/ir?t=boxofficemojo-20&amp;l=as2&o=1& ...
# [3] <img src="http://www.assoc-amazon.com/e/ir?t=boxofficemojo-20&amp;l=as2&o=1& ...
# [4] <img src="/img/misc/bom_logo1.png" width="245" height="56" alt="Box Office Mojo"/>
# [5] <img src="/img/misc/IMDbSm.png" width="34" height="16" alt="IMDb" valign="middle"/>
# [6] <img src="http://b.scorecardresearch.com/p?c1=2&amp;c2=6034961&cv=2.0&cj=1"/>
#同理我们也可以获得网页里前两个<table>标签储存的所有<img>标签里的内容:
ateam %>% html_nodes("table") %>% `[`(1:2) %>% html_nodes("img")
ateam %>% html_nodes("table") %>% extract(1:2) %>% html_nodes("img")
3.3 用rvest从赶集网抓取二手房单页面数据
gurl <- "http://cs.ganji.com/fang5/yuhuashazitang/o1/"
getData <- function(gurl){
# 抓取赶集网二手房源单页的数据
library(rvest)
# 赶集网首页筛选长沙-雨花区-砂子塘的二手房源,获得链接,o1为页数
tmp <- gurl %>% html_session %>%
read_html(encoding="utf-8") %>%
html_nodes("div.f-main-list>div>div")
# 单个房源的puid
puid <- tmp %>% html_attr("id")
# 单个房源的链接
itemURL <-tmp %>% html_attr("href") %>%
gsub(pattern="/fang5",replacement="http://cs.ganji.com/fang5")
# 缩略图链接
smallImg <- tmp %>% html_nodes("dl>dt>div>a>img") %>% html_attr("src")
# 标题
iTitle <- tmp %>% html_nodes("dl>dd>a") %>% html_attr("title")
# 户型
iLayout <- tmp %>% html_nodes("dl>dd[data-huxing]") %>% html_attr("data-huxing")
# 面积
iArea <- tmp %>% html_nodes("dl>dd[data-huxing]") %>%
html_attr("data-area") %>%
gsub(pattern="[^0-9]",replacement="")
# 筛选朝向等数据
iTmp <- tmp %>% html_nodes("dl>dd[data-huxing]>span") %>% html_text
iOrientation <- iTmp[seq(from=5,to=length(iTmp),by=9)] # 提取朝向
iFloor <- iTmp[seq(from=7,to=length(iTmp),by=9)] %>% # 提取楼层
gsub(pattern="\n",replacement="")
iDecoration <- iTmp[seq(from=9,to=length(iTmp),by=9)] # 提取装修
# 提取地址
iAddr <- tmp %>% html_nodes("dl>dd>span.area") %>% html_text %>%
gsub(pattern="\n",replacement=" ") %>%
gsub(pattern=" ",replacement="")
# 提取价格
iPrice <- tmp %>% html_nodes("dl>dd>div.price>span:first-child") %>% html_text
# 提取单价
iTime <- tmp %>% html_nodes("dl>dd>div.time") %>% html_text %>%
gsub(pattern="[^0-9]",replacement="") %>% as.numeric
# 合并数据框
iData <- data.frame(puid=puid,
iLayout=iLayout,
iArea=iArea,
iPrice=iPrice,
iTime=iTime,
# iDecoration=iDecoration,
iFloor=iFloor,
iOrientation=iOrientation,
itemURL=itemURL,
smallImg=smallImg,
iTitle=iTitle,
iAddr=iAddr,
stringsAsFactors=FALSE)
# 返回数据框
return(iData)
}
result <- getData(gurl)
write.xlsx(result,"d:/result.xlsx")
结果:
3.4 如何用rvest包爬取豆瓣图书250的数据-例2
用R中的rvest包爬取豆瓣top250图书的基本信息(包括书名、评分、作者、译者、出版社、出版时间,价格),然后根据出版社和出版时间进行进一步的分析
for (i in 1:length(ind)){
web<-read_html(str_c("https://book.douban.com/top250?start=",ind[i]),encoding="UTF-8")
#爬取图书的作者、出版社、出版时间、价格
p <-web%>%html_nodes("p.pl")%>%html_text()
#爬取图书的name
q <-web%>%html_nodes(".pl2 a")%>%html_text()
#爬取图书的rate
r <- web%>%html_nodes(".rating_nums")%>%html_text()
#消除图书的name q中的空格
q <-str_replace_all(q," ","")
q <-str_replace_all(q,"\n","")
#赋值图书书名和评分
name <- q
rate <- r
#对p进行处理
#提取作者名字
p1 <-str_split_fixed(p,"/",2)
author <- p1[,1];
#提取翻译者名字
p2 <-str_split_fixed(p1[,2],"/",2)
a <-str_detect(p2[,1],"出版")
b <- str_detect(p2[,1],"书店")
interpre <-p2[,1]
interpre[a|b] <-"NA"
#提取出版商
p3 <-str_split_fixed(p2[,2],"/",2)
publisher <-p3[,1]
publisher[a] <-p2[a,1]
publisher[b] <-p2[b,1]
#提取出版时间
p4 <-str_split_fixed(p3[,2],"/",2)
publish_time <-p4[,1]
publish_time[a]<- p3[a,1]
publish_time[b]<- p3[b,1]
publish_time <- str_replace(publish_time,"年","-")
publish_time <- str_replace(publish_time,"月","-")
#提取价格
p5 <-str_split_fixed(p4[,2],"/",2)
price <- p5[,1]
price[a] <-p4[a,1]
price[b] <-p4[b,1]
#创建数据框存储以上信息
book <-data_frame(name,rate,author,interpre,publisher,publish_time,price)
book_inf <-rbind(book_inf,book)
}
write.xlsx(book_inf,"d:/book.xlsx")
结果:
3.5 爬取了戴申在科学网博客
# 1 此均值非彼均值
# 2 [转载]孔丘、孔子、孔老二,它究竟是一只什么鸟?
# 3 大数据分析之——k-means聚类中的坑
# 4 大数据分析之——足彩数据趴取
# 5 [转载]老王这次要摊事了,当年他主管的部门是事被重新抖出来。
# 6 [转载]党卫军是这样抓人的。
# posttime count_of_read
# 1 2015-03-08 216 次阅读
# 2 2015-02-10 190 次阅读
# 3 2015-01-18 380 次阅读
# 4 2015-01-10 437 次阅读
# 5 2015-01-05 480 次阅读
# 6 2015-01-05 398 次阅读
# paperlink
# 1 http://blog.sciencenet.cn/blog-556556-872813.html
# 2 http://blog.sciencenet.cn/blog-556556-866932.html
# 3 http://blog.sciencenet.cn/blog-556556-860647.html
# 4 http://blog.sciencenet.cn/blog-556556-858171.html
# 5 http://blog.sciencenet.cn/blog-556556-856705.html
# 6 http://blog.sciencenet.cn/blog-556556-856640.html
write.table(final,"final.csv",fileEncoding="GB2312")
#抓取的数据需要在Excel进一步加工,加工后读取进来,进一步做分析
a <- read.table("dai_shen_blog_0326.csv",header=TRUE,sep=";",fileEncoding="GB2312")#Mac OS 环境下,要sep=";"
a$posttime <- as.Date(a$posttime)
a$paperlink <- as.character(a$paperlink)
a$papername <- as.character(a$papername)
a$count_of_read_NO. <- as.numeric(a$count_of_read_NO.)
library(ggplot2)
qplot(posttime,count_of_read_NO.,data=a,geom="point",colour=repost,size=6)
3.7 爬取了NBA 2014-2015常规赛技术统计排行 - 得分榜
#Crawl NBA player statistics from sina
#web http://nba.sports.sina.com.cn/playerstats.php?s=0&e=49&key=1&t=1
library(rvest)
library(stringr)
library(sqldf)
rm(NBAdata)
start <- seq(0,250,50)
end <- seq(49,299,50)
getdata <- function(i){
url <- paste0('http://nba.sports.sina.com.cn/playerstats.php?s=',start[i],'&e=',end[i],'&key=1&t=1')
rank <- url %>% html_session() %>% html_nodes("table") %>% .[[2]] %>% html_nodes("td:nth-child(1)") %>% html_text()%>%.[-1]%>%as.numeric()
player <- url %>% html_session() %>% html_nodes("table") %>% .[[2]] %>% html_nodes("td:nth-child(2)") %>% html_text()%>%.[-1]%>%str_sub(9,100)%>%as.character()
team <- url %>% html_session() %>% html_nodes("table") %>% .[[2]] %>% html_nodes("td:nth-child(3)") %>% html_text()%>%.[-1]%>%str_sub(9,100)%>%as.character()
avg_score <- url %>% html_session() %>% html_nodes("table") %>% .[[2]] %>% html_nodes("td:nth-child(4)") %>% html_text()%>%.[-1]
total_score <- url %>% html_session() %>% html_nodes("table") %>% .[[2]] %>% html_nodes("td:nth-child(5)") %>% html_text()%>%.[-1]
total_shoot <- url %>% html_session() %>% html_nodes("table") %>% .[[2]] %>% html_nodes("td:nth-child(6)") %>% html_text()%>%.[-1]
three_point <- url %>% html_session() %>% html_nodes("table") %>% .[[2]] %>% html_nodes("td:nth-child(7)") %>% html_text()%>%.[-1]
punish_point <- url %>% html_session() %>% html_nodes("table") %>% .[[2]] %>% html_nodes("td:nth-child(8)") %>% html_text()%>%.[-1]
avg_time <- url %>% html_session() %>% html_nodes("table") %>% .[[2]] %>% html_nodes("td:nth-child(9)") %>% html_text()%>%.[-1]
total_involve <- url %>% html_session() %>% html_nodes("table") %>% .[[2]] %>% html_nodes("td:nth-child(10)") %>% html_text()%>%.[-1]
data.frame(rank,player,team,avg_score,total_score,total_shoot,three_point,punish_point,avg_time,total_involve)
}
NBAdata <- data.frame()
for(i in 1:6){
NBAdata <- rbind(NBAdata,getdata(i))
}
NBAdata <- sqldf("select distinct * from NBAdata")
write.table(NBAdata,"NBAdata.csv",sep=",",fileEncoding="GB2312")
head(NBAdata)
# rank player team avg_score total_score
# 1 1 拉塞尔-威斯布鲁克 雷霆 27.3 1556
# 2 2 詹姆斯-哈登 火箭 27.1 1900
# 3 3 勒布朗-詹姆斯 骑士 25.8 1600
# 4 4 安东尼-戴维斯 鹈鹕 24.6 1403
# 5 5 德马库斯-考辛斯 国王 23.8 1308
# 6 6 斯蒂芬-库里 勇士 23.4 1618
# total_shoot three_point punish_point avg_time
# 1 42.7% 30.1% 84.6% 33.8
# 2 44% 36.8% 86.6% 36.8
# 3 49.2% 35.4% 71.9% 36.2
# 4 54.5% 10% 81.4% 36.2
# 5 46.5% 28.6% 80.2% 33.7
# 6 47.9% 42.2% 91.4% 32.9
# total_involve
# 1 57
# 2 70
# 3 62
# 4 57
# 5 55
# 6 69
3.8 拉勾网爬了一下
library(rvest)
lagou <- "http://www.lagou.com/jobs/list_%E6%95%B0%E6%8D%AE%E5%88%86%E6%9E%90?kd=%E6%95%B0%E6%8D%AE%E5%88%86%E6%9E%90&spc=2&pl=&gj=&xl=&yx=&gx=&st=&labelWords=&lc
=&workAddress=&city=%E6%B7%B1%E5%9C%B3&requestId=&pn=3"
web<-html(lagou,encoding="UTF-8") #读取数据,规定编码
#之前我是用关键字搜索,阅读html代码,获得html_nodes里需要什么属性,不过许多浏览器有开发者工具,可以直接获得层级信息。如遨游
position<-web %>% html_nodes("li div.hot_pos_l a") %>% html_text()
#上面就是直接读取数据,获得位置信息
#不过在后面做其他网站时发现,有时候信息储存在同类数据里(如div没有class等等),建议是找一个大的分类,先获得表格信息,再做数据
list_lagou<-web %>% html_nodes("li.clearfix")
#这里正确找准正确的划分点很重要。有<li class="odd clearfix">,其实用li.clearfix一样可以取(对于空格二选一,如"li.odd"或者"li.clearfix")
#接下来的company/position照选即可,因为事先已经分好了list,所以每一个出多少心里有数。。
在讲完原理之后,现在开始尝试写代码,因为里面涉及太多的选取数据工作。为了避免出现太多变量,我最后是编了一个函数,输出数据库。
#下面开始写代码,首先写一个函数getdata,会输出一个数据框
getdata<-function(page,urlwithoutpage){
url=paste0(urlwithoutpage,page) #这里输入拉勾网没有页码的url
web<-html(url,encoding="UTF-8") #读取数据,规定编码,access用
list_lagou<-web %>% html_nodes("li.clearfix") #获得一个清单,15个职位
title<-list_lagou %>% html_nodes("div.hot_pos_l div.mb10 a")%>%html_text()
company<-list_lagou %>% html_nodes("div.hot_pos_r div.mb10 a")%>%html_text()
link<-gsub("\\?source\\=search","",list_lagou %>% html_nodes("div.hot_pos_l div.mb10 a")%>%html_attr("href"))
#接下来的由于数据都存在span里,没有很好的划分。这个取数要复杂一些。我在这里,研究他们的表,先取15个完整list,然后用seq等序列取数
#之后要研究是否有更好的方法
#如果有table,可以直接用data.table取数更快。。。
temp<-list_lagou %>% html_nodes("div.hot_pos_l span")
city<-temp[seq(1,90,by=6)] %>% html_text()
salary<-gsub("月薪:","",temp[seq(2,90,by=6)]%>% html_text())
year<-gsub("经验:","",temp[seq(3,90,by=6)]%>% html_text())
degree<-gsub("最低学历:","",temp[seq(4,90,by=6)]%>%html_text())
benefit<-gsub("职位诱惑:","",temp[seq(5,90,by=6)]%>% html_text())
time<-temp[seq(6,90,by=6)]%>%html_text()
data.frame(title,company,city,salary,year,degree,benefit,time,link)
}
获取函数,这里先爬一页!
url<-"http://www.lagou.com/jobs/list_%E6%95%B0%E6%8D%AE%E5%88%86%E6%9E%90?kd=%E6%95%B0%E6%8D%AE%E5%88%86%E6%9E%90&spc=2&pl=&gj=&xl=&yx=&gx=&st=&labelWords=&lc=&workAddress=&city=%E6%B7%B1%E5%9C%B3&requestId=&pn="
final<-data.frame()
for (i in 3){
final<-rbind(final,getdata(i,url))
} #定义个数,把上面的getdata得到的Data.frame合并
head(final)
3.9 R的爬虫和回归模型案例-以北京自如房租价格为例
library(rvest)
library(stringr)
library(XML)
library(xml2)
WebSpider <- function(m){
url <- str_c(cp,"?p=",m)
web <- read_html(url,encoding = "UTF-8")#抓取网页信息
name_rough <- web %>% html_nodes("h3") %>% html_text() #获取粗房屋名
area_rough <- web %>% html_nodes("h4") %>% html_text() #提取区位
price_rough <- web %>% html_nodes("p.price") %>% html_text() #提取价格
price <- str_extract(price_rough, "[0-9]+") %>% as.numeric()#提取精确价格
detail <- web %>% html_nodes("div.detail") %>% html_text() #提取其他信息
#合并成数据框
data.frame(name_rough,area_rough,forward,mate_num,location,price,detail)
# 4、接着是观察翻页规律,然后遇到了一个坑。原以为之后的页码不过是http://www.ziroom.com/z/nl/z3.html的基础上加上/1、/2........,我依照这个思路抓了一番,也获得了将近4000多条数据,原以为这大概就是全部吧。但我仔细看数据才发现,这样下来的基本都是房山、大兴和通州等的数据,基本没有城六区的,城六区的只有选了区域选项后才会出现:
dc <- "http://www.ziroom.com/z/nl/z3-d23008614.html"
xc <- "http://www.ziroom.com/z/nl/z3-d23008626.html"
cy <- "http://www.ziroom.com/z/nl/z3-d23008613.html"
hd <- "http://www.ziroom.com/z/nl/z3-d23008618.html"
ft <- "http://www.ziroom.com/z/nl/z3-d23008617.html"
sjs <- "http://www.ziroom.com/z/nl/z3-d23008623.html"
cp <- "http://www.ziroom.com/z/nl/z3-d23008611.html"
# 这样一来,只有逐区的来进行翻页爬了。为此,只能选定部分区域来做分析了。
results_cp <- data.frame()
for(m in 1:118){ #118为昌平区信息的总页码
results_cp <- rbind(results_cp,WebSpider(m))#合并单个区每一次循环输出的数据
}
#依次重复获得7个区的数据
results <- rbind(results_cp, results_cy,results_dc,results_ft,
results_hd,results_sjs,results_xc) #将所有各区数据的合并
}
有没有关于R语言爬虫的书籍或者资源?
有一本《基于R语言的自动数据收集》翻译得相当好# 从零开始学习rvest网络爬虫抓数据-Stone.Hou 2017/5/1
[大神 Hadley rvest in GitHub] https://github.com/hadley/rvest