Qual o melhor serviço de streaming de filmes e séries no Brasil? O R responde.

Introdução

Recentemente, li um artigo sobre a Balcanização dos serviços de streaming que me fez refletir sobre essa indústria. Dado que assinar mais de um serviço de streaming pode ser um desperdício, devido a falta de tempo para consumir tanto material, as pessoas costumam optar por apenas um dos existentes: Netflix, Amazon Prime Video ou algum outro. Como saber qual escolher?

O serviço JustWatch ajuda um pouco. Nele, é possível pesquisar por um filme ou seriado e descobrir em quais serviços ele está disponível. Assim, se temos em mão os dados dos catálogos dos serviços, podemos avaliar a quantidade e qualidade dos filmes em cada um para decidir o melhor.

Existe um módulo Python que permite extrair os dados do JustWatch em json, tornando os dados muito fáceis para trabalhar. Por isso, este projeto usará código não só em R como em Python.

Este projeto consiste em coletar os 5000 melhores filmes e seriados do IMDB, coletar dados de cada usando a API do Justwatch e analisar os resultados para concluir sobre o melhor serviço de streaming no Brasil.

Primeiro passo: Coletando nomes de filmes e seriados

Infelizmente, a API do Justwatch não nos permite coletar todo o catálogo do site, existindo um limite dos 1000 primeiros resultados, mesmo que se altere os parâmetros page e page_size da função.

Por outro lado, a função não impõe um limite de requisições feitas sequencialmente. Por isso, a melhor estratégia é iterar a função sobre uma lista de filmes a pesquisar.

Onde coletar essa lista de filmes? A ideia que eu tive foi usar o IMDB para isso. Eu coletei os 5000 melhores filmes e seriados (sem distinção) para ter os resultados. Como o código é relativamente simples e os dados bem grandes, não vou descrever essa parte do projeto aqui. Os interessados podem consultar como eu fiz para coletar os dados do IMDB neste gist.

Usar o módulo Python para coletar dados da API do JustWatch

O código é bem simples: eu leio o arquivo com a lista de filmes salvos na parte anterior, itero sobre o dataframe, junto todos os resultados em um dicionário e salvo os dados em formato json.

O comportamento da função just_watch.search_for_item() é o mesmo de você pesquisar manualmente no site. Isto é, ao digitar na caixa de busca o título desejado, aparecerá uma lista com os resultados relacionados, sendo que provavelmente apenas o primeiro resultado é o que interessa. Por isso, no código abaixo, eu extraio o primeiro resultado da busca. Funciona na maioria das vezes.

from justwatch import JustWatch
import csv
import json
import pandas as pd
just_watch = JustWatch(country='BR')

# ler lista de filmes
arquivo = '/home/sillas/R/data/imdb/lista_filmes_imdb.csv'
titulos = pd.read_csv(arquivo)

out = list()

for index, row in titulos.iterrows():
    # imprimir status do loop
    if index % 100 == 0:
        print(index)
    # consultar api
    api_data = just_watch.search_for_item(query = row["full_title"])["items"]
    
    # salvar dados apenas se houver resultado
    if len(api_data) > 0:
        first_item_data = api_data[0]
        first_item_data["tconst"] = row["tconst"]
        out.append(first_item_data)
    
# salvar resultados em json
with open('/home/sillas/R/data/imdb/justwatchdata.json', 'w') as fp:
    json.dump(out, fp)
    
# salvar dados de sites de streaming
provider_details = just_watch.get_providers()

with open('/home/sillas/R/data/imdb/justwatchdata_providers.json', 'w') as fp:
    json.dump(provider_details, fp)

Juntando os dados

Finalmente, chegamos à parte divertida. Abaixo eu importo os pacotes usados e o arquivo json salvo acima.

library(tidyverse)
library(jsonlite)
library(UpSetR)
library(ggridges)

# gist com o json: 
jw <- jsonlite::fromJSON("/home/sillas/R/data/imdb/justwatchdata.json") %>% 
  as_tibble() %>% 
  # selecionar colunas importantes
  select(tconst, original_title, original_release_year,
         tmdb_popularity, object_type, offers, scoring) 

glimpse(jw)
## Observations: 4,442
## Variables: 7
## $ tconst                <chr> "tt0903747", "tt0111161", "tt0068646", "tt…
## $ original_title        <chr> "Breaking Bad", "The Shawshank Redemption"…
## $ original_release_year <int> 2008, 1994, 1972, 2013, 1974, 1994, 1993, …
## $ tmdb_popularity       <dbl> 76.613, 30.009, 26.993, 82.621, 19.567, 32…
## $ object_type           <chr> "show", "movie", "movie", "show", "movie",…
## $ offers                <list> [<data.frame[5 x 14]>, <data.frame[13 x 1…
## $ scoring               <list> [<data.frame[4 x 2]>, <data.frame[10 x 2]…

A estrutura deste dataframe não é necessariamente tabular, como se costuma ver. As colunas offers e scoring são da classe list, pois cada valor dessas duas variáveis é um dataframe. Ou seja, no R é possível termos dataframes dentro de um dataframe.

Para tornar o dataframe mais simples de ser usado, vamos transformar essas duas colunas em dataframes separados.

# criar funcao para extrair apenas as colunas importantes de offers
extract_offers_data <- function(offers){
  if(!is.null(offers)){
    offers %>% 
      select(monetization_type, provider_id,
             date_created, presentation_type)
  }
}

tbl_offers <- jw %>% 
  select(tconst, offers) %>% 
  # remover filmes sem dados de streaming
  filter(!map_lgl(offers, is.null)) %>% 
  # usar funcao criada acima para extrair colunas
  mutate(offers = map(offers, extract_offers_data)) %>% 
  unnest(offers) %>% 
  # filtrar apenas filmes de serviços onde se paga uma taxa mensal para ter
  # acesso aos filmes, ao inves de um preço para cada filme, como se fosse
  # uma loja.
  filter(monetization_type == "flatrate") %>% 
  distinct(tconst, provider_id)

tbl_scoring <- jw %>% 
  select(tconst, scoring) %>% 
  filter(!map_lgl(scoring, is.null)) %>% 
  unnest(scoring) %>% 
  rename(scoring_provider_type = provider_type)

# no dataframe principal, remover essas duas colunas
tbl_jw <- jw %>% 
  select(-c(offers, scoring))

# importar arquivos com os metadados dos sites de streaming
tbl_providers <- fromJSON("/home/sillas/R/data/imdb/justwatchdata_providers.json") %>% 
  as_tibble() %>%
  select(provider_id = id, provider_name = slug) %>% 
  mutate(provider_name = str_replace_all(provider_name, "-", "_"))

tbl_offers_prov <- left_join(tbl_offers, tbl_providers, by = "provider_id")

# criar dataset das analises
tbl_jw <- tbl_jw %>% 
  left_join(tbl_offers_prov, by = "tconst") %>% 
  left_join(tbl_scoring, by = "tconst") %>% 
  select(-provider_id)

Este é o dataset das nossas análises:

# dataset das analises
tbl_jw
## # A tibble: 41,085 x 8
##    tconst original_title original_releas… tmdb_popularity object_type
##    <chr>  <chr>                     <int>           <dbl> <chr>      
##  1 tt090… Breaking Bad               2008            76.6 show       
##  2 tt090… Breaking Bad               2008            76.6 show       
##  3 tt090… Breaking Bad               2008            76.6 show       
##  4 tt090… Breaking Bad               2008            76.6 show       
##  5 tt011… The Shawshank…             1994            30.0 movie      
##  6 tt011… The Shawshank…             1994            30.0 movie      
##  7 tt011… The Shawshank…             1994            30.0 movie      
##  8 tt011… The Shawshank…             1994            30.0 movie      
##  9 tt011… The Shawshank…             1994            30.0 movie      
## 10 tt011… The Shawshank…             1994            30.0 movie      
## # … with 41,075 more rows, and 3 more variables: provider_name <chr>,
## #   scoring_provider_type <chr>, value <dbl>

Resultados

Quais sites possuem mais filmes e seriados?

tbl_jw %>% 
  distinct(tconst, object_type, provider_name) %>% 
  count(provider_name, object_type) %>% 
  group_by(object_type) %>% 
  mutate(pct = 100 * n/sum(n)) %>% 
  ggplot(aes(x = fct_rev(provider_name), y = pct)) +
  geom_col() +
  coord_flip() +
  facet_wrap(~ object_type, ncol = 1) +
  labs(x = NULL, y = "%",
       title = "Presença dos melhores filmes e seriados \nnos sites de streaming no Brasil") +
  theme_bw()

Os resultados são bem interessantes. Para quem adora seriados, a Netflix reina absoluta tendo em seu portfolio 40% dos melhores seriados, enquanto que o segundo melhor neste quesito, o Amazon Prime Video, possui apenas 10%. Em termos de filmes, a Netflix também é a melhor, sendo seguida de perto pelo Telecine Play. O portfolio do Amazon Prime Video para filmes deixa bastante a desejar.

Ainda sobre filmes, é bastante importante mencionar que mais da metade das produções não está disponível em nenhum serviço de streaming de vídeo por assinatura no Brasil, um resultado bem lamentável para cinéfilos.

Para o restante deste post, vamos focar apenas nesses 3 principais streamings:

prvs <- c("netflix", "amazon_prime_video", "telecine_play")

tbl_jw_principais <- tbl_jw %>% 
  filter(is.na(provider_name) | provider_name %in% prvs) %>% 
  mutate(provider_name = tidyr::replace_na(provider_name, "nenhum"))

Quais sites possuem os filmes mais recentes?

range(tbl_jw_principais$original_release_year)
## [1] 1920 2019
tbl_jw_principais %>% 
  ggplot(aes(x = original_release_year, color = provider_name)) +
  stat_ecdf() +
  theme_bw() +
  scale_x_continuous(breaks = seq(1920, 2020, by  = 10), 
                     minor_breaks = NULL) +
  scale_y_continuous(breaks = seq(0, 1, .1), 
                     minor_breaks = NULL,
                     labels = scales::percent) +
  theme(legend.position = "bottom") +
  facet_wrap(~ object_type, ncol = 2) +
  labs(title = "Distribuição da idade de títulos por serviço de streaming",
       x = NULL, 
       y = NULL)

O formato da curva referente à Netflix é muito interessante. Note como ela cresce numa velocidade muito maior a partir de 2010. Olhando para este ponto no eixo vertical, nota-se que 70% dos filmes e incríveis 80% dos seriados foram lançados a partir de 2010. De todos os 3 serviços de streaming, a Netflix é a mais enviesada para produções recentes. Note também como 70% dos filmes que não estão presentes em nenhum serviço por assinatura foram lançados antes de 2010. Na verdade, Netflix e Amazon Prime Video não possuem nenhum filme lançado antes de 1960.

Outra maneira de visualizar os resultados acima é separando os anos por década:

tbl_jw_principais %>% 
  mutate(decada = as.character(round(original_release_year %/% 10) * 10)) %>% 
  count(decada, object_type, provider_name) %>% 
  ggplot(aes(x = decada, y = n, fill = provider_name)) +
  geom_col(position = position_dodge()) +
  theme_bw() +
  labs(title = "Quantidade de títulos por década",
       x = NULL, 
       y = NULL) +
  facet_wrap(~ object_type, scales = "free_y", ncol = 1) +
  theme(legend.position = "bottom")

Qualidade dos filmes

Como parâmetro para qualidade dos filmes, usei a nota do IMDB, que também é fornecida no dataset do JustWatch.

tbl_jw_principais %>% 
  # filtrar filmes com nota IMDB acima de 7 para eliminar entradas erradas
  filter(scoring_provider_type == "imdb:score", value >= 7) %>% 
  mutate(value = cut(value, breaks = 0:10)) %>% 
  count(object_type, provider_name, value) %>% 
  ggplot(aes(x = value, y = n, fill = provider_name)) +
  geom_col(position = position_fill()) +
  theme_bw() +
  scale_y_continuous(breaks = seq(0, 1, .1),
                     labels = scales::percent) +
  facet_wrap(~ object_type) +
  labs(x = NULL,
       y = NULL,
       title = "Percentual de títulos por faixa de nota no IMDB") +
  theme(legend.position = "bottom")

Não aparenta haver diferença em relação à nota dos filmes e seriados de acordo com o streaming.

Exclusividade de conteúdo

Por último, mas não menos importante, existe o critério de exclusividade entre as plataformas, algo comum nesta indústria. Quantos filmes estão presentes apenas, por exemplo no Telecine Play? Quantos são possíveis de encontrar em mais de uma plataforma?

# criar matriz binaria
tbl_jw_principais_wide <- tbl_jw_principais %>% 
  distinct(tconst, provider_name) %>%
  mutate(provider_name = replace_na(provider_name, "nenhum")) %>% 
  mutate(tem = 1) %>% 
  spread(provider_name, tem) %>% 
  mutate_at(vars(-tconst), replace_na, 0) %>% 
  as.data.frame()

# grafico upset
upset(tbl_jw_principais_wide, nsets = 10,
      mainbar.y.label = NULL)

Também neste quesito a Netflix é disparado o melhor dentre os 3. 979 dos títulos são exclusivos da Netflix, muito mais que a soma dos outros dois serviços. É curioso que exista pouquíssima interseção entre as plataformas: apenas 17 títulos estão presentes tanto na Netflix como no Amazon Prime Video.

Bônus: Diagrama de Venn

Outra forma de visualizar os resultados acima é por meio de um diagrama de Venn:

lst <- tbl_jw_principais %>% 
  distinct(tconst, provider_name) %>% 
  split(.$provider_name) %>% 
  map("tconst")

gplots::venn(lst)

Conclusão

Usando o universo dos 5000 melhores títulos de filmes e seriados de acordo com o IMDB, a Netflix é bem superior às concorrentes no Brasil.

 
comments powered by Disqus