R和xml2:即使节点丢失,如何读取不在子节点中的文本并读取信息

时间:2017-03-15 01:36:33

标签: r xpath xml2

我使用R和它的包xml2来解析html文档。我提取了一段html文件,如下所示:

text <- ('<div>
<p><span class="number">1</span>First&nbsp;<span class="small-accent">previous</span></p>
<p><span class="number">2</span>Second&nbsp;<span class="accent">current</span></p>
<p><span class="number">3</span>Third&nbsp;</p>
<p><span class="number">4</span>Fourth&nbsp;<span class="small-accent">last</span> A</p>
</div>')

我的目标是从文本中提取信息并将其转换为数据框,如下所示:

  number      label   text_of_accent   type_of_accent
1      1      First         previous     small-accent
2      2     Second          current           accent
3      3      Third                                  
4      4   Fourth A             last     small-accent

我尝试了以下代码:

library(xml2)
library(magrittr)

html_1 <- text %>% 
    read_html() %>% 
    xml_find_all( "//span[@class='number']")  

number <- html_1 %>% xml_text()

label  <- html_1 %>%
    xml_parent() %>% 
    xml_text(trim = TRUE)

text_of_accent <- html_1 %>%
    xml_siblings() %>% 
    xml_text()

type_of_accent <- html_1 %>% 
    xml_siblings() %>%
    xml_attr("class")

很遗憾,labeltext_of_accenttype_of_accent未按预期提取:

label
[1] "1First previous" "2Second current" "3Third"          "4Fourth last A" 

text_of_accent
[1] "previous" "current"  "last" 

type_of_accent
[1] "small-accent" "accent"       "small-accent"

是否有可能只用xml2实现我的目标,或者我需要一些额外的工具?至少可以提取label的文本片段吗?

1 个答案:

答案 0 :(得分:4)

可以使用xml2完成,label搞砸了xml_text()找出包括当前节点及其子节点在内的所有文本的原因,为了避免这种情况,你可以使用xpath text()首先找到当前节点的文本,然后提取它,还需要检查是否存在某些节点并正确处理丢失的案例:

# read in text as html and extract all p nodes as a list
lst <- read_html(text) %>% xml_find_all("//p")

lapply(lst, function(node) {
    # find the first span
    first_span_node = xml_find_first(node, "./span[@class='number']")

    number = xml_text(first_span_node, trim = TRUE)

    # use the text() to find out text nodes from the current position
    label = paste0(xml_text(xml_find_all(node, "./text()")), collapse = " ")

    # find the second span
    accent_node = xml_find_first(first_span_node, "./following-sibling::span")

    # check if the second span exists
    if(length(accent_node) != 0) {
        text_of_accent = xml_text(xml_find_first(accent_node, "./text()"))
        type_of_accent = xml_text(xml_find_first(accent_node, "./@class"))    
    } else {
        text_of_accent = ""
        type_of_accent = ""
    }

    c(number = number, label = label, 
      text_of_accent = text_of_accent, 
      type_of_accent = type_of_accent)
}) %>% 
do.call(rbind, .) %>% as.data.frame()


#  number     label text_of_accent type_of_accent
#1      1    First        previous   small-accent
#2      2   Second         current         accent
#3      3    Third                               
#4      4 Fourth  A           last   small-accent