R - Разбор XML из нескольких URL-адресов во фрейм данных с помощью tydiverse и xml2 - PullRequest
0 голосов
/ 05 мая 2020

Этот вопрос, вероятно, будет помечен как дублированный, но я просто не могу заставить его работать. Для записи я прочитал все остальные вопросы о stackoverflow и прочитал документацию.

Я хочу извлечь обзоры данных из itunes для нескольких страниц (ссылка: https://itunes.apple.com/gb/rss/customerreviews/id=1388411277 / page = 1 / xml ) и я хочу сделать это аккуратно и динамично c способом, предпочтительно с XML2 и tydiverse .

Моя конечная цель:

Чтобы фрейм данных со всеми доступными полями (например, ID, автор, и т. Д. c) был в столбце и заполнен данными.

Моя борьба начинается с самого начала. Я могу только запустить ссылку и получить ее как XML, но я не могу запустить простую строку кода для извлеченного кода XML. Мне здесь явно чего-то не хватает. Я также не знаю, как go перелистывать страницы. Я знаю, сколько страниц существует, но я хочу, чтобы это было динамически c.

library("tidyverse")
library("xml2")


# Data extraction ---------------------------------------------------------

df_xml <- read_xml('https://itunes.apple.com/gb/rss/customerreviews/id=1388411277/page=1/xml')

teste <- xml_text(xml_find_all(df_xml, '//feed/entry/ author')) *here I try to extract the field author*
> teste
> character(0)


Заранее всем спасибо

1 Ответ

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

Проблема в том, что когда вы вызываете xml_find_all(df_xml, '//feed/entry/ author'), поиск не может найти узлы, которые вы ищете, потому что все они находятся в пространстве имен xml.

uri <- "https://itunes.apple.com/gb/rss/customerreviews/id=1388411277/page=1/xml"
my_xml <- read_xml(uri)
xml_find_all(my_xml, "//feed")
#> {xml_nodeset (0)}

Вы можете узнать, какие пространства имен используются в документе следующим образом:

xml_ns(my_xml)
#> d1 <-> http://www.w3.org/2005/Atom
#> im <-> http://itunes.apple.com/rss

Таким образом, вы можете указать пространство имен, которое хотите использовать в своем xpath, и вы получите узел, который ищете, следующим образом:

xml_find_all(my_xml, "//d1:feed")
#> {xml_nodeset (1)}
#> [1] <feed xmlns:im="http://itunes.apple.com/rss" xmlns="http://www.w3.org/2005/Atom ...

Это, очевидно, немного раздражает, так как вам нужно префикс всех ваших тегов в xpath с помощью d1:, а структура вашего документа такова, что вы можете обойтись без пространств имен, поэтому лучше игнорировать их.

Я считаю, что самый простой способ сделать это - использовать read_html вместо read_xml, поскольку, помимо прочего, он автоматически удаляет пространства имен и более прощает ошибки. Однако есть функция xml_ns_strip, которую вы можете вызвать после чтения read_xml, если хотите.

Итак, ваши три варианта работы с пространствами имен в этом документе:

  1. Префикс всех имен тегов с помощью d1:
  2. Используйте xml_ns_strip после read_xml
  3. Используйте read_html

Этот код пройдёт oop через все страницы xml и даст вам вектор символов всех 365 обзоров. Вы обнаружите, что, хотя на каждой странице из xml есть 100 content тегов, это потому, что внутри каждого entry тегов есть два content тегов. Один из них содержит исходный текст обзора, а другой - то же содержание, но в виде строки html. Поэтому l oop отбрасывает строки, содержащие html, в пользу необработанного текста:

library("tidyverse")
library("xml2")

base <- "https://itunes.apple.com/gb/rss/customerreviews/id=1388411277/page="
reviews <- author <- review_date <- character()
max_pages <- 100

for(i in seq(max_pages))
{
  cat("Trying", paste0(base, i, "/xml"), "\n")
  my_xml       <- paste0(base, i, "/xml") %>% read_xml() %>% xml_ns_strip()
  next_reviews <- xml_find_all(my_xml, xpath = '//feed/entry/content') %>% 
                  xml_text() %>%
                  subset(seq_along(.) %% 2 == 1)  
  if(length(next_reviews) == 0){
    result <- tibble(review_date, author, reviews)
    break
  }

  reviews      <- c(reviews, next_reviews)
  next_author  <- xml_text(xml_find_all(my_xml, xpath = '//feed/entry/author/name'))
  author       <- c(author, next_author)
  next_date    <- xml_text(xml_find_all(my_xml, xpath = '//feed/entry/updated'))
  review_date  <- c(review_date, next_date)
}
#> Trying https://itunes.apple.com/gb/rss/customerreviews/id=1388411277/page=1/xml 
#> Trying https://itunes.apple.com/gb/rss/customerreviews/id=1388411277/page=2/xml 
#> Trying https://itunes.apple.com/gb/rss/customerreviews/id=1388411277/page=3/xml 
#> Trying https://itunes.apple.com/gb/rss/customerreviews/id=1388411277/page=4/xml 
#> Trying https://itunes.apple.com/gb/rss/customerreviews/id=1388411277/page=5/xml 
#> Trying https://itunes.apple.com/gb/rss/customerreviews/id=1388411277/page=6/xml 
#> Trying https://itunes.apple.com/gb/rss/customerreviews/id=1388411277/page=7/xml 
#> Trying https://itunes.apple.com/gb/rss/customerreviews/id=1388411277/page=8/xml 
#> Trying https://itunes.apple.com/gb/rss/customerreviews/id=1388411277/page=9/xml 

И теперь result будет содержать tibble с тремя интересующими вас полями:

result
#> # A tibble: 367 x 3
#>    review_date          author      reviews                                           
#>    <chr>                <chr>       <chr>                                             
#>  1 2020-05-05T02:38:35~ **stace**   "Really good and useful app. Nice to be able to g~
#>  2 2020-05-05T01:51:49~ fire-hazza~ "Not for Scotland or Wales cmon man"              
#>  3 2020-05-04T23:45:59~ Adz-Coco    "Unable to register due to NHS number. My number ~
#>  4 2020-05-04T23:34:50~ Matthew ba~ "Probably spent about £5 developing this applicat~
#>  5 2020-05-04T16:40:17~ Jenny19385~ "Why it is so complicated to sign up an account? ~
#>  6 2020-05-04T14:39:54~ Sienna hea~ "Thankyou NHS for this excellent app I feel a lot~
#>  7 2020-05-04T13:09:45~ Raresole    "A great app that lets me book appointments and a~
#>  8 2020-05-04T12:28:56~ chanters934 "Unable to login. App doesn’t recognise the code ~
#>  9 2020-05-04T11:26:44~ Ad_T        "Unfortunately my surgery must not be participati~
#> 10 2020-05-04T08:25:17~ tonyproctor "It’s a good app although would be better with a ~
#> # ... with 357 more rows

...