knitr::opts_chunk$set( warning = FALSE, message = FALSE, collapse = TRUE, fig.align = "center", comment = "#>" ) library(tidyverse) library(socscrap) library(ggplot2) library(glue) library(thematic) library(lubridate) library(viridis) data(ratings) data("report") ratings <- ratings %>% filter(!is.na(rating)) %>% mutate(year = year(date)) %>% filter(year >= 2000) keep_main_papers <- function(.db, n = 25) { main25 <- ratings %>% count(paper, sort = T) %>% head(n) %>% pull(paper) .db %>% filter(paper %in% main25) } thematic_on(bg = "#1d1f21", fg = "#c5c8c6", accent = "#c5c8c6") crossbar_col <- "#373b41"
Définitions & usages
| Méthode | Avantages | Inconvénients | | :----: | ---- | ---- | | Webscraping | Furtif ; interface ; contrôle ; ludique | Légalité floue ; complexité ; éphémère | | API | Légal si CGU ; données nettoyées ; requêtes simples ; durabilité | Limites ; prix ; inscription ; pas d'interface | | Accès direct à la base de données | Légal ; informations nettoyées ; pas de programmation ! | négociation ; pas de contexte / contrôle |
Les grands principes du webscraping
En pratique, notre bot va procéder en deux temps :
DiagrammeR::grViz(' digraph { bgcolor = "#1d1f21"; rankdir = LR; edge[color = "#c5c8c6", arrowsize = 0.5] node[shape = circle, fixedsize = true, penwidth = 2, fontsize = 8, fontname = "Arial", color = "#c5c8c6", fontcolor = "#c5c8c6"] Crawler Scraper node[shape = box, group = fbranch, color = "#b5bd68", fontcolor = "#b5bd68"] P1[label="Page 1"] P2[label="Page 2"] P3[label="Page ..."] P4[label="Page n"] node[shape = box, color = "#81a2be", fontcolor = "#81a2be"] L1[label="Ligne 1"] L2[label="Ligne 2"] L3[label="Ligne ..."] L4[label="Ligne n"] DATA[shape = none, color = "#cc6666", fontcolor = "#cc6666", label=< <TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0"> <TR><TD>Ligne 1</TD></TR> <TR><TD>Ligne 2</TD></TR> <TR><TD>Ligne ...</TD></TR> <TR><TD>Ligne n</TD></TR> </TABLE> >]; Crawler->P1:w Crawler->P2:w Crawler->P3:w Crawler->P4:w P1:e->Scraper:nw P2:e->Scraper:w P3:e->Scraper:w P4:e->Scraper:sw Scraper->L1:w Scraper->L2:w Scraper->L3:w Scraper->L4:w L1:e->DATA:w L2:e->DATA:w L3:e->DATA:w L4:e->DATA:w } ')
rvest
) -> bien pour débuter, scraping de base.mechanize
, scrapy
) -> pour les projets les plus ambitieux.rvest
, 2.0 pour Python et cie)r sum(report$films)
filmsr sum(report$ratings)
notes de presser round(sum(report$duration)/3600, 1)
heuresr as.character(sum(report$films)*2+floor(sum(report$films)/15))
requêtes, soit r as.character(round((sum(report$films)*2+floor(sum(report$films)/15))/sum(report$duration), 1))
requêtes par seconde.ratings %>% ggplot(aes(x = rating)) + geom_bar() + labs(x = "Note (étoiles)", y = "", title = "Distribution des évaluations presse", caption = "Données : Allociné, films avec avis presse")
ratings %>% group_by(year) %>% summarise(mean_rating = mean(rating), sd_rating = sd(rating), min_sd = mean(rating) - sd(rating), max_sd = mean(rating) + sd(rating)) %>% ggplot(aes(x = year, y = mean_rating, fill = sd_rating)) + geom_crossbar(aes(ymin = min_sd, ymax = max_sd), colour = crossbar_col) + scale_fill_viridis() + labs(x = "", y = "Note (étoiles)", fill = "Écart-type", title = "Évolution des évaluations presse au fil des ans", caption = "Données : Allociné, films avec avis presse")
ratings %>% keep_main_papers() %>% mutate(paper = factor(paper)) %>% group_by(paper) %>% summarise(nb_rating = n(), mean_rating = mean(rating), sd_rating = sd(rating), min_sd = mean(rating) - sd(rating), max_sd = mean(rating) + sd(rating)) %>% mutate(paper = glue("{paper} (n = {nb_rating})")) %>% ggplot(aes(x = fct_reorder(paper, mean_rating), y = mean_rating, fill = sd_rating)) + geom_crossbar(aes(ymin = min_sd, ymax = max_sd), colour = crossbar_col) + coord_flip() + scale_fill_viridis() + labs(x = "", y = "Note (étoiles)", fill = "Écart-type", title = "Distribution des évaluations selon la revue", caption = "Données : Allociné, films avec avis presse")
ratings %>% keep_main_papers() %>% group_by(year, paper) %>% summarise(mean_rating = mean(rating), sd_rating = sd(rating), min_sd = mean(rating) - sd(rating), max_sd = mean(rating) + sd(rating)) %>% ggplot(aes(x = year, y = mean_rating, fill = sd_rating)) + geom_crossbar(aes(ymin = min_sd, ymax = max_sd), colour = crossbar_col) + facet_wrap(~ paper) + scale_fill_viridis() + theme(axis.text.x = element_text(angle = 45, hjust = 1)) + labs(x = "", y = "Note (étoiles)", fill = "Écart-type", title = "Évolution des évaluations presse au fil des ans", subtitle = "Pour chaque revue", caption = "Données : Allociné, films avec avis presse")
get_ratings(2017)
get_film_metadata(url)
get_text(".class")
data(ratings)
data(report)
vignette("allocine-scraper")
DiagrammeR::grViz(' digraph { bgcolor = "#1d1f21"; rankdir = LR; edge[color = "#c5c8c6"] node[shape = box, penwidth = 2, fontname = "Arial", color = "#c5c8c6", fontcolor = "#c5c8c6"] F1[label="get_ratings"] F2[label="process_filmlist"] F3[label="get_film_ratings"] F4[label="get_film_metadata"] F5[label="get_press_ratings"] F6[label="get_text"] F1->F2->F3; F3->F4; F3->F5; F5->F6; } ', height = 200)
get_ratings
) au plus bas (get_text
)?get_ratings
get_ratings
, puis la coder soi-même ; puis remplacer process_filmlist
; puis get_film_ratings
get_ratings(2017, pages = 4)
https://www.allocine.fr/film/fichefilm-215099/critiques/presse/
"https://www.allocine.fr/film/fichefilm-215099/critiques/presse/" %>% get_film_ratings()
DiagrammeR::grViz(' digraph { bgcolor = "#1d1f21"; rankdir = LR; edge[color = "#c5c8c6"] node[shape = box, penwidth = 2, fontname = "Arial", color = "#c5c8c6", fontcolor = "#c5c8c6"] F3[label="get_film_ratings"] F4[label="get_film_metadata"] F5[label="get_press_ratings"] node[shape = box, penwidth = 2, fontname = "Arial", color = "#b5bd68", fontcolor = "#b5bd68"] P4[label="Fiche du film"] P5[label="Notes presse\n du film"] node[shape = box, penwidth = 2, fontname = "Arial", color = "#f0c674", fontcolor = "#f0c674"] D4[label="Métadonnées"] D5[label="Notes presse"] node[shape = box, penwidth = 0, fontname = "Arial", color = "#cc6666", fontcolor = "#cc6666"] D0[label=< <TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0" CELLPADDING="5"> <TR><TD>Notes</TD><TD>Métadonnées</TD></TR> </TABLE> >] F3->F4; F3->F5; F4->P4; F5->P5; P4->D4; P5->D5; D4->D0; D5->D0; } ')
get_press_ratings
"https://www.allocine.fr/film/fichefilm-215099/critiques/presse/" %>% get_press_ratings()
get_film_metadata
"https://www.allocine.fr/film/fichefilm_gen_cfilm=215099.html" %>% get_film_metadata()
Ctrl+Shift+K
Ctrl+Shift+I
GET
, POST
, HEADERS
) -> onglet NetworkGET
POST
Balises imbriquées :
<div> <p>Liste à puces</p> <ul> <li>Élément 1</li> <li>Élément 2</li> </ul> </div>
Balises = nœud (node)
DiagrammeR::grViz(' digraph { bgcolor = "#1d1f21"; rankdir = LR; edge[color = "#c5c8c6"] node[shape = box, penwidth = 2, fixedsize = TRUE, fontname = "Arial", color = "#c5c8c6", fontcolor = "#c5c8c6"] div;p;ul; li1[label="li"] li2[label="li"] div->p; div->ul; ul->li1; ul->li2; } ')
//div/ul/li
DiagrammeR::grViz(' digraph { bgcolor = "#1d1f21"; rankdir = LR; edge[color = "#c5c8c6"] node[shape = box, penwidth = 2, fixedsize = TRUE, fontname = "Arial", color = "#c5c8c6", fontcolor = "#c5c8c6"] div;p;ul; li1[label="li"] li2[label="li"] div->p; div->ul; ul->li1; ul->li2; } ')
<div> <p>Liste à puces</p> <ul> <li class="rouge">Élément 1</li> <li class="jaune">Élément 2</li> </ul> </div>
get_text()
(de soscrap
)"https://www.allocine.fr/film/fichefilm_gen_cfilm=215099.html" %>% xml2::read_html() %>% get_text(".date")
rnorm(100) %>% mean()
dplyr
, forcats
, stringr
, lubridate
, ...%>%
(pipe) qui devient vite indispensable (paquet magrittr
)read_csv(here::here("data", "enquete_emploi.csv"))
setwd()
, enfin des chemins reproductibles !library(glue) glue("Adieu", "horrible", "paste()", .sep = " ") a <- 41 glue("La réponse à votre question est {a+1}.")
paste()
et collapse()
fstrings
dans Python 3.8, printf
chez UNIX, ...)nom_de_la_fonction <- function(arg1, arg2 = FALSE) { result <- arg1 if(arg2) { result <- arg1 + arg2 } # Le retour de la fonction est implicite dans R result } nom_de_la_fonction(3)
message()
plutôt que print()
stop()
avec un message d'erreur clairreturn()
pour forcer un retourrlang::is_missing()
pour les arguments normauxrlang::quo_is_missing()
testthat
nom_de_la_fonction <- function(arg1, arg2 = FALSE) { if(rlang::is_missing(arg1)) { return(NA) stop("Vous avez oublié de donner 'arg1'!") } result <- arg1 if(arg2) { message("On ajoute arg2") result <- arg1 + arg2 } result }
Ou comment j'ai appris à ne plus m'en faire et à aimer les boucles
pages <- c(1, 2, 3) films <- tibble( titre = character(), real = character(), ) for (page in pages) { films <- films %>% add_row(tibble(titre = glue("Film {page}"), real = glue("Real {page}")) ) }
Qu'est-ce que j'obtiens si je fais print(films)
?
pages <- c(1, 2, 3) films <- tibble( titre = character(), real = character(), ) for (page in pages) { films <- films %>% add_row(tibble(titre = glue("Film {page}"), real = glue("Real {page}")) ) } print(films)
for(annee in annees) { for(page in pages) { for(film in films) { for (note in notes) { return(toutes_les_notes) } films %>% add_row(...) } } return(films %>% add_column(annee = annee)) }
Quels problèmes ?
for(annee in annees) { for(page in pages) { for(film in films) { for (note in notes) { return(toutes_les_notes) } films %>% add_row(...) } } return(films %>% add_column(annee = annee)) }
Quels problèmes ?
pages <- c(1, 2, 3) films <- tibble( titre = character(), real = character(), ) for (page in pages) { films <- films %>% add_row(tibble(titre = glue("Film {page}"), real = glue("Real {page}")) ) } print(films)
library(purrr) pages <- c(1, 2, 3) films <- map_df(pages, ~ tibble(titre = glue("Film {.}"), real = glue("Real {.}")) ) print(films)
map_chr(films$real, ~ str_extract(., "\\d")) map_int(films$real, ~ as.integer(str_extract(., "\\d"))) map2_chr(films$titre, films$real, ~ glue("{.x} par {.y}"))
purrr
map
: une listemap_int
: un vecteur d'integers
map_chr
: un vecteur de textemap_df
: une base de données type tibble# Définir une fonction pour récupérer les évaluations des films # d'une page à partir de son URL get_ratings_from_page <- function(url) { ... } # Récupération des URLs des pages des films de cette année this_year_urls <- ... # Lancer le scraping et retourner une base de données toute faite :) this_year_ratings <- map_df(this_year_urls, ~ get_ratings_from_page(.x))
urls <- c("https://google.com", "http://je-nexiste-pas.lol") # Je récupère les nœuds de chaque page resultat <- map(urls, ~ httr::GET(url) %>% read_html() %>% get_nodes()) print(resultat)
urls <- c("https://google.com", "http://je-nexiste-pas.lol") # Je récupère les nœuds de chaque page, en retournant des NA en cas d'échec resultat <- map_chr(urls, possibly(~ read_html(url) %>% html_text(), NA_character_ )) print(resultat)
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.