Как автоматически загрузить несколько изображений с неработающими ссылками в R?

#r #web-scraping #image-processing

Вопрос:

Цель здесь-загрузить кучу изображений, но некоторые URL — адреса изображений повреждены. Что я хочу сделать, так это изменить код с помощью простого оператора next, чтобы, если ссылка возвращает что-либо, кроме кода состояния 200, перейти к следующему URL (или если ссылка возвращает 404 перехода к следующему), но я не уверен, как написать это в векторизованном коде, и когда я пытаюсь написать это в цикле for, я не могу понять, как инициализировать вектор типа «картинка» для записи в цикле for. Итак, теперь я смотрю на код функции, пытаясь выяснить, где вызывается ошибка и куда поместить следующий оператор или что-то похожее на него… если вы не можете поместить следующий оператор в какую-либо форму векторизованного кода:

Простой Векторизованный Код:

 library(magick)
library(rsvg)

image_urls <- na.omit(articles$url_to_image)
image_content <- image_read(image_urls)
 

Непрозрачный код «Функции» (Где вызывается ошибка?—просто куча звонков для загрузки различных типов изображений)

 function (path, density = NULL, depth = NULL, strip = FALSE, 
    coalesce = TRUE, defines = NULL) 
{
    if (is.numeric(density)) 
        density <- paste0(density, "x", density)
    density <- as.character(density)
    depth <- as.integer(depth)
    
    #doesn't seem relevant: https://rdrr.io/cran/magick/src/R/defines.R
    defines <- validate_defines(defines)
    
    #test whether the object is an instance of an S4 class and a function to test inheritance relationships between object and class -- seems relevant maybe?
    image <- if (isS4(path) amp;amp; methods::is(path, "Image"))
      {
        #bioconductor class
        convert_EBImage(path)
    }
    else if (inherits(path, "nativeRaster") || (is.matrix(path) amp;amp; 
        is.integer(path))) {
        image_read_nativeraster(path)
    }
    else if (inherits(path, "cimg")) {
        image_read_cimg((path))
    }
    else if (grDevices::is.raster(path)) {
        image_read_raster2(path)
    }
    else if (is.matrix(path) amp;amp; is.character(path)) {
        image_read_raster2(grDevices::as.raster(path))
    }
    else if (is.array(path)) {
        image_readbitmap(path)
    }
    else if (is.raw(path)) {
        magick_image_readbin(path, density, depth, strip, defines)
    }
    else if (is.character(path) amp;amp; all(nchar(path))) {
        path <- vapply(path, replace_url, character(1))
        path <- if (is_windows()) {
            enc2utf8(path)
        }
        else {
            enc2native(path)
        }
        magick_image_readpath(path, density, depth, strip, defines)
    }
    else {
        stop("path must be URL, filename or raw vector")
    }
    if (is.character(path) amp;amp; !isTRUE(magick_config()$rsvg)) {
        if (any(grepl("\.svg$", tolower(path))) || any(grepl("svg|mvg", 
            tolower(image_info(image)$format)))) {
            warning("ImageMagick was built without librsvg which causes poor qualty of SVG rendering.nFor better results use image_read_svg() which uses the rsvg package.", 
                call. = FALSE)
        }
    }
    if (isTRUE(coalesce) amp;amp; length(image) > 1 amp;amp; identical("GIF", 
        toupper(image_info(image)$format[1]))) {
        return(image_coalesce(image))
    }
    return(image)
}
 

Когда ссылка нарушена, она возвращает: Ошибка в файле download_url(путь) : Не удалось загрузить «ссылку» (HTTP 404), когда URL-адрес нарушен

Возможно Для Кода Цикла?

 library(magick)
library(rsvg)

image_urls <- na.omit(articles$url_to_image)

image_content <- c() #doesn't work, nor does NULL 
#nor does setting to typeof image_content <- image_url[1]

for(i in 1:length(image_urls){
  image_content[i] = image_read(image_urls[i])
    if(grepl('404', download_path(url), fixed = TRUE) == T)
    next
}
 

Но опять же, я не могу инициализировать, и я не знаю, прервется ли цикл до того, как он доберется до оператора if в любом случае.

Может быть, есть другая библиотека, которую я должен использовать… или просто другой язык?

Вот некоторые примеры данных

 data <- c("https://img-s-msn-com.akamaized.net/tenant/amp/entityid/AAOgEbG.img?h=488amp;w=799amp;m=6amp;q=60amp;o=famp;l=f", 
"https://img-s-msn-com.akamaized.net/tenant/amp/entityid/AAOh6FW.img?h=533amp;w=799amp;m=6amp;q=60amp;o=famp;l=f", 
"https://img-s-msn-com.net/tenant/amp/entityid/AAOgIFh.img?h=450amp;w=799amp;m=6amp;q=60amp;o=famp;l=famp;x=570amp;y")
 

Комментарии:

1. (1) articles$url_to_image не определено. Должна ли вся na.omit() функция быть заменена вашими данными примера data ? (2) download_path(url) не определено.

2. Спасибо вам за ваши комментарии. download_path(url) — это идеализированная функция, основанная на полученной неясной ошибке. статьи$url_to_image должны быть заменены данными примера, вы правы, спасибо, что указали на это.

Ответ №1:

Вы могли бы попробовать эту try функцию:

 image_urls <- data

image_content <- lapply(seq_along(image_urls), function(i) try(image_read(image_urls[i])))
 

Это сохранит ваши изображения в списке. С помощью

 image_content[[1]]
 

дает вам доступ к первому изображению. Если есть такие ошибки, как

 Error in curl::curl_fetch_memory(url) : 
Could not resolve host: img-s-msn-com.net simpleError in curl::curl_fetch_memory(url)
 

они пропущены, и цикл переходит к следующей задаче.

Комментарии:

1. Я подумал о функции try… есть какая-нибудь причина для того, чтобы лапли над ваппли?

2. Я редко использую vapply , и я хотел, чтобы результат был списком. Таков lapply был мой первый выбор.

3. Это полностью имеет смысл, и мой вопрос был глупым, из-за формата изображения вывод не может быть vector…my плохо… : / извините.

4. Вопрос не был глупым. Не нужно извиняться. 🙂

Ответ №2:

Другой вариант-использовать purrr::safely для создания «безопасной» версии image_read , которая будет возвращать оба result и error для каждого URL-адреса.

Результаты можно извлечь из списка, используя что-то вроде purrr::map(y,`[[`, 'result') .

 # two working links and one broken
urls <- c("https://img-s-msn-com.akamaized.net/tenant/amp/entityid/AAOgEbG.img?h=488amp;w=799amp;m=6amp;q=60amp;o=famp;l=f", 
          "https://img-s-msn-com.akamaized.net/tenant/amp/entityid/AAOh6FW.img?h=533amp;w=799amp;m=6amp;q=60amp;o=famp;l=f", 
          "https://img-s-msn-com.net/tenant/amp/entityid/AAOgIFh.img?h=450amp;w=799amp;m=6amp;q=60amp;o=famp;l=famp;x=570amp;y")

# create 'safe' function
image_read_safe <- purrr::safely(magick::image_read)

# apply 'safe' function
y <- purrr::map(urls, image_read_safe)

y
#> [[1]]
#> [[1]]$result
#>   format width height colorspace matte filesize density
#> 1   JPEG   799    488       sRGB FALSE    39743   96x96
#> 
#> [[1]]$error
#> NULL
#> 
#> 
#> [[2]]
#> [[2]]$result
#>   format width height colorspace matte filesize density
#> 1   JPEG   799    533       sRGB FALSE    53910   96x96
#> 
#> [[2]]$error
#> NULL
#> 
#> 
#> [[3]]
#> [[3]]$result
#> NULL
#> 
#> [[3]]$error
#> <simpleError in curl::curl_fetch_memory(url): Could not resolve host: img-s-msn-com.net>
 

Создано 2021-09-10 пакетом reprex (v2.0.0)

Комментарии:

1. Хорошенькая. Раньше такого не видел safely .

2. Отслеживание ошибок на самом деле действительно полезно для меня в этом случае. Однако, когда я печатаю y, я не получаю информацию о формате изображения. Любопытно, почему это произошло.

3. На моей машине с RStudio, когда я распечатывал список, он записывал вывод, который вы видите выше, на консоль, а также отображал изображение в окне предварительного просмотра. Не уверен, почему reprex не взял изображения или почему они могут отображаться по-другому на вашем компьютере.

4. @nniloc На этот раз он появился правильно. Мне было интересно, не могли бы вы обновить свой ответ, чтобы работать с новыми данными в моем вопросе? Так что очень извиняюсь за хлопоты!

5. Без проблем. Отредактировано для добавления в новые URL-адреса.