【R分享|实战】利用rvest包编写简单的静态网页爬虫
把梦想藏在秋风中,闯过灯火阑珊。"R实战"专题·第21篇编辑 | 明允 5160字 |13分钟阅读本期推送内容一次有趣的尝试:在学习《基于R语言的自动数据收集》过程中,对HTML文件与各类R语言爬虫包(XML包、rvest包、Rcrawler包、Rseleuium包)有了大致的了解。在浏览知乎的一个专栏时,我发现里面的图片都很有意思,但手动一张一张地下载未免太过麻烦,便着手写一个爬虫程序批量下载这些图片,刚好rvest包就可以满足这个需求。实际上,只要设定好正确的抓取取规则,爬虫便可以自动的抓取网页上的各类信息数据,可以极大地提高我们的工作效率。今天的主要内容便是介绍爬虫的基本知识,并通过两个小案例介绍R语言如何爬取静态网页中的信息、图片内容。01什么是网络爬虫1)网络爬虫:网络爬虫又叫网页蜘蛛,网页机器人,是一种按照一定的规则,自动地抓取万维网信息的程序或者脚本,我们可以把它理解成一种自动搜索引擎。它可在多种编程语言中实验,例如Java、Python、R语言等等。2)爬取静态网页的基本流程:在这之前,我们要了解:什么是静态网页,如何分辨静态网页?顾名思义,静态网页可以理解为内容基本固定,不会改变的网页。它的网页中没有程序代码,每个静态网页都是一个独立的HTML文件,所有的内容都储存在服务器中原始的HTML文件里,网页URL多以.htm、.html、.xml、.xhtml等形式为后缀。与之对立的便是动态网页了,所谓 “ 动态 ” ,并不是指网页上简单的 GIF 动态图片或是 Flash 动画。制作者在动态网页中嵌套了程序,将更新较快的信息内容与网页框架分离并储存在数据中,方便网页根据需求进行调用。这样,我们浏览的每一个界面,自然不能与服务器中的某个HTML文件相对应。迄今为止,动态网页仍缺少一个统一的标准,我们可以观察到的是,动态网页URL多以.asp、.jsp、.php、.cgi等姓氏为后缀。通常来说,抓取动态网页内容的操作比抓取静态网页的内容更为繁琐复杂,正确的区分网页的类型十分重要。
在编写爬虫程序之前,我们要明确爬虫程序的步骤:a. 需要爬取的网页:起始URLb. 分析网页内容 :自己感兴趣的内容在网页中的哪个位置c. 提取感兴趣的部分d. 数据内容清洗 :爬取的数据容易带有多余的空格或"\n\t"等字符,需要后续整理02简单介绍rvest包rvest包是目前R用户中最流行的爬虫包,可以解决大部分的爬虫问题。主要函数:read_html()读取目标网页中的html文档,可以是线上的url,也可以是本地的html文件html_nodes()选择并提取html文档中目标元素html_attar()抓取指定属性的内容html_text()抓取标签内的文本html_table()解析网页数据表的数据到R的数据框中html_form( )抓取表单repair_encoding( ) 用来修复html文档在R读入后的乱码问题(在读取中文内容时特别常见)使用rvest抓取信息十分简单粗暴,一般分为三步:url<- "#网页网址" read_html(url) %>% #明确要抓取的内容与位置,并读入相应的html文件 html_nodes() %>% #通过相应的标签名,属性定位到该内容的位置 html_attar() 或 html_text() 提取目的信息,随后可以根据需求进行部分处理03爬取知乎专栏中的猫猫图现在让我们开始抓取知乎某个回答中的图片内容,指路:https://zhuanlan.zhihu.com/p/156799430library(rvest)library(downloader)library(tidyverse)url1<-"https://zhuanlan.zhihu.com/p/156799430"抓取的第一步是确定目标图片在网页文件中的定位,我们可以通过对比每张图片在html文件里的位置来找到他们整体的规律。首先,我们把鼠标光标放在第一张图片上,右键-检查:
可以发现,这张图片实际上位于右边第二个红色框<img src="https://pic1.zhimg.com/80/v2-3a8073d_1440w.jpg"......(等)>的节点中,这个节点分别提供了src与data-actualsrc两个链接,复制到浏览器中分别可以打开正常图片和原始图片。接下来,我们要确定第二张以及之后全部图片的位置,并取得所有图片的链接。
通过比对可以发现,几乎所有图片都储存在图中蓝框标出的<figure data-size="normal"></figure>中,而且图片链接都储存在src和data-actualsrc下。往上溯源,可以发现,所有的<figure></figure>都位于红框中的<div class="RichText ztext Post-RichText css-hnrfcf" option="[object Object]"></div>,就这样我们就定位到了所有图片链接的位置,就可以批量获得图片链接并下载它们了。link<- read_html(url1) %>% html_nodes("div.RichText.ztext.Post-RichText.css-hnrfcf")%>% #所有图片的父级节点 html_nodes("img")%>% #通过img属性进一步定位 html_attr("data-actualsrc") #提取图片的下载链接
但这样获得的链接集合存在每隔一个都是空值的问题,需要进行相应的处理。link<-link[seq(2,length(link),by=2)] #按空值出现的规律去除所有的空值for(i in 1:length(link)){ download(link[i],paste0("猫猫",i,".gif"), mode = "wb")}
运行后就可以自动下载啦!(实际上并没有400张,只有119张图片)04爬取网页中的信息(以当当网为例)用rvest包爬取当当中R语言相关图书的基本信息(包括书名、作者、价格、出版日期、简介)。网址:http://search.dangdang.com/?key=R%D3%EF%D1%D4&act=inputgoods_url<- list()name<- list()author<- list()price<- list()press_date<- list()abstract<- list() #建立清单list通过比对翻页后的网址,我们可以发现,每页的网址都有一定的规律可言: 在 "index= "后填写一个数字就能跳转到相应的页数。
而经过对比,也能发现商品子网页链接储存在<p class=name></p>中的"a href"处,在爬虫中我们需要定位到这一级。for(i in c(1:5)){ url<-paste0("http://search.dangdang.com/?key=R%D3%EF%D1%D4&act=input&page_index=",i) url1 <- read_html(url, encoding="GBK") %>% html_nodes("div#search_nature_rg ul.bigimg p.name a") %>% ##属性class一般用.代替,div class=a 写作 div.a ##而id则用#代替, div id=a 写作 div#a html_attr("href") #提取指定的子网页链接 goods_url<- c(goods_url, url1) #合并每一次循环的链接组}goods_url<- gsub("//","http://",goods_url) %>% data.frame() #因为得到的链接均不完整,因此用gsub()函数补齐: #将goods_url组里的“//”替换为“http://”由于html文件为嵌套式架构,在上文中的html_nodes()中,越靠前的节点范围越大。即<div id="search_nature_rg"></div>这一层嵌套了<ul class="bigimg"></ul>,而后者又嵌套了<p class="name"></p>。最终我们需要定位定位到p.name a,实际上因为所有子网页链接都储存在一系列的<p class="name"></p>中,使用html_nodes("p.name a")或html_nodes("p[class="name] a")均可定位到所需要的链接。运行后便可得到前5页中所有的子网页链接。
进入一个图书界面,找到储存书名、作者、出版时间、简介、价格这些信息的节点:
for(i in c(1:30)){ #当当网会检测访问者是否已登录,R语言想要下载全部内容需要用Rseleuinm包模拟登陆。 good<- as.character(goods_url[i,1]) book_name<- read_html(good, encoding = "GBK") %>% html_nodes("div.name_info h1") %>% html_text()book_author<- read_html(good, encoding = "GBK") %>% html_nodes("div.messbox_info span.t1#author") %>% html_text() book_price<- read_html(good, encoding = "GBK") %>% html_nodes("div.price_d p#dd-price") %>% html_text()book_press_date<- read_html(good, encoding = "GBK") %>% html_nodes("div.messbox_info span.t1:nth-child(2):nth-child(2)") %>% html_text() book_press_date<- data.frame(book_press_date)[1,1]book_abstract<- read_html(good, encoding = "GBK") %>% html_nodes("div.name_info h2 span.head_title_name") %>% html_text()name<- c(name, book_name) author<- c(author, book_author) price<- c(price, book_price) press_date<- c(press_date, book_press_date) abstract<- c(abstract, book_abstract)}name<- gusb("\n\t","",name) %>% as.vector(unlist())author<- gusb("\n\t","",author) %>% as.vector(unlist())price<- gusb("\n\t","",price) %>% as.vector(unlist())press_date<- gusb("\n\t","",press——date) %>% as.vector(unlist())abstract<- gusb("\n\t","",abstract) %>% as.vector(unlist())data<- data.frame(name,author,price,press_date,abstract)作者唠叨:rvest包操作比较简便,因此面对一些检测登陆或者是动态网页时会显得力不从心。实际上,想要完整的将当当网中的信息完全爬取出来需要在后台模拟浏览器登陆(用Rseleuinm或者Rwebdriver包可以实现这个过程),后期我们将再出一期如何搭建jdk与seleuinm框架,并利用RSelenium包或者Rwebdriver模拟登陆或爬取动态网页等难爬取的网页信息的推文。