Подтвердить что ты не робот

Скребок комментариев YouTube возвращает ограниченные результаты

Задача:

Я хотел очистить все комментарии YouTube от данного видео.

Я успешно адаптировал код R из предыдущего вопроса (Скремблировать комментарии Youtube в R).

Вот код:

library(RCurl)
library(XML)
x <- "https://gdata.youtube.com/feeds/api/videos/4H9pTgQY_mo/comments?orderby=published"
html = getURL(x)
doc  = htmlParse(html, asText=TRUE) 
txt  = xpathSApply(doc, 
"//body//text()[not(ancestor::script)][not(ancestor::style)[not(ancestor::noscript)]",xmlValue)

Чтобы использовать его, просто замените идентификатор видео (т.е. "4H9pTgQY_mo" ) на требуемый ID.

Проблема:

Проблема заключается в том, что он не возвращает все комментарии. Фактически, он всегда возвращает вектор с 283 элементами, независимо от количества комментариев в видео.

Кто-нибудь может пролить свет на то, что здесь происходит не так? Это невероятно расстраивает. Спасибо.

4b9b3361

Ответ 1

Я был (по большей части) способен выполнить это, используя последнюю версию Youtube Data API и пакет R httr. Основной подход, который я принял, заключался в том, чтобы отправить несколько запросов GET на соответствующий URL-адрес и получить данные в партиях по 100 (максимально допустимый API) - т.е.

base_url <- "https://www.googleapis.com/youtube/v3/commentThreads/"
api_opts <- list(
  part = "snippet",
  maxResults = 100,
  textFormat = "plainText",
  videoId = "4H9pTgQY_mo",  
  key = "my_google_developer_api_key",
  fields = "items,nextPageToken",
  orderBy = "published")

где key - ваш фактический ключ разработчика Google, конечно.

Исходная партия извлекается следующим образом:

init_results <- httr::content(httr::GET(base_url, query = api_opts))
##
R> names(init_results)
#[1] "nextPageToken" "items"
R> init_results$nextPageToken
#[1] "Cg0Q-YjT3bmSxQIgACgBEhQIABDI3ZWQkbzEAhjVneqH75u4AhgCIGQ="       
R> class(init_results)
#[1] "list"

Второй элемент - items - это фактический набор результатов из первой партии: это список длиной 100, так как мы указали maxResults = 100 в запросе GET. Первый элемент - nextPageToken - это то, что мы используем, чтобы каждый запрос возвращал соответствующую последовательность результатов. Например, мы можем получить следующие 100 результатов следующим образом:

api_opts$pageToken <- gsub("\\=","",init_results$nextPageToken)
next_results <- httr::content(
    httr::GET(base_url, query = api_opts))
##
R> next_results$nextPageToken
#[1] "ChYQ-YjT3bmSxQIYyN2VkJG8xAIgACgCEhQIABDI3ZWQkbzEAhiSsMv-ivu0AhgCIMgB"

где текущий запрос pageToken возвращается как предыдущие запросы nextPageToken, и нам предоставляется новая nextPageToken для получения следующей партии результатов.


Это довольно просто, но, очевидно, было бы очень утомительно продолжать изменять значение nextPageToken вручную после каждого отправляемого запроса. Вместо этого я подумал, что это будет хорошим вариантом для простого класса R6:

yt_scraper <- setRefClass(
  "yt_scraper",
  fields = list(
    base_url = "character",
    api_opts = "list",
    nextPageToken = "character",
    data = "list",
    unique_count = "numeric",
    done = "logical",
    core_df = "data.frame"),

  methods = list(
    scrape = function() {
      opts <- api_opts
      if (nextPageToken != "") {
        opts$pageToken <- nextPageToken
      }

      res <- httr::content(
        httr::GET(base_url, query = opts))

      nextPageToken <<- gsub("\\=","",res$nextPageToken)
      data <<- c(data, res$items)
      unique_count <<- length(unique(data))
    },

    scrape_all = function() {
      while (TRUE) {
        old_count <- unique_count
        scrape()
        if (unique_count == old_count) {
          done <<- TRUE
          nextPageToken <<- ""
          data <<- unique(data)
          break
        }
      }
    },

    initialize = function() {
      base_url <<- "https://www.googleapis.com/youtube/v3/commentThreads/"
      api_opts <<- list(
        part = "snippet",
        maxResults = 100,
        textFormat = "plainText",
        videoId = "4H9pTgQY_mo",  
        key = "my_google_developer_api_key",
        fields = "items,nextPageToken",
        orderBy = "published")
      nextPageToken <<- ""
      data <<- list()
      unique_count <<- 0
      done <<- FALSE
      core_df <<- data.frame()
    },

    reset = function() {
      data <<- list()
      nextPageToken <<- ""
      unique_count <<- 0
      done <<- FALSE
      core_df <<- data.frame()
    },

    cache_core_data = function() {
      if (nrow(core_df) < unique_count) {
        sub_data <- lapply(data, function(x) {
          data.frame(
            Comment = x$snippet$topLevelComment$snippet$textDisplay,
            User = x$snippet$topLevelComment$snippet$authorDisplayName,
            ReplyCount = x$snippet$totalReplyCount,
            LikeCount = x$snippet$topLevelComment$snippet$likeCount,
            PublishTime = x$snippet$topLevelComment$snippet$publishedAt,
            CommentId = x$snippet$topLevelComment$id,
            stringsAsFactors=FALSE)
        })
        core_df <<- do.call("rbind", sub_data)
      } else {
        message("\n`core_df` is already up to date.\n")
      } 
    }
  )
)

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

rObj <- yt_scraper()
##
R> rObj$data
#list()
R> rObj$unique_count
#[1] 0
##
rObj$scrape_all()
##
R> rObj$unique_count
#[1] 1673
R> length(rObj$data)
#[1] 1673
R> ##
R> head(rObj$core_df)
                                                           Comment              User ReplyCount LikeCount              PublishTime
1                    That Andorra player was really Ruud..<U+feff>         Cistrolat          0         6 2015-03-22T14:07:31.213Z
2                          This just in; Karma is a bitch.<U+feff> Swagdalf The Obey          0         1 2015-03-21T20:00:26.044Z
3                                          Legend! Haha B)<U+feff>  martyn baltussen          0         1 2015-01-26T15:33:00.311Z
4 When did Van der sar ran up? He must have run real fast!<U+feff> Witsakorn Poomjan          0         0 2015-01-04T03:33:36.157Z
5                           <U+003c>b<U+003e>LOL<U+003c>/b<U+003e>           F Hanif          5        19 2014-12-30T13:46:44.028Z
6                                          Fucking Legend.<U+feff>        Heisenberg          0        12 2014-12-27T11:59:39.845Z
                            CommentId
1   z123ybioxyqojdgka231tn5zbl20tdcvn
2   z13hilaiftvus1cc1233trvrwzfjg1enm
3 z13fidjhbsvih5hok04cfrkrnla2htjpxfk
4   z12js3zpvm2hipgtf23oytbxqkyhcro12
5 z12egtfq5ojifdapz04ceffqfrregdnrrbk
6 z12fth0gemnwdtlnj22zg3vymlrogthwd04

Как я уже упоминал ранее, это дает вам почти все - 1673 из 1790 комментариев. По какой-то причине он, похоже, не обнаруживает вложенные ответы пользователей, и я не совсем уверен, как указать это в рамках API.


Я ранее настраивал аккаунт Google Developer для использования API Google Analytics, но если вы еще этого не сделали, это должно быть довольно просто. Здесь обзор - вам не нужно настраивать OAuth или что-то в этом роде, просто создайте проект и создайте новый ключ доступа к публичному API.

Ответ 2

Альтернативой пакету XML является пакет rvest. Используя URL-адрес, который вы предоставили, скремблирование комментариев будет выглядеть так:

library(rvest)
x <- "https://gdata.youtube.com/feeds/api/videos/4H9pTgQY_mo/comments?orderby=published"
x %>% 
  html %>% 
  html_nodes("content") %>% 
  html_text

Что возвращает символ символа комментариев:

[1] "That Andorra player was really Ruud.."                                                                  
[2] "This just in; Karma is a bitch."                                                                        
[3] "Legend! Haha B)"                                                                                        
[4] "When did Van der sar ran up? He must have run real fast!"                                               
[5] "What a beast Ruud was!"
...

Более подробную информацию о rvest можно найти здесь.

Ответ 3

Ваша проблема заключается в получении максимальных результатов.

Алгоритм решения

Сначала вам нужно позвонить url https://gdata.youtube.com/feeds/api/videos/4H9pTgQY_mo?v=2 Этот url содержит информацию для подсчета комментариев видео, оттуда вычеркните этот номер, и мы его перейдем.

<gd:comments>&ltgd:feedLink ..... countHint='1797'/></gd:comments>

После этого используйте его для повторения мысленного url с этими двумя параметрами https://gdata.youtube.com/feeds/api/videos/4H9pTgQY_mo/comments?max-results=50&start-index=1

Когда вы выполняете итерацию, вам нужно изменить начальный индекс от 1,51,101,151... Проверил ли max-result, что он имеет ограничение до 50.