课程背景:本课程需要一点R语言基础,以及了解一点CSS
选择器的知识,不过没有相关的知识同样也不需要担心,所有的背景知识都很简单
学完之后能够做什么:通过本课程知识的讲解,加上案例的分析,同学可以学会使用R语言快速的爬取网络上的数据
如何使用Rvest
爬取网页
Rvest
简介Rvest API
介绍Rvest API
详解完整案例
网络爬虫是将呈现在网页上以非结构格式(html
)存储的数据转化为结构化数据的技术
非结构化的数据通过查看网页的源代码就可以看到:
结构化的数据通常就是,由行和列组成的数据,行代表观测,列代表特征或变量:
结构化数据
任何分析建模,都需要数据来作为基础。而网络,是获取数据一个非常重要的渠道,R语言网络爬虫能够帮助你快速的爬取一些你需要的数据
下载相关的包
install.packages("rvest")
install.packages("tidyverse")
library(rvest)
library(tidyverse)
安装 Google Chrome
浏览器
要爬取网页上的数据,首先要知道的是,网页上的数据处于网页中的什么位置。那么如何去描述网页上的位置,可以通过 Xpath
和 selector
举一个例子:
CSS选择器 这个标题,在网页中的位置描述就是:
selector
: #main > h2 > font > font
Xpath
: //*[@id="main"]/h2/font/font
这个位置描述本质上可以通过观察网页的结构得出来,但是使用Google Chrome
能够快速的获取数据的位置
Rvest
简介rvest
是R
用户使用率最多的爬虫包,它简洁的语法可以解决大部分的爬虫问题。
基本使用方法:
read_html
读取网页;CSS
或Xpath
获取所需要的节点并使用html_nodes
读取节点内容;stringr
包对数据进行清理。读取与提取:
read_html()
读取html文档的函数html_nodes()
选择提取文档中指定元素的部分html_name()
提取标签名称;html_text()
提取标签内的文本;html_attr()
提取指定属性的内容;html_attrs()
提取所有的属性名称及其内容;html_table()
解析网页数据表的数据到R的数据框中;html_form()
提取表单。乱码处理:
guess_encoding()
用来探测文档的编码,方便我们在读入html
文档时设置正确的编码格式repair_encoding()
用来修复html
文档读入后的乱码问题行为模拟:
set_values()
修改表单submit_form()
提交表单html_session()
模拟HTML浏览器会话jump_to()
得到相对或绝对链接follow_link()
通过表达式找到当前页面下的链接session_history()
历史记录导航工具Rvest API
详解read_html
参数:x
可以是url
,本地路径,包含html
的字符串,或者来自httr
的请求如果x
是URL
参数就传递给GET()
encoding
文档的编码形式,查看iconvlist()
有完整列表,他如果不能正确确定encoding
方式可以尝试stri_enc_detect
使用这个函数来获取html
数据require(rvest)
HTML <- read_html(x = "https://hz.fang.anjuke.com/?from=navigation")
HTML
## {xml_document}
## <html>
## [1] <head>\n<meta http-equiv="Content-Type" content="text/html; charset= ...
## [2] <body>\n<div id="header">\n <div class="top-banner">\n ...
以上就获取了猎聘网的html
网页数据
html_nodes
与html_node
参数:x
一个xml_document数据css
, xpath
要收集的节点。传入XPath和selector,也可以使用浏览器Google Chrome辅助网页
比如我们想获取楼盘名称,“东原旭辉璞阅”这个位置的数据,利用html_node
NAME <- HTML %>% html_nodes("#container > div.list-contents > div.list-results > div.key-list.imglazyload > div:nth-child(1) > div > a.lp-name > h3 > span")
NAME
## {xml_nodeset (1)}
## [1] <span class="items-name">绿都鉴未来</span>
这样我们就获得了对应位置的节点,想要得到对应节点的数据,使用html_text
函数
NAME %>% html_text()
## [1] "绿都鉴未来"
到这里,就可以使用Rvest爬取简单的数据了
包外API
head()
检查数据as.numeric()
转化为数值型gsub()
移除" “,”“,”“等as.factor()
转化为因子data.frame()
合并为数据框trycatch()
错误处理write.csv()
输出csv文件file.download()
下载网络文件read.table()
读取本地文件save()
保存变量到本地sapply()
向量化调用函数iconv()
转化编码格式rbind()
合并行paste()
连接字符串,多用来生成有规律的链接地址相关的 package
stringr
字符串处理RSelenium
模拟网页操作,可抓取动态页面ggplot2
数据可视化magrittr
辅助rvest
使用技巧
html
标签,用到了magrittr
包里的extract2
函数。下面两行代码都可以获得该网页中第一个<table>
标签
ateam %>% html_nodes("table") %>% extract2(1) %>% html_nodes("img")
ateam %>% html_nodes("table") %>%
[[(1) %>% html_nodes("img")
%>%
是管道操作符,意思是把左边的操作结果作为参数传递给右边的命令html_nodes+stringr
更方便,不使用html_text
网址:https://www.zhipin.com/job_detail/?query=%E6%95%B0%E6%8D%AE%E5%88%86%E6%9E%90&scity=101210100&industry=&position=
网页:
BOSS
主要爬取四个字段:
# 网页网址
url <- "https://www.zhipin.com/job_detail/?query=%E6%95%B0%E6%8D%AE%E5%88%86%E6%9E%90&scity=101210100&industry=&position="
# 获取网址
HTML <- read_html(url)
HTML
## {xml_document}
## <html class="standard">
## [1] <head>\n<meta http-equiv="Content-Type" content="text/html; charset= ...
## [2] <body>\n<div id="wrap">\n \n \n\n\n\n\n\n\n<script>\n var s ...
JobName <- HTML %>% html_nodes('#main > div > div.job-list > ul > li > div > div.info-primary > h3 > a > div.job-title') %>% html_text()
CompanyName <- HTML %>% html_nodes('#main > div > div.job-list > ul > li > div > div.info-company > div > h3 > a') %>% html_text()
Salary <- HTML %>% html_nodes('#main > div > div.job-list > ul > li > div > div.info-primary > h3 > a > span') %>% html_text()
Description <- HTML %>% html_nodes('#main > div > div.job-list > ul > li > div > div.info-primary > p') %>% html_text()
Result <- data.frame(JobName,CompanyName,Salary,Description)
head(Result)
## JobName CompanyName Salary Description
## 1 数据分析师 阿里巴巴集团 30k-60k 杭州 余杭区 仓前3-5年本科
## 2 数据分析专员 云车金融 10k-20k 杭州 萧山区 宁围1-3年本科
## 3 数据分析 爱唯 6k-10k 杭州 下城区 1-3年大专
## 4 数据分析主管 浙江上佰 7k-12k 杭州 西湖区 古墩路3-5年本科
## 5 数据分析师 吉利集团 10k-20k 杭州 滨江区 西兴5-10年大专
## 6 数据分析师 海康威视 12k-18k 杭州 滨江区 西兴1-3年硕士
其次,爬取翻页后的其他数据,这个时候就需要观察,翻页之后网址的变化:
第一页的网址:
https://www.zhipin.com/job_detail/?query=%E6%95%B0%E6%8D%AE%E5%88%86%E6%9E%90&scity=101210100&industry=&position=
第二页的网址:
https://www.zhipin.com/c101210100/?query=%E6%95%B0%E6%8D%AE%E5%88%86%E6%9E%90&page=2&ka=page-next
观察发现,只需要修改page = ,修改成不同的页码就对应相应的页面。于是,写一个循环,修改页码即可
for (i in 2:10) {
url <- paste('https://www.zhipin.com/c101210100/?query=%E6%95%B0%E6%8D%AE%E5%88%86%E6%9E%90&page=',i,'&ka=page-next',sep = "")
url <- "https://www.zhipin.com/job_detail/?query=%E6%95%B0%E6%8D%AE%E5%88%86%E6%9E%90&scity=101210100&industry=&position="
HTML <- read_html(url)
HTML
JobName <- HTML %>% html_nodes('#main > div > div.job-list > ul > li > div > div.info-primary > h3 > a > div.job-title') %>% html_text()
CompanyName <- HTML %>% html_nodes('#main > div > div.job-list > ul > li > div > div.info-company > div > h3 > a') %>% html_text()
Salary <- HTML %>% html_nodes('#main > div > div.job-list > ul > li > div > div.info-primary > h3 > a > span') %>% html_text()
Description <- HTML %>% html_nodes('#main > div > div.job-list > ul > li > div > div.info-primary > p') %>% html_text()
Result1 <- data.frame(JobName,CompanyName,Salary,Description)
Result <- rbind(Result,Result1)
}
dim(Result)
## [1] 300 4
head(Result)
## JobName CompanyName Salary Description
## 1 数据分析师 阿里巴巴集团 30k-60k 杭州 余杭区 仓前3-5年本科
## 2 数据分析专员 云车金融 10k-20k 杭州 萧山区 宁围1-3年本科
## 3 数据分析 爱唯 6k-10k 杭州 下城区 1-3年大专
## 4 数据分析主管 浙江上佰 7k-12k 杭州 西湖区 古墩路3-5年本科
## 5 数据分析师 吉利集团 10k-20k 杭州 滨江区 西兴5-10年大专
## 6 数据分析师 海康威视 12k-18k 杭州 滨江区 西兴1-3年硕士
require(tidyverse)
CN <- as.data.frame(table(Result$CompanyName)) %>% arrange(desc(Freq))
CN
## Var1 Freq
## 1 阿里巴巴集团 20
## 2 城云科技 20
## 3 浙江上佰 20
## 4 浙江天鹂科技网络 20
## 5 vivo 20
## 6 爱财集团 10
## 7 爱唯 10
## 8 安恒信息 10
## 9 垂衣 10
## 10 海康威视 10
## 11 杭州南境网络科技... 10
## 12 杭州追灿科技 10
## 13 好衣库 10
## 14 黑羊科技 10
## 15 机蜜 10
## 16 吉利集团 10
## 17 览众数据 10
## 18 蚂蚁金服 10
## 19 亲宝宝 10
## 20 水轻轻 10
## 21 网易 10
## 22 原廷贸易 10
## 23 云车金融 10
## 24 助创科技 10
## 25 misscandy健康指彩 10
require(stringr)
Salary<- Result$Salary %>% str_remove_all(pattern = 'k') %>% str_split(pattern = "-",simplify = T)
Salary <- data.frame(Salary)
Salary$X1 <- as.numeric(as.character(Salary$X1))
Salary$X2 <- as.numeric(as.character(Salary$X2))
p1 <- ggplot(data = Salary,aes(x=X1)) + geom_histogram(aes(y=..density..),
binwidth=.5,
colour="black", fill="white") +
geom_density(alpha=.2, fill="#FF6666") # 重叠部分采用透明设置
p1 + xlab(label = "薪资的下界")+theme(text = element_text(family = 'STKaiti'))
p2 <- ggplot(data = Salary,aes(x=X2)) + geom_histogram(aes(y=..density..),
binwidth=.5,
colour="black", fill="white") +
geom_density(alpha=.2, fill="#FF6666") # 重叠部分采用透明设置
p2 + xlab(label = "薪资的上界")+theme(text = element_text(family = 'STKaiti'))
模拟登陆案例的网址如下:http://www.medscape.com/viewarticle/884676
模拟登陆
首先要模拟对话
library(xml2)
library(rvest)
# 取地址,用html_session模拟会话
url <- 'http://www.medscape.com/viewarticle/884676'
pgsession <- html_session(url)
pgsession
## <session> https://login.medscape.com/login/sso/getlogin?urlCache=aHR0cHM6Ly93d3cubWVkc2NhcGUuY29tL3ZpZXdhcnRpY2xlLzg4NDY3Ng==&ac=401
## Status: 200
## Type: text/html;charset=UTF-8
## Size: 50905
# 使用html_form 来解析网页的表单
pgform <- html_form(pgsession) # 在这里找,列表的第几个元素包含了username,passward
pgform
## [[1]]
## <form> 'search-form-header' (GET javascript:subsearchheadertrack('en');)
## <input hidden> 'searchSrc': news
## <input text> 'q':
## <button submit> '<unnamed>
## <button button> '<unnamed>
##
## [[2]]
## <form> 'search-form-header' (GET javascript:subsearchheadertrack('en');)
## <input hidden> 'searchSrc': news
## <input text> 'q':
## <button submit> '<unnamed>
##
## [[3]]
## <form> 'loginRequest' (POST /login/sso/login)
## <input hidden> 'urlCache': aHR0cHM6Ly93d3cubWVkc2NhcGUuY29tL3ZpZXdhcnRpY2xlLzg4NDY3Ng==
## <input hidden> 'spa':
## <input hidden> 'stepUp': false
## <input hidden> 'facilitatedUrl':
## <input text> 'userId':
## <input password> 'password':
## <input checkbox> 'remember':
## <input submit> 'loginbtn': Log In
pgform1 <- pgform[[3]] # 这里提取对应的列表,第三个
接下来就是填写账号与密码:
filled_form <- set_values(pgform1,
'userId'='15527504293@163.com',
'password'='h89paAybMt8ecku')
# 提交
sbmt <- submit_form(pgsession,filled_form)
## Submitting with 'loginbtn'
sbmt
## <session> https://www.medscape.com/viewarticle/884676
## Status: 200
## Type: text/html;charset=UTF-8
## Size: 105761
如果成功,就会看类似这样的登录状态: ·
Status 200表示请求顺利
登陆之后就原url就会变成这样:
登陆成功之后的页面
登陆之后,爬取其他数据就与我们之前讲解的一样,爬取文章:
Text <- sbmt%>%html_nodes('div.article-content-wrapper div p')%>%html_text(trim = T)
head(Text)
## [1] "Nothing seemed to help the patient — and hospice staff didn’t know why."
## [2] "They sent home more painkillers for weeks. But the elderly woman, who had severe dementia and incurable breast cancer, kept calling out in pain."
## [3] "The answer came when the woman’s daughter, who was taking care of her at home, showed up in the emergency room with a life-threatening overdose of morphine and oxycodone. It turned out she was high on her mother’s medications, stolen from the hospice-issued stash."
## [4] "Dr. Leslie Blackhall handled that case and two others at the University of Virginia’s palliative care clinic, and uncovered a wider problem: As more people die at home on hospice, some of the powerful, addictive drugs they are prescribed are ending up in the wrong hands."
## [5] "Hospices have largely been exempt from the national crackdown on opioid prescriptions because dying people may need high doses of opioids. But as the nation’s opioid epidemic continues, some experts say hospices aren’t doing enough to identify families and staff who might be stealing pills. And now, amid urgent cries for action over rising overdose deaths, several states have passed laws giving hospice staff the power to destroy leftover pills after patients die."
## [6] "Blackhall first sounded the alarm about drug diversion in 2013, when she found that most Virginia hospices she surveyed didn’t have mandatory training and policies on the misuse and theft of drugs. Her study spurred the Virginia Association for Hospices and Palliative Care to create new guidelines, and prompted national discussion."
爬取的内容可能会随时间变化,因为网页会发生变化.同学们在自己操作的时候需要注意一下
欢迎添加我的微信:
扫微信