R语言进阶网页抓取:处理JavaScript动态加载的数据

R语言进阶网页抓取:处理JavaScript动态加载的数据

本文旨在教授如何使用r语言处理通过JavaScript动态加载的网页数据。针对传统网页抓取工具(如xml或rvest)无法直接获取此类数据的挑战,文章将详细介绍如何利用V8包模拟JavaScript执行环境,直接从JavaScript文件中提取所需数据。通过一个具体的案例,我们将演示如何识别数据源、获取JavaScript内容、在R环境中运行JavaScript代码,并对提取的数据进行清洗和整理,从而高效、准确地获取动态生成的网页信息。

1. 动态网页数据抓取的挑战

在网页数据抓取(web scraping)中,我们经常会遇到数据并非直接嵌入在html结构中,而是通过javascript动态生成的情况。传统的r包,如xml或rvest,主要依赖于解析静态html内容。当目标数据通过javascript异步请求(ajax)或直接内嵌在javascript变量中时,这些工具往往无法直接获取到所需信息。

以从https://www.fatf-gafi.org/countries/网站抓取国家列表为例,初步尝试使用XML::htmlParse可能无法获取到预期的国家数据,因为这些数据并非以标准HTML表格(

)的形式存在,而是嵌套在

元素中,且由JavaScript动态填充。在这种情况下,我们需要一种能够执行JavaScript代码并访问其内部变量的解决方案。

2. 解决方案:利用V8包执行JavaScript

R语言的V8包提供了一个嵌入式JavaScript和WebAssembly引擎,允许我们在R环境中执行JavaScript代码,并与JavaScript运行时环境进行交互。这意味着我们可以加载网页中使用的JavaScript文件,执行其中的代码,然后提取在JavaScript中定义的变量值。

核心思路:

  1. 识别JavaScript数据源: 通过浏览器开发者工具(Network/网络标签页)检查网页加载过程中请求的资源,特别是.JS文件,寻找可能包含目标数据的JavaScript文件。
  2. 获取JavaScript内容: 使用httr包下载该JavaScript文件的内容。
  3. 初始化V8引擎: 创建一个V8上下文。
  4. 执行JavaScript代码: 将下载的JavaScript内容在V8上下文中执行。
  5. 提取变量: 从V8上下文中获取JavaScript变量的值。
  6. 数据清洗: 对提取到的数据进行必要的格式转换和清洗。

3. 实战案例:抓取FATF网站的国家数据

我们将以FATF(金融行动特别工作组)网站为例,演示如何抓取其国家列表。

立即学习Java免费学习笔记(深入)”;

3.1 识别数据源

通过访问https://www.fatf-gafi.org/countries/并打开浏览器开发者工具,在“网络”或“Network”标签页中,我们可以观察到页面加载过程中会请求一个名为country-data-multi-lang.js的JavaScript文件。经验证,这个文件包含了我们所需的所有国家数据,以一个名为countries的JavaScript数组变量形式存在。

该JavaScript文件的完整URL通常是:https://www.fatf-gafi.org/media/fatf/fatfv20/js/country-data-multi-lang.js。

3.2 实施步骤与代码示例

首先,确保你已经安装了所需的R包:httr、V8、dplyr和tidyr。

# 载入所需库 library(httr)    # 用于发送HTTP请求 library(V8)      # 用于执行JavaScript library(dplyr)   # 用于数据操作 library(tidyr)   # 用于数据整理,特别是unnest函数  # 1. 定义JavaScript数据文件的URL js_url <- 'https://www.fatf-gafi.org/media/fatf/fatfv20/js/country-data-multi-lang.js'  # 2. 使用httr包获取JavaScript文件的内容 # content(GET(js_url), 'text') 将GET请求的响应内容解析为文本 js_content <- content(GET(js_url), 'text')  # 3. 初始化V8上下文 # ct <- v8() 创建一个新的V8引擎上下文 ct <- v8()  # 4. 在V8上下文中执行JavaScript代码 # ct$eval(js_content) 会执行js_content中的所有JavaScript代码 # 这将使得js_content中定义的变量(例如'countries')在V8上下文中可用 ct$eval(js_content)  # 5. 从V8上下文中提取'countries'变量的值 # ct$get("countries") 将JavaScript中的'countries'变量转换为R的数据结构 # 经验证,'countries'是一个嵌套的数据结构,其中'groups'列是列表形式 country_data_raw <- ct$get("countries")  # 6. 数据清洗与整理 # 使用tidyr::unnest() 将嵌套的'groups'列展开 # select() 选择我们感兴趣的列。这里的列索引是根据实际数据结构确定的, # 建议在实际操作中先查看数据框的列名和结构,再进行选择。 # Filter(!is.na(name)) 过滤掉name列为NA的行,通常这些是无效或填充行。 final_country_data <- country_data_raw %>%   unnest(cols = c(groups)) %>% # 展开嵌套的 'groups' 列   select(c(1:2, 4:14, 16)) %>% # 选择特定列,这些列包含了国家名称、代码及相关组织信息   filter(!is.na(name))        # 过滤掉国家名称为空的行  # 查看最终的数据结构和前几行 print(final_country_data) # str(final_country_data) # 查看数据结构

3.3 代码解释

  • httr::GET() 和 content(): 用于发起HTTP GET请求并获取响应内容。’text’参数确保内容被解析为字符串
  • v8(): 创建一个V8引擎实例,它提供了一个独立的JavaScript运行环境。
  • ct$eval(js_content): 这是核心步骤。它将从网站下载的JavaScript代码在V8引擎中执行。执行后,该JavaScript代码中定义的任何全局变量(例如本例中的countries)都可以在R中访问。
  • ct$get(“countries”): 从V8引擎中提取名为countries的JavaScript变量的值。V8包会自动将其转换为R中对应的数据结构(通常是列表或数据框)。
  • unnest(cols = c(groups)): 观察到ct$get(“countries”)返回的数据框中,有一列名为groups,其内部是一个嵌套的列表或数据框。unnest()函数(来自tidyr包)用于将这些嵌套结构展开为扁平的列,使得数据更易于分析。
  • select(c(1:2, 4:14, 16)): 经过unnest操作后,数据框可能包含多余的列或者需要重新排列的列。这里通过列索引选择最终需要的列。在实际应用中,建议先打印names(final_country_data)或使用glimpse()来查看所有列名,然后按名称选择,以提高代码的可读性和健壮性。
  • filter(!is.na(name)): 过滤掉name列为NA(缺失值)的行。在某些数据源中,末尾可能会有填充的空行,通过这种方式可以清除。

4. 注意事项与总结

  • JavaScript源定位: 找到正确的JavaScript文件是关键。熟练使用浏览器开发者工具(尤其Network/网络和Sources/源标签页)是必备技能。
  • 数据结构探索: 从V8中获取的数据可能是一个复杂的嵌套列表。使用str()、names()、glimpse()等函数来探索其结构,以便正确地使用unnest()、select()等函数进行数据清洗。
  • 动态内容复杂性: 并非所有动态内容都能通过V8直接提取。如果数据是通过复杂的dom操作、用户交互或加密方式加载,可能需要更高级的工具,如selenium配合无头浏览器进行模拟。但对于数据直接存在于某个JavaScript变量中的情况,V8是一个轻量且高效的选择。
  • 网站政策: 在进行任何网页抓取活动前,请务必查阅网站的robots.txt文件和使用条款,确保你的行为符合网站的规定。

通过本文的学习,你应该能够掌握使用R语言V8包处理JavaScript动态加载数据的基本方法。这种技术极大地扩展了R在网页数据抓取方面的能力,使其能够应对更复杂的现代网页结构。

© 版权声明
THE END
喜欢就支持一下吧
点赞10 分享