网络数据爬虫总结【R】

浏览: 2956

爬虫基础:Rcurl与XML包

    爬虫是一种利用代码(例如:R code或Python code)模拟浏览器访问(下载)页面并根据HTML结构筛选获取所需信息的一种工具。在R里面我们通常用Rcurl包实现前一半的功能(模拟浏览器访问页面),用XML包完成后一半功能(通过HTML树结构筛选提取信息)。

Rcurl包访问页面

第一步:模拟浏览器行为

    若想用R语言模拟浏览器行为,就必须伪装报头。实际上,当我们用浏览器访问一个网页的时候,浏览器会像网页服务器发送一些指令。
# 构造请求报头
cust_header = c(`User-Agent` = "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:26.0) Gecko/20100101 Firefox/26.0",
   Accept = "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
   `Accept-Language` = "en-us", Connection = "keep-alive")

第二步:模拟访问

这个过程其实就是把指定的网页临时下载下来。两行代码就可以实现。
library(RCurl)
library(XML)
# 读取拉手深圳美食搜索
start_url = "http://shenzhen.lashou.com/cate/meishi"
# 读取PageSouce
pagesource <- getURL(start_url, httpheader = cust_header, .encoding = "utf-8")

XML包树结构筛选提取信息

第三步:整理HTML结构

    使用XML中的htmlTreeParse函数解析刚才得到的webpage变量(其中储存了HTML所爬取网页的代码)生成标准的HTML树形结构并赋值给pagetree变量。之后便可在XML中对该变量进行各种操作了。
pagetree <- htmlTreeParse(pagesource, encoding = "GB2312", error = function(...) {}, useInternalNodes = TRUE, trim = TRUE)# 注意encoding

第四步:定位节点

    在这里我们需要一点HTML的基础知识。HTML下的代码实际上是一个树级结构,是不同标签的层级嵌套。每一个标签以<x>开始,以</x>结束。举个例子:
<ul>
   <li>
       <div class="first">
          <a href="https://ask.hellobi.com/www.baidu.com" title="百度">链接1</a>
       </div>
   </li>
   <li>
       <div class="first">
          <a href="https://ask.hellobi.com/www.sina.com" title="新浪">链接2</a>
       </div>
   </li>
<body>与</body>之间的内容为网页的正文部分,如果我们想获取两个<a>标签中的正文(“链接1”,“链接2”),可以利用如下代码进行定位,其中text()代表的是获取标签内的文本。乍一看,是不是和文件路径很像?

ul/li/div/a/text() 

此外,我们也可以用利用两个具有相同的class属性值直接从开始定位,也即

div[@class='first']/a/text() 这两个写法在XML包中都是适用的。

    一般会利用chrome浏览器右键->查看源代码,也可以对想要查看的内容点击右键->检查,这时候我们如果右键某一行,就可以鼠标右键->copy->selector或Xpath直接复制相应的层级定位。需要注意的是,我们还需将复制来的定位代码更换成XML包所能识别的格式。比如用属性值用单引号,“//”表示所有该类型节点等。
node<-getNodeSet(pagetree, "//div[contains(@class,"goods")]//a[@class="goods-name"]//text()")
goods_name<-sapply(node,xmlValue)

其他细节点

对于多页面的解析

解析页数

# 解析页数
parseTotalPage <- function(pagesource) {
   doc <- htmlParse(pagesource)
   as.numeric(sapply(getNodeSet(doc, "//div[@class=\"page\"]/a[last()-1]/text()"),
       xmlValue))}

构造一个提取函数提取信息

# 解析页面内容,获取门店名称、描述、优惠价,门店价
parseContent <- function(pagesource) {
   doc <- htmlParse(pagesource)
   goods_name <- sapply(getNodeSet(doc, "//div[contains(@class,\"goods\")]//a[@class=\"goods-name\"]//text()"),
       xmlValue)
   goods_text <- sapply(getNodeSet(doc, "//div[contains(@class,\"goods\")]//a[@class=\"goods-text\"]//text()"),
       xmlValue)
   price <- sapply(getNodeSet(doc, "//div[contains(@class,\"goods\")]//span[@class=\"price\"]/text()"),
       xmlValue)
   org_price <- sapply(getNodeSet(doc, "//div[contains(@class,\"goods\")]//span[@class=\"money\"]/del/text()"),
       xmlValue)
   result <- data.frame(goods_name, goods_text, price, org_price)}

循环读取每一页面信息

# 获取总页数和第一页内容
total_page <- parseTotalPage(pagesource)pageresults <- parseContent(pagesource)# 生成2-n页urlpage = 1:(total_page - 1)url_list = ""url_list[page] = paste0("http://shenzhen.lashou.com/cate/meishi/page", page +
   1)
# 循环读取url,并进行下载解析
for (url in url_list) {
   pagesource <- getURL(url, httpheader = cust_header, .encoding = "utf-8")
   pageresult <- parseContent(pagesource)
   pageresults <- rbind(pageresults, pageresult)}
# 输出结果到文件
write.table(pageresults, "result.txt", row.names = FALSE)

字符编码的改变

# 查看encod编码类型
library(stringi)stri_enc_detect(names)
# 转换编码类型names <- iconv(name2, "UTF-8", "gb2312")

删除冗余信息,字符串清理

to_remove <- paste(c("\n", "^\\s+|\\s+$"), collapse = "|")
faddress <- gsub(to_remove, "", faddress)

最后给出了XML里常用的节点定位方法与代码

# XML实例
library(XML)
url = "http://www.w3school.com.cn/example/xmle/books.xml"
doc = xmlTreeParse(url, useInternal = TRUE)
top = xmlRoot(doc)
# 选取所有 title 节点
nodes = getNodeSet(top, "/bookstore/book/title")
# 选取第一个 book 的 title
nodes1 = getNodeSet(top, "/bookstore/book[1]/title")
# 选取 price 节点中的所有文本
price = getNodeSet(doc, "/bookstore/book/price/text()")
# 选取价格高于 35 的 price 节点
price1 = getNodeSet(doc, "/bookstore/book[price>35]/price")
getNodeSet(doc, "/bookstore/book[1]")
# 选取属于 bookstore 子元素的最后一个 book 元素。
getNodeSet(doc, "/bookstore/book[last()]")
# 选取最前面的两个属于 bookstore 元素的子元素的 book 元素
getNodeSet(doc, "/bookstore/book[position()<3]")
# 选取所有拥有名为 lang 的属性的 title 元素。
getNodeSet(doc, "//title[@lang]")
# 选取所有 title 元素,且这些元素拥有值为 eng 的 lang 属性
getNodeSet(doc, "//title[@lang='eng']")
# 选取 bookstore 元素的所有 book 元素,且其中的 price 元素的值须大于 35.00。getNodeSet(doc, "/bookstore/book[price>35.00]")
# 选取 bookstore 元素中的 book 元素的所有 title 元素,且 price# 元素的值须大于 35.00。
getNodeSet(doc, "/bookstore/book[price>35.00]/title")
# 选取 book 元素的所有 title 和 price 元素
getNodeSet(doc, "//book/title | //book/price")
# 选取文档中的所有 title 和 price 元素
getNodeSet(doc, "//title | //price")
# 选取 bookstore 元素的所有子元素
getNodeSet(doc, "/bookstore/*")
# 选取所有带有属性的 title 元素
getNodeSet(doc, "//title[@*]")
# 选择所有属性lang的值
unlist(getNodeSet(doc, "//title/@lang"), use.names = FALSE)
# title结点下的所有文本
getNodeSet(doc, "//title/text()")

运用rvest包提取数据

    Rcurl和XML包进行爬虫,这个组合虽然功能强大,但是经常会出一点意想不到的小问题。于是更便捷的Rvest包登场,它真正的实现了快速爬取数据的目的。

运用SelectorGadget插件快速定位

安装

    首先安装SelectorGadget插件,点击<https://cran.r-project.org/web/packages/rvest/vignettes/selectorgadget.html> 中的Installation,SelectorGadget链接。或<http://selectorgadget.com/> Chrome 扩展。

使用

    点击插件后,在网页最下面会启动对话框,同时会有highlight(黄色)框框随鼠标滑动,当点击需要提取的信息时,选择框会变为绿色,SelectorGadget对话框将产生minimal CSS 选择语句。同时匹配语句的元素也会变黄,当再次点击黄色元素时,选择器变为红色,表示排除所选元素。当点击未标黄元素时,表示增加相应的元素,SelectorGadget对话框会随选择元素而发生变化。点击XPath时,会自动根据选择元素自动转换为xpath语法。

rvest爬取数据

以爬取豆瓣数据为例:
# 查看书的信息
library(rvest)
web <- read_html("https://book.douban.com/top250?icn=index-book250-all", encoding = "UTF-8")
positions <- web %>%
   html_nodes("p.pl") %>%
   html_text()
# 查看书的题目
position <- web %>%
   html_nodes(".pl2 a") %>%
   html_text()
# 查看encod编码类型
library(stringi)
stri_enc_detect(position)
# 转换编码类型
position <- iconv(position, "UTF-8", "gb2312")
# 字符串清理 去掉多余的空格,“\n”等信息
to_remove <- paste(c("\n", "^\\s+|\\s+$"), collapse = "|")position <- gsub(to_remove, "", position)
    第一行是加载Rvest包。第二行是用“read_html函数读取网页信息(类似Rcurl里的getURL),在这个函数里只需写清楚网址和编码(一般就是UTF-8)即可第三行是获取节点信息。用%>%符号进行层级划分。web就是之前存储网页信息的变量,所以我们从这里开始,然后html_nodes()函数获取网页里的相应节点。在下面代码里我简单的重现了原网页里的一个层级结构。可以看到,实际上我们要爬取的信息在25个class属性为pl的<p>标签里的文本。
   [清] 曹雪芹 著 / 人民文学出版社 / 1996-12 / 59.70元    
    而对于这样的结构,在htmlnodes()函数里的写法就是简单的"p.pl",其中“.”表示class属性的值,如果是id属性则用“#”,如果大家学过CSS选择器就很好理解了,是完全一致的。

最后我们用html_text()函数表示获取文本信息,否则返回的是整个<p>

标签。总体上用以下一行代码就可以实现:

position <- web %>% 
   html_nodes("p.pl") %>%
   html_text()

比较与XML获取节点的方法(如下行代码),其实二者是异曲同工的,只不过将“/”分隔换为了“%>%”,同时个别写法有些许调整。

node <- getNodeSet(pagetree, "//p[@class='pl']/text()")

最终如果我们打印出这个变量的内容,就会发现和上篇文章中的爬取内容是一致的。

Rvest这个包的说明文档里给出了一些其他例子:

ateam <- read_html("http://www.boxofficemojo.com/movies/?id=ateam.htm")
# 两行代码分别获取了ateam这个网页里<center>标签里<td>的全部内容和<center>标签里<font>的全部内容
ateam %>% html_nodes("center") %>% html_nodes("td")
ateam %>% html_nodes("center") %>% html_nodes("font")

# 官方例子中还给出了获取特定序位的html标签的方法,用到了magrittr包里的extract2函数;以下两行代码都可以获得该网页中第一个<table>标签(由extract2(1)或`[[`(1)获取)中的所有<img>标签里的内容。
library(magrittr)
ateam %>% html_nodes("table") %>% extract2(1) %>% html_nodes("img")
ateam %>% html_nodes("table") %>% 1[[]] %>% html_nodes("img")
# 同理我们也可以获得网页里前两个<table>标签储存的所有<img>标签里的内容。
ateam %>% html_nodes("table") %>% 1:2[] %>% html_nodes("img")
ateam %>% html_nodes("table") %>% extract(1:2) %>% html_nodes("img")

参考资料:

1.知乎:【数据获取】爬虫利器Rvest包 <https://zhuanlan.zhihu.com/p/22940722?refer=rdatamining>

2.利用RCurl包完成自己感兴趣的团购信息【批量】抓取<http://blog.csdn.net/jiabiao1602/article/details/40856975>

3. Hadley Wickham,Selectorgadget <https://cran.rproject.org/web/packages/rvest/vignettes/selectorgadget.html>

4. CRAN其他相关资料

要想获取分析代码,可查看原文,进入本人的GitHubhttps://github.com/Alven8816查看下载,或通过本人邮箱yuwenhuajiayou@sina.cn与本人联系



”乐享数据“个人公众号,不代表任何团体利益,亦无任何商业目的。任何形式的转载、演绎必须经过公众号联系原作者获得授权,保留一切权力。欢迎关注“乐享数据”。

image.png

推荐 6
本文由 余文华 创作,采用 知识共享署名-相同方式共享 3.0 中国大陆许可协议 进行许可。
转载、引用前需联系作者,并署名作者且注明文章出处。
本站文章版权归原作者及原出处所有 。内容为作者个人观点, 并不代表本站赞同其观点和对其真实性负责。本站是一个个人学习交流的平台,并不用于任何商业目的,如果有任何问题,请及时联系我们,我们将根据著作权人的要求,立即更正或者删除有关内容。本站拥有对此声明的最终解释权。

1 个评论

关注了,写的好详细

要回复文章请先登录注册