R语言网络数据爬虫之三个问题
现在大家对爬虫的兴趣不断高涨,R和PYTHON是两个非常有力的爬虫工具。Python倾向于做大型爬虫,与R相比,语法相对复杂,因此Python爬虫的学习曲线会相对陡峭。对于那些时间宝贵,又想从网上获取数据的初学者而言,用R做爬虫是最好的选择,有三个原因:R语法相对直观,规则更加灵活;对于数据量不大的用户来数(小于百万级),R也能够非常自如地处理;先学习R爬虫,等熟悉爬虫的原理之后,在过渡到Python是很容易的。R中有好几个包都可以抓取网页数据,这里介绍Rcurl包应用的三个问题。
一、RCurl包简介与安装
RCurl包要感谢Duncan Temple Lang等牛人的无私工作,我们才可以在R中运用CURL,将R和CURL这两大开源利器的优势完美的结合到一起,使用它能更方便地提取网页上的信息,包括文本、数字、表格等,本文对RCurl包的运用做一个详细介绍,希望能够帮助你在网页抓取的武器库中新添一把利器。
1.RCurl包的安装与加载
install.packages("RCurl")
library(RCurl)
2.RCurl包的函数
RCurl包中一共有几个get开头的函数嘛,大约有十四五个那么多(保守估计)!遇到复杂的数据爬取需求,自然是摸不着头脑。实际上很多我们都不常用,常用的不超过五个,而且这些函数命名都很有规律,一般是类似功能的名称中都有统一的关键词标识,只要理解这些关键词,下面我对9个可能用到的get函数简要做一个分类。
函数 | 函数说明 |
---|---|
getURL | get请求的一般形式 |
getBinaryURL | get请求二进制资源 |
getURLContent | get请求(可以根据返回状态的ContentType决定返回内容是文本格式还是二进制格式 |
getURIAsynchronous | 这个函数文档给的解释是可以实现请求的异步发送和多并发,需要计算机的cpu支持多核性能 |
getForm | 单独提交查询参数的get请求函数 |
getFormParams | 可以根据带参数的URL,分解出原始参数对容错与配置句柄函数 |
getCurlErrorClassNames | 排错函数,可以根据请求错误信息得到错误类型,方便后期排错 |
getCurlHandle | curl句柄函数(是请求回话维持与进程管理的最重要部分,所有登录操作、身份认证都都需要该函数的支持) |
getCurlInfo | 根据curl句柄的记录信息,返回各项目信息详情 |
getURL函数是一个基础get请求函数,其核心参数主要有URL、.opt、curl、.encoding。URL就是请求的对应网址链接。curl参数是一个句柄函数,它的参数指定对象是一个内嵌函数,通常是curl = getCurlHandle(),getCurlHandle()函数内同样是配置信息,不过curl句柄函数内的所有配置信息是可以提供给全局使用的,多次携带,维持整个回话状态,相对于一组初始化参数,而.opt参数内的各项配置信息是当前get请求使用的,它会覆盖和修改curl句柄函数内的初始化信息(当没有提供.opt参数时,get请求仍然使用curl中的初始化参数。).opt是一个配置参数,它就收一组带有命名的list参数,这些通常包括httpheader、proxy、timeout、verbose、cookiefile(cookiejar)等配置信息。.encoding是字符集编码,这个通常可以通过请求的相应头ContType获取。
getBinaryURL函数用来获取网络上的二进制资源。二进制资源一般是指网络服务器上的二进制文件、图像文件、音视频等多媒体文件。这些资源通常可以直接通过download函数进行请求下载,但是getBinaryURL函数可以添加更多配置信息。
使用getURLContent请求网页时,返回的是字符串(未解析的HTML文档),请求图片时,反回的是bytes值。不那么讲究的场合,getURLContent可以替代getURL或者getBinaryURL,但是通常为了便于记忆,一般请求网页使用getURL,请求二进制文件使用getBinaryURL,实际上三个函数仅仅是返回值的差异,通过参数设置的转换,基本可以相互替代。
二、RCurl包应用示例
1.二进制网页内容的爬取
library(RCurl)
library(XML)
library(curl)
library(xml2)
url<-"https://pic3.zhimg.com/720845d4f960c680039dbf7cc83ec21a_r.jpg"
response<-getBinaryURL(url)
note <- file("hello.jpg",open = "wb")#打开一个文件链接,对该文件进行二进制写入操作,注意文件的后缀名
writeBin(response,note)#将temp写入note文件中
close(note)#关闭写入文件
2. 网页数据的爬取
R语言代码
library(RCurl)
library(XML)
library(raster)
library(stringr)
myHttpheader <- c("User-Agent"="Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN; rv:1.9.1.6) ",
"Accept"="text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
"Accept-Language"="en-us",
"Connection"="keep-alive",
"Accept-Charset"="GB2312,utf-8;q=0.7,*;q=0.7")
url400<-"https://tianjin.anjuke.com/sale/?pi=360-cpc-tj-tongyong2&kwid=13246865469&utm_term=%E5%A4%A9%E6%B4%A5%E4%BA%8C%E6%89%8B%E6%88%BF"
webpage <- getURL(url400,httpheader=myHttpheader)
pagetree <- htmlTreeParse(webpage,encoding="UTF-8", error=function(...){}, useInternalNodes = TRUE,trim=TRUE)
node<-getNodeSet(pagetree, "//h3[@title]/text()")
info<-sapply(node,xmlValue)
info
node<-getNodeSet(pagetree, "//p[@class='property-price-average']/text()")
info4<-sapply(node,xmlValue)
info4
result <- data.frame(info, info4)
爬取结果
head(result)
info info4
1 新上房源低价房 低单价两室 性价比高 价格可议 19025元/㎡
2 金海园!精装二室!南北通透!好楼层!价格可议!看房方便 12165元/㎡
3 太便宜了 上上佳园 电梯洋房 全明户型 12088元/㎡
4 隔壁小王卖房 奢华大毛坯 急售 10940元/㎡
5 山倾城一小 至道庄园电梯洋房次顶楼精装3室2卫 满五 15892元/㎡
6 阳光100 超合适两室 精装修 买到即专到 价格有惊喜 来聊 19388元/㎡
三、爬取数据的三个问题
在爬取网页数据时,经常发生下面三个方面的问题:
1. 链接网站
我们平时绝大部分时间是怎么用浏览器的?第一步:打开自己钟爱的那款浏览器;第二步:输入某个网址,如http://www.baidu.com/;第三步:回车;第四步:拖拖鼠标,看自己想看的东西;第五步:点进某个链接,接着看。在关注呈现的信息的时候,大多数人都不大会去关心上述的5步(或者更多步)中浏览器(客户端)和网站(服务器端)是如何工作的。其实客户端和服务器端一直在保持联系:告诉对方想干什么,是否同意等等内容?一定要观察是否连接到该网址。
url300="http://t.dianping.com/list/guangzhou?q=%E7%94%B5%E5%BD%B1"
curl=getCurlHandle()
url=getURL(url300,curl=curl,httpheader=myheader)
getCurlInfo(curl)$response.code
显示为200 表示获取成功。
有时候网页获取信息不全,可能是头信息导致的错误
#设置头信息
myheader<-c(
"User-Agent"="Mozilla/5.0 (Linux; U; Android 2.3.3; zh-cn; HTC_DesireS_S510e Build/GRI40) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1",
"Accept"="text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
"Accept-Language"="en-us",
"Connection"="keep-alive",
"Accept-Charset"="GB2312,utf-8;q=0.7,*;q=0.7"
)
2. 爬取网页乱码
url = "http://www.tianqihoubao.com/aqi/handan-201602.html"
temp = getURL(url)
#查看 temp 时发现里面的汉字出现了乱码:
temp
[1] "\r\n<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\r\n<html xmlns=\"http://www.w3.org/1999/xhtml\">\r\n<head id=\"Head1\"><title>\r\n\t2016<c4>ê2<d4><c2>oaμ|<bf><d5><c6><f8><d6>êá<bf><d6><b8>êy2é<d1>ˉ(AQI)_2<d4><c2>·Yoaμ|PM2.5àúê·êy<be>Y2é<d1>ˉ_ìì<c6><f8>oó±¨\r\n</title>
#于是又重新查看该网页的源代码,确定其编码方式。改代码为:temp = getURL(url,.encoding="GBK"),惊喜的发现出现了汉字,没有了乱码。
temp
[1] "\r\n<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\r\n<html xmlns=\"http://www.w3.org/1999/xhtml\">\r\n<head id=\"Head1\"><title>\r\n\t2016年2月邯郸空气质量指数查询(AQI)_2月份邯郸PM2.5历史数据查询_天气后报\r\n</title>
貌似一切顺利啊,查看tables中的内容还是有问题。
#于是又尝试了“UTF-8”,“ISO-8859-1”,“GB2312”等等 都是乱码。于是我又纠结了,上网谷歌,看到了一个这样的解释:尝试用R语言去抓取网页和提取信息
使用帮助查看htmlParse官方文档中的encoding的解释:
encoding:a character string (scalar) giving the encoding for the document. This is optional as the document should contain its own encoding information. However, if it doesn’t, the caller can specify this for the parser. If the XML/HTML document does specify its own encoding that value is used regardless of any value specified by the caller. (That’s just the way it goes!) So this is to be used as a safety net in case the document does not have an encoding and the caller happens to know theactual encoding.
#意思是:如果HTML中本身已经指定了编码(此处就是,但是有2个charset...前一个是GB2312,后一个是UTF-8),那么就会强制使用HTML中内部指定的编码而忽略调用者(此处我们的代码所传入的GBK),所以即使调用者指定了正确的HTML的编码,结果也还是使用HTML内部自己所指定的错误的编码(此处应该就是用了第二个charset,即UTF-8来解析的)从而导致乱码的。
大体上获取网页信息乱码,总共三种处理方法。第一,增加参数 .encoding(观察html的编码情况);第二,可以尝试设置头信息去解决; 第三,windows 出现乱码问题,需在Linux系统下执行。
3.解析网页Xpath路径
解析网页xpath路径的写法
3.1选取节点
表达式 | 描述 |
---|---|
nodename | 选取此节点的所有子节点。 |
/ | 从根节点选取。 |
// | 从匹配选择的当前节点选择文档中的节点,而不考虑它们的位置。 |
. | 选取当前节点。 |
.. | 选取当前节点的父节点。 |
@ | 选取属性。 |
路径表达式 | 结果 |
---|---|
bookstore | 选取 bookstore 元素的所有子节点。 |
/bookstore | 选取根元素 bookstore。注释:假如路径起始于正斜杠( / ),则此路径始终代表到某元素的绝对路径! |
bookstore/book | 选取属于 bookstore 的子元素的所有 book 元素。 |
//book | 选取所有 book 子元素,而不管它们在文档中的位置。 |
bookstore//book | 选择属于 bookstore 元素的后代的所有 book 元素,而不管它们位于 bookstore 之下的什么位置。 |
//@lang | 选取名为 lang 的所有属性 |
举例
1.查找页面根元素://
2.查找页面上所有的input元素://input
3.查找页面上第一个form元素内的直接子input元素(即只包括form元素的下一级input元素,使用绝对路径表示, 单/号)://form[1]/input
4.查找页面上第一个form元素内的所有子input元素(只要在form元素内的input都算,不管还嵌套了多少个其他标 签,使用相对路径表示,双//号)://form[1]//input
5.查找页面上第一个form元素://form[1]
6.查找页面上id为loginForm的form元素://form[@id='loginForm']
7.查找页面上具有name属性为username的input元素://input[@name='username']
8.查找页面上id为loginForm的form元素下的第一个input元素://form[@id='loginForm']/input[1]
9.查找页面具有name属性为contiune并且type属性为button的input元素://input[@name='continue'][@type='button']
10.查找网页中所有属性为id的元素 : //@id
3.2对于节点查找的内容进行修饰
举例
路径表达式 | 结果 |
---|---|
/bookstore/book[1] | 选取属于 bookstore 子元素的第一个 book 元素。 |
/bookstore/book[last()] | 选取属于 bookstore 子元素的最后一个 book 元素。 |
/bookstore/book[last()-1] | 选取属于 bookstore 子元素的倒数第二个 book 元素。 |
/bookstore/book[position() < 3 ] | 选取最前面的两个属于 bookstore 元素的子元素的 book 元素。 |
//title[@lang] | 选取所有拥有名为 lang 的属性的 title 元素。 |
//title[@lang='eng'] | 选取所有 title 元素,且这些元素拥有值为 eng 的 lang 属性。 |
/bookstore/book[price>35.00] | 选取 bookstore 元素的所有 book 元素,且其中的 price 元素的值须大于 35.00。 |
/bookstore/book[price>35.00]/title | 选取 bookstore 元素中的 book 元素的所有 title 元素,且其中的 price 元素的值须大于 35.00。 |
3.3选取未知节点
通配符 | 描述 |
---|---|
* | 匹配任何元素节点。 |
@* | 匹配任何属性节点。 |
node() | 匹配任何类型的节点。 |
举例
路径表达式 | 结果 |
---|---|
/bookstore/* | 选取 bookstore 元素的所有子元素。 |
//* | 选取文档中的所有元素。 |
//title[@*] | 选取所有带有属性的 title 元素。 |
3.4选取若干路径
通过在路径表达式中使用“|”运算符,您可以选取若干个路径。
路径表达式 | 结果 |
---|---|
//book/title | //book/price |
//title | //price |
/bookstore/book/title | //price |
3.5关键字
用例 | 举例 |
---|---|
text() | book/author/text() |
string() | book/author/string() |
data() | book/author/data() |
. | book/author/. |
总结
RCurl的确是一个很好地数据抓取工具,不过他的强项更多在于网页解析,你可能惊艳于RCurl强大的解析能力,短短几个关键词路径就可以提取出来很重要的数据。网络爬虫是讲呈现在网页上以非结构格式(html)存储的数据转化为结构化数据的技术,该技术非常简单易用。RCurl是R用户使用率最多的爬虫包,它简洁的语法可以解决大部分的爬虫问题。
参考文献
1.(RCurl中这么多get函数,是不是一直傻傻分不清)[https://zhuanlan.zhihu.com/p/31178486]
2.(R语言爬虫实践一)[https://blog.csdn.net/cumtzhuzheng/article/details/72830497]
3.(Xpath路径的写法)[https://www.cnblogs.com/pythonywy/p/11082153.html]