暑假过半,一个月来深入学习R语言,最近横向拓宽一下R语言技能,包括利用Rmd文件构建常用的幻灯片和网页文档、网络爬虫等。
此文用于记录爬虫使用心得,仅为个人理解,不足之处请指正。
简单理解,爬虫就是利用计算机替代我们的手工复制和粘贴,因此和手工复制一样,爬虫的关键是:
- 找到所有网页链接;
- 找到同一网页链接上所有所需信息的位置并提取;
- 访问所有网页,获取所有信息。
以通过翻页展示网站内容的数英网为例,爬取该网站上与数据有关的文章标题和发文时间:
构建网页地址
在搜索结果下点击“查看更多文章”后,可以看到页面需要进行翻页:
https://www.digitaling.com/search/articles/1?kw=%E6%95%B0%E6%8D%AE # 第一页
https://www.digitaling.com/search/articles/2?kw=%E6%95%B0%E6%8D%AE # 第二页
https://www.digitaling.com/search/articles/3?kw=%E6%95%B0%E6%8D%AE # 第三页
可以发现,网址中除了1,2,3会随着翻页而改变,其余部分完全相同。由此,可以通过paste0()
函数构建所有需要访问的网页链接:
urls <- paste0("https://www.digitaling.com/search/articles/",
1:35, # 共有35页
"?kw=%E6%95%B0%E6%8D%AE")
urls
#> [1] "https://www.digitaling.com/search/articles/1?kw=%E6%95%B0%E6%8D%AE"
#> [2] "https://www.digitaling.com/search/articles/2?kw=%E6%95%B0%E6%8D%AE"
#> [3] "https://www.digitaling.com/search/articles/3?kw=%E6%95%B0%E6%8D%AE"
#> [4] "https://www.digitaling.com/search/articles/4?kw=%E6%95%B0%E6%8D%AE"
#> [5] "https://www.digitaling.com/search/articles/5?kw=%E6%95%B0%E6%8D%AE"
#> [ reached getOption("max.print") -- omitted 30 entries ]
获取所需信息的位置
这一环节需要用到rvest包读取网页信息,而后确认所需信息在HTML中的位置,最终构建读取函数,供第三步循环使用。如有扎实的HTML和CSS知识作支撑会更顺利,由于鄙人条件不成熟,因此以大量的试错法进行弥补。😇
library(rvest)
library(tidyverse) # 偷懒起见,还是直接加载了tidyverse大包
读取一个测试网址
test_url <- urls[1]
web <- read_html(test_url)
web
#> {html_document}
#> <html xmlns="https://www.w3.org/1999/xhtml">
#> [1] <head>\n<meta http-equiv="Content-Type" content="text/html; charset=utf-8 ...
#> [2] <body>\r\n\t<!-- Google tag (gtag.js) -->\r\n<script async src="https://w ...
这一步把整个网页文件都读取到了R中,接下来只要想办法在里边提取出想要的信息即可。
确认选择器
利用选择器进行提取,不同类型的信息对应的选择器不同,目前有两种方法确认不同信息的选择器:
- SelectorGadget,可以从这里安装谷歌浏览器插件。1
- 在所需信息的位置右击,检查。
提取“标题”
title <- web |>
html_nodes("h3") |>
html_text()
title
#> [1] "东方甄选又爆了,入淘宝直播首秀一场卖出1亿!"
#> [2] "狂卖170亿,今年电影为啥大爆发?"
#> [3] "2023年夏日经济之现制咖啡&茶饮市场洞察报告,线上总用户达1.51亿"
#> [4] "详拆「茶百道」招股书:新茶饮TOP3的喜与忧"
#> [5] "暂停4年,维密在中国“咸鱼翻身”了"
#> [ reached getOption("max.print") -- omitted 25 entries ]
这一页上一共提取了30个标题。
提取“日期”
date <- web |>
html_nodes("label") |>
html_text() # 这里提取了33个,最后3个为无效信息,需剔除
date <- date[1:(length(date)-3)]
date
#> [1] "2023-08-31" "2023-08-20" "2023-08-17" "2023-08-17" "2023-08-16"
#> [ reached getOption("max.print") -- omitted 25 entries ]
输出测试网址上的信息
将每页的标题和时间合并到数据框中,便于后续处理。
data <- tibble(title, date)
DT::datatable(data)
构建爬虫函数
以上测试的最终目的是构建一个自动抓取的函数,该函数以网址为参数,如上所示的数据框为输出。具体如下:
scraper <- function(url) {
# 读取参数网址对应的网页文件
web <- read_html(url)
# 获取网页文件中的标题
title <- web |>
html_nodes("h3") |>
html_text()
# 获取网页文件中的时间
date <- web |>
html_nodes("label") |>
html_text()
date <- date[1:(length(date)-3)] # 剔除最后3个无效信息
# 返回数据框
return(tibble(title, date))
# 休眠2秒,防止访问过快,被服务器禁止访问
Sys.sleep(2)
}
获取所有信息
每爬取一个页面,可以获得一个数据框,其中包含30条记录(最后一页可能不足30条)。利用循环遍历35个页面,获得35个数据框,最后按行合并所有数据框,即可得到页面上所有的信息。
这一步看似复杂,但是可以由map_dfr()
函数一步到位:
scraper_data <- map_dfr(urls, scraper)
nrow(scraper_data)
#> [1] 1050
最终获取了1050行数据,结果如下(展示前十条):
DT::datatable(scraper_data[1:10, ])
总结
最近遇到很多奇奇怪怪的网站,但思路始终是一致的,灵活运用,🉑解决90%的爬虫问题。
有的网站加载页的方式不一(比如下拉滚动加载),这会导致在网址不变的情况下爬虫无法抓取该页面所有信息,针对这样的网页可以采用还不错的谷歌插件Web Scraper等方法去抓取。利用R语言需补充一些其他的技术,目前只略知一二,未能熟练运用。
-
手头没有其他浏览器,不是谷歌浏览器的小伙伴自己寻找一下有无该插件吧!🏃 ↩︎