在 R 中读取 XML 文件并在节点具有不同长度和内容时创建数据框

Read an XML file in R and create a dataframe when nodes have different lengths and content

我正在尝试从 XML 文件创建数据框对象。任务看似简单,但经过多次尝试,我无法准确提取出我想要的东西。

原始数据来自这里:
https://www.data.gouv.fr/fr/datasets/repertoire-national-des-certifications-professionnelles-et-repertoire-specifique/#resource-071a7029-f237-40b1-81ca-e3c1d78282b7

基本上,该文档有超过17K个同名节点,即“FICHES”。其中一个“子节点”是“BLOCS_COMPETENCES”。那个“子节点”还有一个叫做“BLOC_COMPETENCES”(区别在于 BLOC 中的 S),这个有 2 个我感兴趣的节点:CODE 和 LIBELLE。

但是,并非所有“FICHES”都具有相同的属性 and/or 子节点。此外,其中一些有 1 个“BLOC_COMPETENCES”,有 1、2、3 个或根本没有元素。

this document 之后(在其他帖子中)我尝试提取根节点并使用 xpath 访问元素:

# Import the file after zip extraction
RNCP_aout2020 <- xmlParse("U:/RNCP_2020/Fiches-rncp-2020-08-03/export_fiches_RNCP_2020-08-03.xml", encoding = "UTF-8")

# Finding root node
rootNode <- xmlRoot(RNCP_aout2020)

BLOCS_COMPETENCES <- as.data.frame(xpathSApply(rootNode, '/FICHES/FICHE/BLOCS_COMPETENCES/BLOC_COMPETENCES/CODE', xmlValue))

这适用于提取所有存在的节点和属性。例如,以下行在提取每个“FICHES”的 ID 时有效:

# Extract ID
NUMERO_FICHE <- as.data.frame(xpathSApply(rootNode, '/FICHES/FICHE/NUMERO_FICHE', xmlValue))

但是当我为“BLOC_COMPETENCES”尝试使用它时,长度不同,因此无法与其他列合并。

我可以提供我尝试过的所有代码,但不能提供 CSS 格式的 reprex(目前),因为我不知道如何对大文件进行子集化。

任何帮助将不胜感激!

“BLOC_COMPETENCES”的可变数量确实使问题复杂化。在下面的脚本中,解析出所有的 fiche 节点,然后循环遍历每个节点以检索所需的代码和脚本。还需要检查零长度代码和诽谤。由于 fiche 列表很长,lapply 语句将需要一段时间才能完成。

library(xml2)
library(dplyr)

#read the document
page <- read_xml("export_fiches_RNCP_2020-08-03.xml")

#read all fiches nodes
fiches <- xml_find_all(page, "//FICHE")

#parse each fiches
dfs <-lapply(fiches, function(node){
   id <- node %>% xml_find_first(".//ID_FICHE")  %>% xml_text()
   codes <-  node %>% xml_find_all(".//BLOC_COMPETENCES/CODE") %>% xml_text()
   libelles <- node %>% xml_find_all(".//BLOC_COMPETENCES/LIBELLE")%>% xml_text()
   #correct for codes which don't exist
   if (length(codes) <1 ) {codes = NA}
   if (length(libelles) <1 ) {libelles = NA}

   df<- data.frame(id, codes, libelles, stringsAsFactors = FALSE)
})

#merge all of the data frames
answer <- bind_rows(dfs)

考虑 XSLT 转换您的原始 XML 以提取所需的节点。然后,使用便利处理程序 xmlToDataFrame,避免 forapply 系列循环或 if 逻辑。

作为一种独立的、可移植的行业语言,运行 XSLT 1.0 包括 processors like xsltproc、通过其他语言(Java、Python、PHP) 或使用 R 包 xslt.

XSLT (另存为.xsl文件,下面用到的特殊.xml文件)

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="xml" omit-xml-declaration="yes" indent="yes"/>
    <xsl:strip-space elements="*"/>

    <!-- EXTRACT ONLY FICHE NODES -->
    <xsl:template match="/FICHES">
     <xsl:copy>
       <xsl:apply-templates select="FICHE"/>
     </xsl:copy>
    </xsl:template>
    
    <!-- WITHIN EACH FICHE NODE, EXTRACT ONLY ITS DESCENDANT, BLOC_COMPETENCES NODES-->
    <xsl:template match="FICHE">
       <xsl:apply-templates select="descendant::BLOC_COMPETENCES"/>
    </xsl:template>
    
    <!-- WITHIN EACH BLOC_COMPETENCE, DO TWO THINGS:            -->
    <!--      1. RETRIEVE UPPER LEVEL ANCESTOR NODE, ID_FICHE   -->
    <!--      2. RETRIEVE CODE AND LIBELLE CHILD NODES          -->
    <xsl:template match="BLOC_COMPETENCES">
     <xsl:copy>
       <xsl:copy-of select="ancestor::FICHE/ID_FICHE"/>
       <xsl:copy-of select="CODE | LIBELLE"/>
     </xsl:copy>
    </xsl:template>
    
</xsl:stylesheet>

方法 1 (在名为 style.xsl 的文件中使用上述 XSLT)

R + xsltproc

library(XML)

setwd("...")
system("xsltproc -o transformed.xml style.xsl export_fiches_RNCP_2020-08-03.xml")

doc <- xmlParse("transformed.xml")
doc
# <FICHES>
#   <BLOC_COMPETENCES>
#     <ID_FICHE>3614</ID_FICHE>
#     <CODE>RNCP13004BC01</CODE>
#     <LIBELLE>Du dérushage au montage</LIBELLE>
#   </BLOC_COMPETENCES>
#   <BLOC_COMPETENCES>
#     <ID_FICHE>3614</ID_FICHE>
#     <CODE>RNCP13004BC02</CODE>
#     <LIBELLE>Analyser un projet cinématographique</LIBELLE>
#   </BLOC_COMPETENCES>
#   <BLOC_COMPETENCES>
#     <ID_FICHE>3614</ID_FICHE>
#     <CODE>RNCP13004BC03</CODE>
#     <LIBELLE>Tourner sur fond d'incrustation </LIBELLE>
#   </BLOC_COMPETENCES>
#   <BLOC_COMPETENCES>
#   ...

df <- xmlToDataFrame(doc)

方法 2 (在名为 style.xsl 的文件中使用以上 XSLT)

R + xslt

library(xml2)
library(xslt)
library(XML)

# PARSE XML AND XSLT
doc <- read_xml('export_fiches_RNCP_2020-08-03.xml')
style <- read_xml('style.xsl', package = "xslt")

# TRANSFORM NESTED INPUT INTO FLATTER OUTPUT
new_xml <- as.character(xslt::xml_xslt(doc, style))

# PARSE FLATTER XML
flat_xml <- XML::xmlParse(new_xml, asText=TRUE)

# BUILD DATA FRAME
df <- xmlToDataFrame(doc)