数据科学与R语言生物统计生物信息-生物统计

R语言:rvest包学习爬虫--笔记

2017-12-08  本文已影响23人  育种数据分析之放飞自我

| 参考: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抓取示例

网页抓取的一般步骤我总结有如下三步:

  1. 首先,明确所要抓取的内容

  2. 然后,通过html的标签名称、属性以及id等确切的描述定位到该内容的位置

  3. 最后,提取相关的内容信息,必要时再做一些数据处理

3.1 :提取新浪NBA新闻标题

网址:http://sports.sina.com.cn/nba/

sina

使用SelectorGadget的谷歌插件,点击标题,复制nodes:

图片.png
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")

结果:

result

获取节点信息

%>% 符号进行层级划分。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;amp;l=as2&amp;o=1&amp ...

# [3] <img src="http://www.assoc-amazon.com/e/ir?t=boxofficemojo-20&amp;amp;l=as2&amp;o=1&amp ...

# [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;amp;c2=6034961&amp;cv=2.0&amp;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")

结果:

图片.png

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")

结果:

图片.png

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&amp;e=49&amp;key=1&amp;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&amp;spc=2&amp;pl=&amp;gj=&amp;xl=&amp;yx=&amp;gx=&amp;st=&amp;labelWords=&amp;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&amp;spc=2&amp;pl=&amp;gj=&amp;xl=&amp;yx=&amp;gx=&amp;st=&amp;labelWords=&amp;lc=&amp;workAddress=&amp;city=%E6%B7%B1%E5%9C%B3&amp;requestId=&amp;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

参考資料

rvest + CSS Selector 网页数据抓取的最佳选择-戴申

R爬虫实战1(学习)—基于RVEST包

上一篇下一篇

猜你喜欢

热点阅读