Очистка и извлечение XML элементов карты сайта с использованием R и Rvest - PullRequest
1 голос
/ 01 мая 2020

Мне нужно извлечь большое количество XML элементов карты сайта из нескольких xml файлов, используя Rvest. Я смог извлечь html_nodes из веб-страниц, используя xpaths, но для файлов xml это для меня ново.

И я не могу найти вопрос Stackoverflow, который позволил бы мне проанализировать адрес файла xml, вместо того, чтобы анализировать большой фрагмент текста XML.

Пример того, что я использовал для html:

library(dplyr)
library(rvest)

webpage <- "https://www.example.co.uk/"

data <- webpage %>%
  read_html() %>%
  html_nodes("any given node goes here") %>%
  html_text()

Как мне приспособить это, чтобы взять элемент файла "lo c" XML из XML файл (синтаксический анализ адреса), который выглядит следующим образом:

<urlset>
<url>
<loc>https://www.example.co.uk/</loc>
<lastmod>2020-05-01</lastmod>
<changefreq>always</changefreq>
<priority>0.8</priority>
</url>
<url>
<loc>https://www.example.co.uk/news</loc>
<changefreq>always</changefreq>
<priority>0.6</priority>
</url>
<url>
<loc>https://www.example.co.uk/news/uk</loc>
<changefreq>always</changefreq>
<priority>0.5</priority>
</url>
<url>
<loc>https://www.example.co.uk/news/weather</loc>
<changefreq>always</changefreq>
<priority>0.5</priority>
</url>
<url>
<loc>https://www.example.co.uk/news/world</loc>
<changefreq>always</changefreq>
<priority>0.5</priority>
</url>

Вот что я изменил в сценарии, любезно предоставленном Дейвом:

library(xml2)

#list of files to process
fnames<-c("xml1.xml")

dfs<-lapply(fnames, function(fname) {
  doc<-read_xml(fname)

  #find loc and lastmod
  loc<-trimws(xml_text(xml_find_all(doc, ".//loc")))
  lastmod<-trimws(xml_text(xml_find_all(doc, ".//last")))

  #find all of the nodes/records under the urlset node
  nodes<-xml_children(xml_find_all(doc, ".//urlset"))

  #find the sub nodes names and values
  nodenames<-xml_name(nodes)
  nodevalues<-trimws(xml_text(nodes))

  #make data frame of all the values
  df<-data.frame(file=fname, loc=loc, lastmod=lastmod, node.names=nodenames, 
                 values=nodevalues, stringsAsFactors = FALSE, nrow(0))

})

#Make one long df
longdf<-do.call(rbind, dfs)

#make into a wide format
library(tidyr)
finalanswer<-spread(longdf, key=node.names, value=values)

1 Ответ

1 голос
/ 01 мая 2020

Поскольку число дочерних элементов в узле URL отличается, это рабочий подход:

file<-read_xml(text)

library(dplyr)

#find parent nodes
parents <-xml_find_all(file, ".//url")

#parse each child
dfs<-lapply(parents, function(node){
  #Find all children
  nodes <- xml_children(node)

  #get node name and value
  nodenames<-  xml_name(nodes)
  values <- xml_text(nodes)

  #made data frame with results
  df<- as.data.frame(t(values), stringsAsFactors=FALSE)
  names(df)<-nodenames
  df
})

#Make find answer
answer<-bind_rows(dfs)

Поскольку у вас есть несколько файлов, вы можете заключить скрипт во внешнюю l oop, чтобы циклически проходить через список файлов. Конечно, это всего лишь oop в пределах всех oop, поэтому производительность будет снижаться, если в каждом файле будет большое количество файлов и большое количество родительских узлов.

Альтернатива: Если число дочерних узлов невелико, то лучше проанализировать их напрямую и избежать вышеупомянутых ошибок l oop.

loc <- xml_find_first(parents, ".//loc") %>% xml_text()
lastmod <- xml_find_first(parents, ".//lastmod") %>% xml_text()
changefreq <- xml_find_first(parents, ".//changefreq") %>% xml_text()
priority <- xml_find_first(parents, ".//priority") %>% xml_text()

answer<-data.frame(loc, lastmod, chargefreq, priority)
...