返回博客
指南
Raluca PenciucLast updated on Mar 31, 20262 min read

R语言中的网页抓取如何让数据科学变得有趣

R语言中的网页抓取如何让数据科学变得有趣

随着网络的发展,越来越多的数据被动态生成。这使得通过API提取数据变得越来越困难。R语言中的网络爬虫技术应运而生,为那些项目需要海量数据的数据科学家提供了帮助。

值得庆幸的是,编程语言也在不断演进以应对这些挑战。R 便是此类语言的典型代表,它专为高效处理海量任务而设计。

在本教程中,我将通过探讨R在实际应用中的价值,带您了解其基本原理。我还将介绍R网络爬虫领域中最受欢迎的包之一——rvest。随后,我将演示R在提取网站数据方面的具体运作方式。

R 简介

R是S编程语言的开源版本,融合了Scheme的语义。它于1993年中期首次亮相,1995年开源,并于2000年发布了首个稳定的测试版。

Ross Ihaka 和 Robert Gentleman 设计 R 的初衷是“快速且忠实地将想法转化为软件”。

R 是一种函数式编程语言,在数据科学家中广为人知。其最常见的应用场景包括:

  • 银行业;
  • 金融;
  • 电子商务;
  • 机器学习;
  • 以及任何其他需要处理海量数据的领域。

与 SAS 和 SPSS 相比,R 是全球使用最广泛的分析工具。其活跃且支持性强的社区拥有近 200 万用户。

若我们观察一些将 R 融入业务的企业及其具体做法,会发现:

  • Facebook:用于更新状态及其社交网络图;
  • Google:预测经济活动并提升在线广告效率;
  • Foursquare:用于其推荐引擎;
  • Trulia:用于预测房价和当地犯罪率。

然而,与其他编程语言相比,R 始终与 Python 处于竞争之中。两者都提供了网络爬虫工具,并且拥有活跃的开发者社区。

差异主要体现在目标受众上。Python 拥有非常易学的语法和众多高级特性,这使其对初学者和非技术用户更具吸引力。

R 初看可能略显晦涩,但它更专注于统计分析。它提供了更丰富的内置数据分析和可视化工具。因此,对于处理海量数据的项目(如网络爬虫),它可能是一个更好的选择。

关于 rvest

rvest 是 R 语言中用于网页抓取的最受欢迎的包之一。它提供了强大且简单的解析功能。Python 的 BeautifulSoup 是其灵感来源,且它是 tidyverse 集合的一部分。

听起来不错,但既然 R 本身就有能完成相同任务的原生库,为什么还要使用 rvest 呢?第一个充分的理由是:rvest 实际上是 httrxml2 包的封装。这意味着它既能处理 GET 请求,又能进行 HTML 解析。

因此,您只需使用一个库而非两个,代码将变得更加简洁紧凑。不仅如此,rvest 还能接受字符串作为输入,并处理 XML 解析和文件下载。

然而,我们需要考虑到网站上存在更多动态生成的内容。原因多种多样:性能、用户体验等。由于 rvest 无法处理 JavaScript 的执行,因此此时你需要寻找替代方案。

使用 R 进行数据抓取

好了,理论部分到此为止。让我们看看 R 在实际应用场景中的表现。在本教程中,我选取了一本非常著名的书籍——乔治·奥威尔的《1984》在 Goodreads 上的页面。您可以在此处访问该网站:https://www.goodreads.com/book/show/61439040-1984

我想了解这本书的受欢迎程度随时间推移的变化情况。为此,我将抓取评论列表,并提取每条评论的日期和评分。最后,我会将数据保存到外部文件中,以便后续由其他程序进行处理。

配置环境

但首先,您必须确保已具备编写代码所需的一切条件。

关于集成开发环境(IDE),你有两种选择:

  • 为 Visual Studio Code 安装 R 插件
  • 下载 RStudio,该软件旨在让 R 编程变得轻松便捷。

在本教程中,我将使用后者。您可以在此处下载:https://www.rstudio.com/products/rstudio/download/

RStudio Desktop的免费版本足以让你熟悉基础操作。和之前一样,请按照安装说明进行操作。

打开 RStudio 并创建一个新的空目录。我将在名为“goodreads-rvest.r”的新文件中编写代码。

浏览器预览

在提取数据之前,您需要明确需要哪些数据。Rvest 同时支持 CSS 和 XPath 选择器,请根据需求选择。 

如果您计划开展更复杂的爬取项目,建议您具备基本的 HTML 和 CSS 知识。这里有一个不错的入门练习平台。

如果您对 HTML 不太熟悉,也有一些非技术性的选择。例如,Chrome 浏览器提供了一个名为 SelectorGadget 的扩展程序。它允许您点击页面上的任意位置,并显示用于获取数据的 CSS 选择器。

然而,并非所有网站都像 Goodreads 那样简单。我将选择通过手动检查 HTML 来查找 CSS 选择器,从而提取数据。

在浏览器中访问目标网址,向下滚动至“社区评论”部分。然后右键单击该区域,选择“检查”以打开开发者工具。

ID 为“other_reviews”的容器正是我关注的重点。现在,请使用“检查”按钮查找评论日期和评分的 CSS 选择器。

因此,你会注意到以下几点:

  • 每条评论都是一个带有“review”类的 div 容器;
  • 评论的日期是一个带有“reviewDate”类的单个锚点元素;
  • 评论的评分是一个带有“staticStars”类的span元素。它有五个span子元素,即用户可设置的星级数量。我们将关注带有“p10”类的彩色部分。

提取评论

确认所有先决条件后,你终于可以开始编写代码了。

install.packages('rvest')

将光标置于行尾,并点击代码编辑器上方的“运行”按钮。您将在控制台中看到包的安装进度。

安装仅需执行一次,因此现在您可以注释或删除前面的那行代码:

#install.packages('rvest')

现在需要加载(或导入)该库:

library(rvest)

我将使用 read_html 函数向目标网站发送一个 GET 请求,从而下载所需的 HTML 文档。通过这种方式,我将下载所需的 HTML 文档:

book_html <- read_html("https://www.goodreads.com/book/show/61439040-1984")

结果现已存储在 book_html 变量中,您只需在控制台输入以下内容即可查看:

如果您在任何时候需要查看要使用的函数的官方文档,请在控制台中输入:

help(function_name) 

RStudio 将打开一个 HTTP 服务器,提供直达文档的链接。对于 read_html 函数,输出结果如下:

为了获取评论列表,我将使用 html_elements 函数。它将接收我之前找到的 CSS 选择器作为输入:

reviews <- book_html %>% html_elements('div.review')

结果将是一个 XML 节点列表,我将对其进行遍历以获取每个元素的日期和评分:

R 程序员使用管道运算符“%>%”来增强代码的灵活性。其作用是将左侧操作数的值作为参数传递给右侧操作数。

您可以将操作数进行链式调用(如本指南后文所示),从而帮助您大幅减少局部变量的使用。如果不使用管道运算符,上述代码行将如下所示:

reviews <- html_elements(book_html, 'div.review')

为了收集数据,我将在循环外部初始化两个向量。通过快速查看网站,我可以确保这两个向量具有相同的长度。

dates <- vector()
ratings <- vector()

现在,在遍历评论列表时,我需要查找两个值:日期和评分。正如你之前所见,日期是一个带有 reviewDate 类的锚元素。

评分是一个带有 staticStars 类的 span 元素,其中每颗星对应五个 span 子元素。如果用户给出一颗星,则该 span 元素将具有 p10 类名,其余的则具有 p0 类名。

代码如下:

for (review in reviews) {
  review_date = review %>% html_element('a.reviewDate') %>% html_text()
  dates <- c(dates, review_date)
 
  review_rating_element = review %>% html_element('span.staticStars')
  valid_stars = review_rating_element %>% html_elements('span.p10')
  review_rating = length(valid_stars)
  ratings <- c(ratings, review_rating)
}

请注意 html_element 函数;这不是笔误。当你需要提取一组 XML 节点时可以使用 html_elements,而提取单个节点时则使用 html_element

在此示例中,我针对 HTML 文档中较小的一部分(即一条评论)使用了后者。我还使用了 html_text 函数来获取所找到元素的文本内容。 

最后,我将这两个向量合并到一个数据框中,以便集中管理数据:

result = data.frame(date = dates, rating = ratings)

最终结果将如下所示:

保存结果

众所周知,如果不将结果存储在某处,数据抓取便毫无意义。在 R 中,将数据写入 CSV 文件只需一行代码:

write.csv(result, "reviews.csv")

结果必须是矩阵或数据框(当前结果已符合),否则系统会尝试进行转换。运行代码并检查项目目录,你会发现可以使用文本编辑器、Excel文档等工具打开之前的表格。

结果必须是矩阵或数据框(当前数据已符合此格式),否则系统会尝试进行转换。运行代码并检查项目目录,你会发现可以使用文本编辑器、Excel 文档等工具打开之前的表格。

毋庸置疑,我们的数据列表仅包含 30 条记录。而网站上显示的评论超过 90,000 条,评分超过 300 万条。那么究竟发生了什么?答案是分页。

不仅如此,请返回浏览器并点击第二页。你会发现列表内容发生了变化,但 URL 却保持不变。这意味着他们使用状态来动态加载列表的另一部分。

在这种情况下,rvest可能派不上用场。相反,自动化浏览器可以模拟点击行为来加载列表的其余部分。RSelenium就是这样一个库的例子,但我将把这个主题留作后续练习。

结论

希望本教程能为你提供一个扎实的 R 语言网络爬虫入门基础。现在,你在为下一个项目选择技术栈时,可以更轻松地做出决策。

但请注意,本文并未涵盖网络爬虫的诸多挑战。您可以在这本通俗易懂的指南中找到更详细的概念说明。

关于作者
Raluca Penciuc, 全栈开发工程师 @ WebScrapingAPI
Raluca Penciuc全栈开发工程师

Raluca Penciuc 是 WebScrapingAPI 的全栈开发工程师,主要负责开发爬虫、优化规避机制,并探索可靠的方法以降低在目标网站上的被检测概率。

开始构建

准备好扩展您的数据收集规模了吗?

加入2,000多家企业,使用WebScrapingAPI在无需任何基础设施开销的情况下,以企业级规模提取网络数据。