Текстовые файлы Webscrape, использующие R, rvest или rcurl - PullRequest
0 голосов
/ 30 октября 2019

Итак, у меня есть веб-сайт, https://ais.sbarc.org/logs_delimited/, на котором есть несколько ссылок, и внутри каждой ссылки есть 24 ссылки с файлами .txt.

Я новичок в R, но могу пройти по одной ссылке, чтобы получить 24 текстовых файла в кадре данных. Но я не могу понять, как зациклить весь каталог.

Мне удалось зациклить 24 ссылки с помощью hours.list, но year.list и trip.list не сработали ... Я прошу прощения, если это похоже на другие вопросы о Webscrape, или если яупустить что-то действительно простое, но я был бы признателен за любую помощь

get_ais_text = function(ais_text){

    hours.list = c(0:23)
    hours.list_1 = sprintf('%02d', hours.list)

    year.list = c(2018:2022)
    year.list1 = sprintf('%d', year.list)

    trip.list = c(190101:191016)
    trip.list_1 = sprintf("%d", trip.list)

ais_text = tryCatch(    
lapply(paste0('https://ais.sbarc.org/logs_delimited/2019/190101/AIS_SBARC_190101-', hours.list_1,'.txt'),
                    function(url){
                      url %>% 
                        read_delim(";", col_names = sprintf("X%d", 1:25), col_types = ais_col_types)                   
                    }),
      error = function(e){NA}
    )
  DF = do.call(rbind.data.frame, ais_text)
  return(DF)
}

get_ais_text()

Ответы [ 2 ]

1 голос
/ 30 октября 2019

Вот функция, которая работает рекурсивно, чтобы получить все ссылки, начиная с домашнего каталога. Обратите внимание, что для запуска требуется немного:

library(xml2)
library(magrittr)
.get_link <- function(u){
  node <- xml2::read_html(u)
  hrefs <- xml2::xml_find_all(node, ".//a[not(contains(@href,'../'))]") %>% xml_attr("href")
  urls <- xml2::url_absolute(hrefs, xml_url(node))
  if(!all(tools::file_ext(urls) == "txt")){
    lapply(urls, .get_link)
  }else {
    return(urls)
  }
}

То, что он делает, в основном начинается с url, и читается содержимое, поиск любых ссылок <a... с использованием xpath selector, что говорит"все ссылки, которые не являются ../" т.е. ... не самая верхняя обратная ссылка каталога. затем, если ссылка имеет больше ссылок, просмотрите и получите все эти ссылки. Если у нас есть последние ссылки, то есть .txt файлов, мы закончили.

Пример обмана и начиная только с 2018

a <- .get_link("https://ais.sbarc.org/logs_delimited/2018/")
> a[[1]][1:2]
[1] "https://ais.sbarc.org/logs_delimited/2018/180101/AIS_SBARC_180101-00.txt"
[2] "https://ais.sbarc.org/logs_delimited/2018/180101/AIS_SBARC_180101-01.txt"
> length(a)
[1] 365
> a[[365]][1:2]
[1] "https://ais.sbarc.org/logs_delimited/2018/181231/AIS_SBARC_181231-00.txt"
[2] "https://ais.sbarc.org/logs_delimited/2018/181231/AIS_SBARC_181231-01.txt"

Что бы вы сделали, просто начните с: https://ais.sbarc.org/logs_delimited/ для ввода URL, а затем добавьте что-то вроде data.table::fread, чтобы переварить данные. Что я бы предложил сделать в отдельной итерации. Примерно так работает:

lapply(1:length(a), function(i){
    lapply(a[[i]], data.table::fread)
})

Для чтения в данных ...

Первое, на что стоит обратить внимание, - это 11 636 файлов. Это много ссылок, чтобы попасть на чей-то сервер одновременно ... поэтому я собираюсь попробовать несколько и показать, как это сделать. Я бы предложил добавить Sys.sleep звонок в ваш ...

# This gets all the urls
a <- .get_link("https://ais.sbarc.org/logs_delimited/")
# This unlists and gives us a unique array of the urls
b <- unique(unlist(a))
# I'm sampling b, but you would just use `b` instead of `b[...]`
a_dfs <- jsonlite::rbind_pages(lapply(b[sample(1:length(b), 20)], function(i){
    df <- data.table::fread(i, sep = ";") %>% as.data.frame()
    # Giving the file path for debug later if needed seems helpful
    df$file_path <- i
    df
}))

> a_dfs %>% head()
  17:00:00:165              24  0 338179477 LAUREN SEA        V8 V9   V15 V16 V17 V18 V19 V20 V21 V22 V23                                                                file_path   V1   V2 V3 V4
1 17:00:00:166     EUPHONY ACE 79     71.08          1 371618000  0 254.0 253  52   0   0   0   0   5  NA https://ais.sbarc.org/logs_delimited/2018/180113/AIS_SBARC_180113-17.txt <NA> <NA> NA NA
2 17:00:01:607 SIMONE T BRUSCO 31     32.93          3 367593050 15 255.7  97  55   0   0   1   0 503   0 https://ais.sbarc.org/logs_delimited/2018/180113/AIS_SBARC_180113-17.txt <NA> <NA> NA NA
3 17:00:01:626 POLARIS VOYAGER 89    148.80          1 311000112  0 150.0 151  53   0   0   0   0   0  22 https://ais.sbarc.org/logs_delimited/2018/180113/AIS_SBARC_180113-17.txt <NA> <NA> NA NA
4 17:00:01:631         SPECTRE 60     25.31          1 367315630  5 265.1 511  55   0   0   1   0   2  20 https://ais.sbarc.org/logs_delimited/2018/180113/AIS_SBARC_180113-17.txt <NA> <NA> NA NA
5 17:00:01:650          KEN EI 70     73.97          1 354162000  0 269.0 269  38   0   0   0   0   1  84 https://ais.sbarc.org/logs_delimited/2018/180113/AIS_SBARC_180113-17.txt <NA> <NA> NA NA
6 17:00:02:866 HANNOVER BRIDGE 70     62.17          1 372104000  0 301.1 300  56   0   0   0   0   3   1 https://ais.sbarc.org/logs_delimited/2018/180113/AIS_SBARC_180113-17.txt <NA> <NA> NA NA
  V5 V6 V7 V10 V11 V12 V13 V14 02:00:00:489 338115994  1 37 SRTG0$ 10  7  4 17:00:00:798 BROADBILL 16.84 269   18 367077090 16.3 -119.981493 34.402530 264.3 511 40
1 NA NA NA  NA  NA  NA  NA  NA         <NA>        NA NA NA     NA NA NA NA         <NA>      <NA>    NA  NA <NA>      <NA>   NA          NA        NA    NA  NA NA
2 NA NA NA  NA  NA  NA  NA  NA         <NA>        NA NA NA     NA NA NA NA         <NA>      <NA>    NA  NA <NA>      <NA>   NA          NA        NA    NA  NA NA
3 NA NA NA  NA  NA  NA  NA  NA         <NA>        NA NA NA     NA NA NA NA         <NA>      <NA>    NA  NA <NA>      <NA>   NA          NA        NA    NA  NA NA
4 NA NA NA  NA  NA  NA  NA  NA         <NA>        NA NA NA     NA NA NA NA         <NA>      <NA>    NA  NA <NA>      <NA>   NA          NA        NA    NA  NA NA
5 NA NA NA  NA  NA  NA  NA  NA         <NA>        NA NA NA     NA NA NA NA         <NA>      <NA>    NA  NA <NA>      <NA>   NA          NA        NA    NA  NA NA
6 NA NA NA  NA  NA  NA  NA  NA         <NA>        NA NA NA     NA NA NA NA         <NA>      <NA>    NA  NA <NA>      <NA>   NA          NA        NA    NA  NA NA

Очевидно, что нужно кое-что почистить ... но я думаю, это то, как вы доберетесь до этого.

Редактировать 2

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

a_dfs <- rbind_pages(lapply(b[sample(1:length(b), 20)], function(i){
    raw <- readLines(i)
    str_matrix <- stringi::stri_split_regex(raw, "\\;", simplify = TRUE)
    as.data.frame(apply(str_matrix, 2, function(j){
        ifelse(!nchar(j), NA, j)
    })) %>% mutate(file_name = i)
}))

> a_dfs %>% head
            V1           V2 V3    V4    V5 V6 V7        V8 V9 V10  V11 V12         V13       V14   V15 V16 V17 V18 V19 V20 V21 V22  V23  V24  V25
1 09:59:57:746    STAR CARE 77 75.93   135  1  0 566341000  0   0 16.7   1 -118.839933 33.562167   321 322  50   0   0   0   0   6   19 <NA> <NA>
2 10:00:00:894     THALATTA 70 27.93 133.8  1  0 229710000  0 251 17.7   1 -119.366765 34.101742 283.9 282  55   0   0   0   0   7 <NA> <NA> <NA>
3 10:00:03:778   GULF GLORY 82 582.3   256  1  0 538007706  0   0 12.4   0 -129.345783 32.005983    87  86  54   0   0   0   0   2   20 <NA> <NA>
4 10:00:03:799    MAGPIE SW 70 68.59 123.4  1  0 352597000  0   0 10.9   0 -118.747970 33.789747 119.6 117  56   0   0   0   0   0   22 <NA> <NA>
5 10:00:09:152 CSL TECUMSEH 70 66.16 269.7  1  0 311056900  0  11   12   1 -120.846763 34.401482 105.8 106  56   0   0   0   0   6   21 <NA> <NA>
6 10:00:12:870    RANGER 85 60 31.39 117.9  1  0 367044250  0 128    0   1 -119.223133 34.162953   360 511  56   0   0   1   0   2   21 <NA> <NA>
                                                                 file_name  V26  V27
1 https://ais.sbarc.org/logs_delimited/2018/180211/AIS_SBARC_180211-10.txt <NA> <NA>
2 https://ais.sbarc.org/logs_delimited/2018/180211/AIS_SBARC_180211-10.txt <NA> <NA>
3 https://ais.sbarc.org/logs_delimited/2018/180211/AIS_SBARC_180211-10.txt <NA> <NA>
4 https://ais.sbarc.org/logs_delimited/2018/180211/AIS_SBARC_180211-10.txt <NA> <NA>
5 https://ais.sbarc.org/logs_delimited/2018/180211/AIS_SBARC_180211-10.txt <NA> <NA>
6 https://ais.sbarc.org/logs_delimited/2018/180211/AIS_SBARC_180211-10.txt <NA> <NA>
0 голосов
/ 30 октября 2019

Это работает для меня:

library(rvest)

crawler <- function(base_url) {

  get_links <- function(url) {
    read_html(url) %>% 
      html_nodes("a") %>% 
      html_attr("href") %>% 
      grep("../", ., fixed = TRUE, value = TRUE, invert = TRUE) %>% 
      url_absolute(url)
  }

  links <- base_url
  counter <- 1

  while (sum(grepl("txt$", links)) != length(links)) {
    links <- unlist(lapply(links, get_links))
    message("scraping level ", counter, " [", length(links), " links]")
    counter <- counter + 1
  }

  return(links)

}

txts <- crawler("https://ais.sbarc.org/logs_delimited/")

Похоже, что он отказывается от уровня 3, но это только потому, что есть так много ссылок, чтобы пройти.

Как только у вас естьвсе текстовые URL, вы можете использовать это для чтения в файлах:

library(dplyr)
library(data.table)

df <- lapply(txts, fread, fill = TRUE) %>% 
  rbindlist() %>% 
  as_tibble()

Я бы определенно сделал это в два шага, так как он будет работать довольно долго, и имеет смысл сохранить промежуточные результаты (т.е. , ссылки).

Вы также можете попытаться запустить это параллельно, если хотите (cl - количество используемых ядер):

library(pbapply)             

df <- pblapply(txts[1:10], fread, fill = TRUE, cl = 3) %>% 
  rbindlist() %>% 
  as_tibble()

Должно быть немного быстрееи вы также получите хороший индикатор выполнения.

...