Как сохранить и прочитать вывод read_html в виде файла RDS?

Объекты можно сохранять и читать так

# Save as file
saveRDS(iris, "mydata.RDS")

# Read back in 
readRDS("mydata.RDS")

Но, похоже, это не работает для объектов, созданных с помощью xml2::read_html()

Пример

library(rvest)
someobject <- read_html("https://stackoverflow.com/")
saveRDS(someobject, "someobject.RDS")

Что создает файл, но не так, как ожидалось, т.е.

readRDS("someobject.RDS")
Error in doc_is_html(x$doc) : external pointer is not valid

Что происходит и как проще всего сохранить объект html, чтобы его можно было загрузить обратно с минимальными затратами кода и суеты?


person stevec    schedule 03.09.2019    source источник


Ответы (4)


Мы можем использовать write_xml и read_html из пакета xml2

before <- read_html("https://stackoverflow.com/")
xml2::write_xml(before, "someobject1.xml")
after <- xml2::read_html("someobject1.xml")

Однако identical возвращает FALSE

identical(before, after)
#[1] FALSE

но запрос на обоих из них, похоже, возвращает тот же результат

library(rvest)
before %>%  html_nodes("div")
after %>% html_nodes("div")
person Ronak Shah    schedule 03.09.2019

Чтобы ответить «что происходит»: saveRDS пытается сериализовать сохраняемый объект. Здесь объект someobject - это список с элементами someobject$doc и someobject$node. Тип элементов - externalptr (внешний указатель), что означает, что они ссылаются на структуру данных C, хранящуюся в памяти. При сериализации внешних указателей ссылка теряется. Следовательно, ошибка «внешний указатель недействителен».

Вы можете сериализовать someobject с помощью as.character() и передать это saveRDS:

saveRDS(as.character(someobject), "someobject.RDS")

Затем воссоздайте объект, используя readRDS и read_html:

someobject <- read_html(readRDS("someobject.RDS"))

Но проще использовать write_html(), как предлагали другие.

Некоторое обсуждение в этой ветке выпуска Github.

person neilfws    schedule 03.09.2019

Насколько мне известно, методы, использующие файлы XML и RDS, кажутся отключенными на одинаковое количество символов. Я провел сравнение, и похоже, что разница между исходной и загруженной версиями заключается в узлах тела.

url <-  "https://stackoverflow.com/"
html <- read_match(url)
html_node(html, "body")  %>% html_text() %>%  unlist() -> OBT
nchar(OBT)

28879

xml2::write_xml(html, "someobject1.xml")
html_node(html, "body")  %>% html_text() %>%  unlist() -> BT1
nchar(BT1)

28893

html   %>% toString %>% saveRDS(., "someobject.RDS")
after2 <- readRDS("someobject.RDS") %>% read_html
html_node(html, "body")  %>% html_text() %>%  unlist()-> BT2
nchar(BT2)

28893

Это показывает, что два загруженных объекта имеют одинаковое количество символов. Если мы удалим символы \ n из всех текстовых объектов, число должно быть таким же.

BT1 %>% str_remove_all(.,"\n") %>% nchar(.)

27733

BT2 %>% str_remove_all(.,"\n") %>% nchar(.) 

27733

OBT %>% str_remove_all(.,"\n") %>% nchar(.) 

27733

person SignorCasa    schedule 18.02.2021
comment
Хорошее расследование, можете ли вы предоставить результаты кода в нижнем блоке? - person stevec; 18.02.2021
comment
Я добавил к результатам, и я добавил URL-адрес, потому что результаты зависят от URL-адреса. - person SignorCasa; 18.02.2021
comment
Интересно. Похоже, write_xml() может добавить эти разрывы строк. Возможно, вы сможете выполнить это, чтобы увидеть, есть ли шаблон, куда вставляются символы \n? - person stevec; 18.02.2021
comment
Я быстро посмотрел, и, похоже, это связано с макетом. Например, внизу страницы находится таблица с четырьмя столбцами: Переполнение стека, Продукты, Компания и Сеть обмена стеками. Если вы щелкните в нижней части четвертого столбца (Другие) и щелкните Технологии. Каждый заголовок столбца (кроме самого левого) будет иметь добавленный \ n. - person SignorCasa; 18.02.2021

Используйте toString() для преобразования класса xml_document в character перед сохранением, например

library(rvest)
someobject <- read_html("https://stackoverflow.com/")

someobject  %>% toString %>% saveRDS(., "someobject.RDS")
newobject <- readRDS("someobject.RDS") %>% read_html

Обратите внимание, что эти объекты не полностью идентичны (я не уверен, почему).

identical(someobject, newobject)
# [1] FALSE
person stevec    schedule 03.09.2019