卧槽, R 语言也能爬取网页的数据!

2022-04-08 10:39:52 浏览数 (1)

大家好,我是辰哥~

爬虫技术是一种从网页中获 取数据的方式,是按照一定规则,自动地抓取网页数据的程序或者脚本。除了Python可以写爬虫程序外,R语言一样可以实现爬虫功能

但R语言并不适合开发一个专业的爬虫工具,因此对于开发爬虫软件或者其他相关的工作,R 语言并不是一个好的选择。对R 语言用户而言,如果仅仅想快速地获取网页上的某些信息,然后在R 语言中进行分析,那么使用R 语 言来编写爬虫代码绝对是一个好的选择。

本文讲解三个R语言实战小案例:

代码语言:javascript复制
1.快速爬取网页数据
2.爬取BOOS直聘数据
3.模拟登录

上面三个实战案例来自于《深入浅出R语言数据分析》一书,这只是书中的其中一部分

如果平时对R语言数据分析比较感兴趣,今天文末也会送出几本关于R语言数据分析的书!

一、快速爬取网页数据

在数据分析项目中,处理的数据大多数是结构化数据,即由行和列组成, 但是网页数据往往是非结构化的,这就需要对数据进行转换。网页的非结构 化数据可以通过网页源代码看到,如图1所示。

图1 网页源代码

图 1 显示了一个招聘网站的源代码,而招聘信息就散落在网页源代码中,这样的数据没有办法使用。这个时候就需要将网页数据爬取下载,并将其转换成结构化数据。在爬取数据之前需要做一些准备工作。首先下载相关的 R 包,并进行加载:

代码语言:javascript复制
install.packages("rvest")
library(rvest) 

然后安装GoogleChrome浏览器。要爬取网页数据,首先要知道网页数据处于网页的 什么位置。那么如何描述数据在网页中的位置?一般而言,可采用两种方式,即XPath和 Selector。

图 2显示了XPath和Selector是如何描述数据在网页中的位置的。

图2 数据定位

在图2中,“CSS选择器参考手册”这个标题在网页中的位置的描述如下。

代码语言:javascript复制
●  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( ) 读取网页。

代码语言:javascript复制
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( ) 函数。

代码语言:javascript复制
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( ) 函数。

代码语言:javascript复制
NAME %>% html_text()
## [1] "东原旭辉璞阅"

至此,就可以使用rvest爬取简单的数据了。

三、爬取 BOSS 直聘数据

本节尝试爬取 BOSS 直聘数据

代码语言:javascript复制
网页的链接: 
https://www.zhipin.com/job_detail/?query=数据分析&scity=101210100&industry=&position=

具体的网页如图 6 所示。

图 6 BOSS 直聘

在这个案例中,主要爬取 4 个字段:职位名称、公司名称、薪资范围,以及地址、年限等信息。爬取此网页的信息,首先要获取一个页面中所有数据的路径,进而获取这个页面的数据,获取下来之后,将数据合并成一个数据框。

代码语言:javascript复制
# 网页网址 url <- "https://www.zhipin.com/ job_detail
/?query=数据分析&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年本科

到这里,就已经爬取了一个页面的数据,并且将数据结构化了。然后,爬取翻页后的 其他数据,这时就需要观察翻页之后网址的变化:

代码语言:javascript复制
第一页的网址:
https://www.zhipin.comjob_detail/?query=数据% E5��析&scity=101210100&industry=&position=

第二页的网址:
https://www.zhipin.com/c101210100/?query=数�%A E分析&page=2&ka=page-next

观察发现,只需要修改页码就可以获取对应的页面。于是,编写一个循环语句,修改页码即可。

代码语言:javascript复制
for (i in 2:10) {
url <- paste('https://www.zhipin.com/c101210100/?query=数�%8
D�分析&page=',i,'&ka=page-next',sep = "")
url <- "https://www.zhipin.com/job_detail/?query=数据%E
5��析&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 条数据。通过这种方式,网页中对应页面的数据就被爬取下来了。

四、模拟登录

很多爬取的网页是需要先登录的,所以需要在爬取网页数据之前模拟登录,然后进行数据的爬取。

代码语言:javascript复制
模拟登录案例的网址为:
https://login.medscape.com/login/sso/getlogin?ac=401&urlCache=aHR0cHM6Ly93d3cubWVkc2NhcGUuY29tL3ZpZXdhcnRpY2xlLzg4NDY3Ng==

其 对应的页面如图 7 所示。

图 7 对应的页面

模拟登录的第一步是模拟对话。通过 html_session( ) 函数模拟与服务器的会话,然后使 用 html_form( ) 来解析网页的表单,希望从中找到 username 和 password 的数据位置。

代码语言:javascript复制
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( ) 进行提交。

代码语言:javascript复制
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:  102961

set_values ( ) 用于设置账号和密码,submit_form ( ) 用于提交账号和密码。如果提交完 成之后登录成功,就会看类似这样的登录信息:

代码语言:javascript复制
http://www.medscape.com/viewarticle/884676 Status: 200 Type: text/
html;charset=UTF-8 Size: 86339

Status 200 表示请求顺利。

登录成功之后的页面如图8 所示。

图 8 登录成功之后的页面

登录成功之后,即可爬取网页。爬取网页数据的方法就与之前讲解的一样。例如,爬取文章:

代码语言:javascript复制
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 用户而言,这些已经足够了,因为绝大部分情况下可以使用这样 的方式来获取网页数据。

0 人点赞