卧槽,不用Python也能爬网页数据?
来源丨清华出版社
爬虫技术是一种从网页中获 取数据的方式,是按照一定规则,自动地抓取网页数据的程序或者脚本。除了Python可以写爬虫程序外,R语言一样可以实现爬虫功能
但R语言并不适合开发一个专业的爬虫工具,因此对于开发爬虫软件或者其他相关的工作,R 语言并不是一个好的选择。对R 语言用户而言,如果仅仅想快速地获取网页上的某些信息,然后在R 语言中进行分析,那么使用R 语 言来编写爬虫代码绝对是一个好的选择。
本文讲解三个R语言实战小案例:
1.快速爬取网页数据
2.爬取BOOS直聘数据
3.模拟登录
一、快速爬取网页数据
在数据分析项目中,处理的数据大多数是结构化数据,即由行和列组成, 但是网页数据往往是非结构化的,这就需要对数据进行转换。网页的非结构 化数据可以通过网页源代码看到,如图1所示。
图1 网页源代码
图 1 显示了一个招聘网站的源代码,而招聘信息就散落在网页源代码中,这样的数据没有办法使用。这个时候就需要将网页数据爬取下载,并将其转换成结构化数据。在爬取数据之前需要做一些准备工作。首先下载相关的 R 包,并进行加载:
install.packages("rvest")
library(rvest)
然后安装GoogleChrome浏览器。要爬取网页数据,首先要知道网页数据处于网页的 什么位置。那么如何描述数据在网页中的位置?一般而言,可采用两种方式,即XPath和 Selector。
图 2显示了XPath和Selector是如何描述数据在网页中的位置的。
图2 数据定位
在图2中,“CSS选择器参考手册”这个标题在网页中的位置的描述如下。
● Selector:#main>h2 >font>font。
● XPath://*[@id="main"]/h2/font/font。
网页数据的位置本质上可以通过观察网页的结构,然后结合Selector和XPath的语法规则得出来(限于篇幅,Selector和XPath 的语法规则在本节就不进行介绍了)。另外,使 用GoogleChrome也能够快速地获取网页数据的位置。获取的方式是右击想要获取的数据,在弹出的快捷菜单中选择“检查”命令,这时界面会显示网页数据在网页代码中对应的位置,如图3 所示。
图 3 数据位置
右击对应位置的代码,在弹出的快捷菜单中选择 Copy → Copy selector 命令,如图 4所示。
图 4 右键菜单命令
这样即可获取数据对应的位置。至此,关于爬虫的准备工作已经完成。
二、rvest 简介
rvest 是 R 用户使用得最多的爬虫包,它简洁的语法可以解决大部分的爬虫问题。它的 基本使用方法如下。
使用 read_html( ) 读取网页。
● 通过 CSS 或 XPath 获取所需要的节点,并使用 html_nodes( ) 读取节点内容,再使 用 html_text( ) 提取对应节点的文本。
● 结合 stringr 包对数据进行清理。
1.rvest API
下面对 rvest 包的 API 进行一个简单总结。
(1)读取与提取。这一部分主要涉及对网页进行操作的基本函数,如表 1 所示。
(2)乱码处理。当爬取的数据存在乱码时,一般情况下是编码的问题。乱码处理函数如表 2 所示。
(3)行为模拟。当爬取一些网页需要用户进行操作时,如需要输入账号、密码,就需要用到行为模拟。行为模拟相关函数如表 3 所示。
2. rvest API 详解
下面对几个关键 rvest API 进行比较详细的介绍。
1. read_html( ) 函数
read_html ( ) 函数的主要参数如下。
● x 可以是 URL、本地路径、包含 HTML 的字符串,或者来自 HTTP 的请求。如果 x
是 URL,则参数就传递给 GET( )。
● encoding 用于指定文档的编码形式。使用 iconvlist( ) 函数可以查看完整的编码列表。如果编码方式不能确定,则可以尝试使用 stri_enc_detect ( ) 函数来获取 HTML 数据。
下面举一个简单的例子,使用到的网页链接是 https://hz.fang.anjuke.com/?from=navigation。首先加载包,然后使用 read_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 网页数据。
2. html_nodes ( ) 函数和 html_node ( ) 函数
html_nodes ( ) 与 html_node ( ) 适用于获取对应的节点数据,其参数如下。
● x :一个 xml_document 数据。
● css、xpath :要收集的节点。在 html_nodes( ) 函数和 html_node( ) 函数中传入 XPath 或者 Selector,也可以使用浏览器 Google Chrome 辅助获取网页数据的 XPath 或者 Selector。
仍以前一部分使用的连接为例子,尝试获取其中的部分数据,如图 5 所示。
如图5所示,如果我们想获取楼盘“ 东原旭辉璞阅”这个位置的数据,可利用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爬取简单的数据了。
三、爬取 BOSS 直聘数据
本节尝试爬取 BOSS 直聘数据
网页的链接:
https://www.zhipin.com/job_detail/?query=%E6%95%B0%E6%8D%AE%E5%88%86%E6%9E%90&scity=101210100&industry=&position=
具体的网页如图 6 所示。
图 6 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" class="search-job-list-wrap">\n<script>\n
...
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 数据分析 中软国际 10k-15k 杭州 3-5年本科
## 2 数据分析师 同盾科技 10k-20k 杭州 余杭区 仓前3-5年硕士
## 3 数据分析 爱唯 6k-10k 杭州 1-3年大专
## 4 数据分析 边锋 13k-20k 杭州 西湖区 文一路3-5年本科
## 5 数据分析 银江研究院 8k-16k 杭州 西湖区 三墩1-3年本科
## 6 数据分析专家 网易杭州 10k-20k 杭州 滨江区 长河3-5年本科
到这里,就已经爬取了一个页面的数据,并且将数据结构化了。然后,爬取翻页后的 其他数据,这时就需要观察翻页之后网址的变化:
第一页的网址:
https://www.zhipin.comjob_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%A E%E5%88%86%E6%9E%90&page=2&ka=page-next
观察发现,只需要修改页码就可以获取对应的页面。于是,编写一个循环语句,修改页码即可。
for (i in 2:10) {
url <- paste('https://www.zhipin.com/c101210100/?query=%E6%95%B0%E6%8
D%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%E
5%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 数据分析 中软国际 10k-15k 杭州 3-5年本科
## 2 数据分析师 同盾科技 10k-20k 杭州 余杭区 仓前3-5年硕士
## 3 数据分析 爱唯 6k-10k 杭州 1-3年大专
## 4 数据分析 边锋 13k-20k 杭州 西湖区 文一路3-5年本科
## 5 数据分析 银江研究院 8k-16k 杭州 西湖区 三墩1-3年本科
## 6 数据分析专家 网易杭州 10k-20k 杭州 滨江区 长河3-5年本科
可以看到,这里一共爬取了 300 条数据。通过这种方式,网页中对应页面的数据就被爬取下来了。
四、模拟登录
很多爬取的网页是需要先登录的,所以需要在爬取网页数据之前模拟登录,然后进行数据的爬取。
模拟登录案例的网址为:
https://login.medscape.com/login/sso/getlogin?ac=401&urlCache=aHR0cHM6Ly93d3cubWVkc2NhcGUuY29tL3ZpZXdhcnRpY2xlLzg4NDY3Ng==
其 对应的页面如图 7 所示。
图 7 对应的页面
模拟登录的第一步是模拟对话。通过 html_session( ) 函数模拟与服务器的会话,然后使 用 html_form( ) 来解析网页的表单,希望从中找到 username 和 password 的数据位置。
library(xml2)
library(rvest)
# 取地址,用html_session模拟会话
url <- 'https://login.medscape.com/login/sso/getlogin?ac=401&urlCache=a
HR0cHM6Ly93d3cubWVkc2NhcGUuY29tL3ZpZXdhcnRpY2xlLzg4NDY3Ng==' pgsession <- html_session(url)
pgsession
## <session> https://login.medscape.com/login/sso/getlogin?urlCache=aHR
0cHM6Ly93d3cubWVkc2NhcGUuY29tL3ZpZXdhcnRpY2xlLzg4NDY3Ng==&ac=401
## Status: 200
## Type: text/html;charset=UTF-8
## Size: 50574
# 使用html_form 来解析网页的表单
pgform <- html_form(pgsession) # 在这里找,列表的第几个元素包含了username、
password 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': aHR0cHM6Ly93d3cubWVkc2NhcGUuY29tL3ZpZXd hcnRpY2xlLzg4NDY3Ng==
## <input hidden> 'spa':
## <input hidden> 'stepUp': false
## <input hidden> 'facilitatedUrl':
## <input text> 'userId':
## <input password> 'password':
## <input hidden> 'remember': on
## <input submit> 'loginbtn': Log In
pgform1 <- pgform[[3]] # 这里提取对应的列表,第三个
在上面一步的代码中,使用 html_session( ) 传入需要登录的页面,然后使用 html_form ( ) 解析网页的表单,再在解析的表单中找到 username、password 在解析结果列表中的位置,最 后提取对应列表的解析结果。这样做的目的是找到填写账号、密码的表单。如上面的结果 所示,账号、密码对应着第三个列表。
接下来填写账号与密码。使用 set_values( ) 来填写表单中的账号、密码,然后通过 submit_form( ) 进行提交。
filled_form <- set_values(pgform1,
'userId'='[email protected]',
'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: 102961
set_values ( ) 用于设置账号和密码,submit_form ( ) 用于提交账号和密码。如果提交完 成之后登录成功,就会看类似这样的登录信息:
http://www.medscape.com/viewarticle/884676 Status: 200 Type: text/
html;charset=UTF-8 Size: 86339
Status 200 表示请求顺利。
登录成功之后的页面如图8 所示。
图 8 登录成功之后的页面
登录成功之后,即可爬取网页。爬取网页数据的方法就与之前讲解的一样。例如,爬取文章:
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."
这样,就完成了登录的模型,并可以进一步爬取数据。
五、总结
网络是获取数据的一个重要渠道,但是如果想要获取网页中的数据,那么就必须掌握爬虫这门工具,以便从网页中爬取数据。虽然 R 语言是进行数据分析的优秀工具,但是 R 语言并不是专业开发爬虫软件的工具,这并不妨碍使用 R 语言编写爬虫代码、爬取数据。 当需要快速爬取网页数据,并进行分析时,R 语言是一个非常好的选择。使用 R 语言能够 非常快速地完成爬虫和数据分析的工作。本文章介绍了如何使用 R 语言爬取网络数据,如何 爬取多网页的数据,以及行为模拟。
当然,很多关于爬虫的内容在本章没有涉及,但是对于想要快速爬取数据的 R 用户而言,这些已经足够了,因为绝大部分情况下可以使用这样 的方式来获取网页数据。