R语言数据抓取实战——RCurl+XML组合与XPath解析
经常有小伙伴儿跟我咨询,在使用R语言做网络数据抓取时,遇到空值和缺失值或者不存在的值,应该怎么办。
因为我们大多数场合从网络抓取的数据都是关系型的,需要字段和记录一一对应,但是html文档的结构千差万别,代码纷繁复杂,很难保证提取出来的数据开始就是严格的关系型,需要做大量的缺失值、不存在内容的判断。
如果原始数据是关系型的,但是你抓取来的是乱序的字段,记录无法一一对应,那么这些数据通常价值不大,今天我以一个小案例(跟昨天案例相同)来演示,如何在网页遍历、循环嵌套中设置逻辑判断,适时的给缺失值、不存在值填充预设值,让你的爬虫代码更稳健,输出内容更规整。
加载扩展包:
#加载包:
library("XML")
library("stringr")
library("RCurl")
library("dplyr")
library("rvest")
#提供目标网址链接/报头参数
url<-'https://read.douban.com/search?q=Python'
header =c('User-Agent'='Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.79 Safari/537.36')
构建抓取函数:
getcontent<-function(url){
#这个数据框是为最终的数据汇总返回提供的初始值
myresult=data.frame()
#这些空向量是遍历单页书籍记录提供的初始值
title=author=category=subtitle=eveluate_nums=rating=price=c()
#开始遍历网页
for (page in seq(0,3)){
#遍历不同页面
link<-paste0(url,'&start=',page*10)
#请求网页并解析
content<-getURL(link,httpheader=header) %>% htmlParse()
#计算单页书籍条目数
length<-content %>% xpathSApply(.,"//ol[@class='ebook-list column-list']/li") %>% xmlSize()
###提取标题:
title<-content %>% xpathSApply(.,"//ol/li//div[@class='title']/a| //ol/li//h4/a",xmlValue) %>% c(title,.)
###提取图书类别:
category=content %>% xpathSApply(.,"//span[@class='category']/span[2]/span | //p[@class='category']/span[@class='labled-text'] | //div[@class='category']",xmlValue) %>% c(category,.)
###提取作者/副标题/评论数/评分/价格信息:
author_text=subtitle_text=eveluate_nums_text=rating_text=price_text=rep('',length)
for (i in 1:length){
###提取作者
author_text[i]=content %>% xpathSApply(.,sprintf("//li[%d]//p[@class]//span/following-sibling::span/a | //li[%d]//div[@class='author']/a",i,i),xmlValue) %>% paste(.,collapse='/')
###考虑副标题是否存在
if (content %>% xpathSApply(.,sprintf("//ol/li[%d]//p[@class='subtitle']",i),xmlValue) %>% length!=0){
subtitle_text[i]=content %>% xpathSApply(.,sprintf("//ol/li[%d]//p[@class='subtitle']",i),xmlValue)
}
###考虑评价是否存在:
if (content %>% xpathSApply(.,sprintf("//ol/li[%d]//a[@class='ratings-link']/span",i),xmlValue) %>% length!=0){
eveluate_nums_text[i]=content %>% xpathSApply(.,sprintf("//ol/li[%d]//a[@class='ratings-link']/span",i),xmlValue)
}
###考虑评分是否存在:
if (content %>% xpathSApply(.,sprintf("//ol/li[%d]//div[@class='rating list-rating']/span[2]",i),xmlValue) %>% length!=0){
rating_text[i]=content %>% xpathSApply(.,sprintf("//ol/li[%d]//div[@class='rating list-rating']/span[2]",i),xmlValue)
}
###考虑价格是否存在:
if (content %>% xpathSApply(.,sprintf("//ol/li[%d]//span[@class='price-tag ']",i),xmlValue) %>% length!=0){
price_text[i]=content %>% xpathSApply(.,sprintf("//ol/li[%d]//span[@class='price-tag ']",i),xmlValue)
}
}
#拼接以上通过下标遍历的书籍记录数
author=c(author,author_text)
subtitle=c(subtitle,subtitle_text)
eveluate_nums=c(eveluate_nums,eveluate_nums_text)
rating=c(rating,rating_text)
price=c(price,price_text)
#打印单页任务状态
print(sprintf("page %d is over!!!",page))
}
#构建数据框
myresult=data.frame(title,subtitle,author,category,price,rating,eveluate_nums)
#打印总体任务状态
print("everything is OK")
#返回最终汇总的数据框
return(myresult)
}
提供url链接并运行我们构建的抓取函数:
myresult=getcontent(url)
[1] "page 0 is over!!!"
[1] "page 1 is over!!!"
[1] "page 2 is over!!!"
[1] "page 3 is over!!!"
[1] "everything is OK"
查看数据结构:
str(myresult)
规范变量类型:
myresult$price<-myresult$price %>% sub("元|免费","",.) %>% as.numeric()
myresult$rating<-as.numeric(myresult$rating)
myresult$eveluate_nums<-as.numeric(myresult$eveluate_nums)
预览数据:
DT::datatable(myresult)
构建自动化抓取函数,其实挑战不仅仅是缺失值、不存在值的处理,变量作用域的设置也至关重要,以上自动以函数中使用了两层for循环嵌套,在内层for循环中还使用了四个if 判断,个别字段的XPath路径不唯一,为了数据规范,我在XPath中使用了多重路径“|”。
判断缺失值(或者填充不存在值)的一般思路就是遍历每一页的每一条记录的XPath路径,判断其length,倘若为0基本就可以判断该对应记录不存在。
通过设置一个长度为length的预设向量,仅需将那些存在的(长度不为0)记录通过下标插入对应位置即可,if判断可以只写一半(后半部分使用预设的空值)。
至于里面让人眼花缭乱的XPath表达式,请参考这一篇,你可以直接去W3C school查看完整版!
左手用R右手Python系列16——XPath与网页解析库
Python网络数据抓取实战——Xpath解析豆瓣书评 往期案例数据请移步本人GitHub: https://github.com/ljtyduyu/DataWarehouse/tree/master/File
- 十六个有用的Linux命令行技巧
- keras系列︱深度学习五款常用的已训练模型
- 基于VGG19的识别中国人、韩国人、日本人分类器
- 机器学习算法GBDT的面试要点总结
- 了解、接受和利用Java中的Optional (类)
- 一个强化学习 Q-learning 算法的简明教程
- 天池大赛—商场中精确定位用户所在店铺 作品分享
- 代码实战:从单体式应用到微服务的低风险演变
- 数据转换:从单体式应用到微服务的低风险演变
- JDK8 stream toMap() java.lang.IllegalStateException: Duplicate key异常解决(key重复)
- 如约而至,Java 10 正式发布!
- Intellij IDEA查看所有断点
- Spring Boot国际化支持
- 有记忆会推理的可微分神经计算机,DeepMind现在开源了代码
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- CS学习笔记 | 16、用户枚举三个关键步骤
- Python数据分析实战(1)数据分析概述
- JVM面试常问知识点
- Python 为什么没有 void 关键字?
- FPGA设计心得(10)关于行为仿真的一点观点
- 一、Axios基础
- 二、fetch中的基础语法
- Laradock 运行 Nuxt 的一些问题
- Spring缓存注解@Cacheable、@CacheEvict、@CachePut
- 微信小程序设置请求超时
- SAP CRM One Order函数CREATE_OW的设计原理
- 决策树(decision tree)
- 寻找质数—埃式筛法
- 语义分割之Dice Loss深度分析
- SAP CRM One Order函数SAVE_EC的设计原理