Plan du site  
pixel
pixel

Articles - Étudiants SUPINFO

Utiliser l'API de Reddit

Par Thibault HOTTOU Publié le 13/09/2017 à 14:01:17 Noter cet article:
(0 votes)
En attente de relecture par le comité de lecture

Reddit est une plateforme de partage de contenu variée, chaque utilisateur peut y poster des liens vers des sites externes pour montrer des vidéos stockées sur Youtube, un message sur Facebook, une image sur imgur ou juste un petit pavé de texte que vous venez d'écrire. Chaque post de reddit possède un score qui permet d'obtenir un classement des posts les plus aimés pour la journée en cours, et peut être lié à un subreddit qui n'est autre qu'un sous-forum pour un communauté précise.

Par exemple le subreddit r/programming est crée uniquement pour des posts en rapport à la programmation, les gens peuvent alors écrire des commentaires sur les posts du subreddit qu'ils préfèrent.

Ce que nous réaliserons

Cet article va présenter comment concevoir deux types d'applications: la première va récupérer une liste d'images de multiples subreddit de qualité pour obtenir une sorte de galerie; la seconde application va lister tous les articles de programmation récents pour obtenir un condensé des nouvelles sur de nombreux domaines.

Pour faire tout ça nous utiliserons du Javascript dans le navigateur avec de l'html et du CSS pour un peu de design. L'article sera adapté pour que n'importe qui ayant déjà fait un peu de programmation puisse le comprendre.

Galerie d'images

Commençons par l'application qui sera une sorte de galerie d'images récupérées à partir de l'API de reddit pour des subreddits de communautés dédiées au partage d'images de bonne qualités. Ces subreddits utilisent très souvent imgur.com pour héberger les images qu'ils postent, imgur.com est une plateforme de partage pour les images et pour les gifs. Note: imgur fut au départ crée par un utilisateur de reddit car il pensait qu'il n'y avait aucune solution simple et efficace qui permettait aux utilisateurs de reddit de partager facilement ses images, les deux se couplent alors parfaitement et ça va nous aider grandement.

L'architecture de l'application

Avant de commencer voyons à quoi va ressembler l'architecture principale de l'application galerie. Comme annoncé un peu plus tôt nous avons pour chaque application une page HTML qui est une syntaxe avec laquelle nous pouvons déclarer le squelette statique d'une page web. Chaque élément est déclaré à l'aide d'une balise, par exemple un paragraphe se fait avec la balise <p> et si nous voulons y insérer du texte il suffit d'écrire notre texte puis de dire au parser quand est-ce que le paragraphe se termine avec la fin de balise </p>. Nous ajoutons simplement simplement le même nom de balise avec un slash /. Un paragraphe complet se fait alors de la manière suivante

<p> Le contenu du paragraphe </p>

Il est aussi possible d'enchainer plusieurs balises et ainsi faire qu'une balise parente possède plusieurs balises enfants. Imaginons maintenant un block théorique qui serait composé de deux paragraphes, pour faire ça nous utiliserons la balise

<div> block théorique </div>

Et comme on peut voir ici nous pouvons choisir de mettre uniquement du texte à l'intérieur, histoire de pouvoir séparer physiquement deux zones de texte par exemple. Et donc pour mettre deux paragraphe dans un block <div> nous faisons ainsi:

<div>
  <p> Premier paragraphe </p>
  <p> Second paragraphe </p>
</div>

Bref, ce sont les bases de l'XML et de l'HTML qui permettent uniquement à hiérarchiser une page. Si nous voulons y ajouter un peu de couleur, de forme et modifier un peu les positions des éléments il va falloir que nous utilisions du CSS. Pour ça voyons alors à quoi ressemblent la page d'accueil de cette première application

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <link rel="stylesheet" href="image.css">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Reddit API | Images</title>
</head>
<body>
  
  <script src='images.js'></script>
</body>
</html>

Nous y déclarons

  • la langue utilisée dans la page, qui est l'anglais avec

    <html lang="en">
  • L'encodage de notre page qui est en UTF8 avec

    <meta charset="UTF-8">
  • Nous précisons que le style de la page sera présent dans le fichier image.css

    <link rel="stylesheet" href="image.css">
  • (noter que je passe quelques éléments peu importants) Et vers la fin je dis que le script en javascript sera posé dans le fichier image.js avec

    <script src='images.js'></script>

C'est dans le fichier image.css que nous préciserons le style de la page à l'aide de propriétés que nous pouvons définir nous même, comme la couleur du texte et du fond, la taille de la police de caractère. Imaginons alors que nous voulons modifier tous les paragraphes d'une page, pour faire ça le CSS nécessaire sera comme celui-ci

p {
}

Et nous pouvons ensuite définir les différentes propriétés de la balise <p> avec les ajoutant entre les accolades comme ceci

p {
  color: black;
  font-size: 16px;
  background-color: white;
}

Comprendre l'API utilisée

Avant de pouvoir utiliser à 100% l'API de reddit, il va falloir la comprendre, et surtout voir comment elle est composée. Pour faire ça je vous invite à aller sur reddit.com choisir un sujet qui vous intéresse, ou bien un subreddit (pour rappel: une communauté) comme r/pics ou r/earthporn Ce lien est totalement Safe For Work, le nom de la communauté peut paraître suspect mais il s'agit tout simplement d'un lieu où est partagé des photos de la planète Terre (Earth) et de ses paysages. On peut donc voir qu'en allant vers des communautés une liste de post est affichées où chacun des posts (dans le cas de r/pics et r/earthporn) est un lien vers une image.

Maintenant puisque une interface n'est pas ce dont nous avons besoin, ajoutez .json à la fin de l'url de la communauté choisie ou du post, etc... Par exemple r/earthporn/.json et là nous obtenons un pavés de texte pas bien facile à cerner

{"kind": "Listing", "data": {"modhash": 
"6egn7r2io52fc940d89329dcc46a62f72c576e9367783916ce"
, "children": [{"kind": "t3", "data": {"domain": "i.redd.it", "approved_at_utc": null, 
"banned_by": null, "media_embed": {}, "thumbnail_width": 140, "subreddit": "EarthPorn", 
"selftext_html": null, "selftext": "", "likes": null, "suggested_sort": null, 
"user_reports": [], "secure_media": null, "link_flair_text": null, "id": "6zf0vh", 
"banned_at_utc": null, "view_count": null, "archived": false, "clicked": false, 
"report_reasons": null, 
"title": "Enchanted tidal pool at Lofoten islands in Norway [OC] [2000x1383]",

... Que j'ai coupé parceque c'est tout de même un peu long. Il s'agit en réalité de JSON (JavaScrip Object Notation) qui est une manière d'écrire des informations dans une syntaxe très proche de celle du javascript pour que, nous utilisateurs, puissions interpreter cette grosse quantité de données très rapidement à l'aide du javascript. Car le JSON est entre autre conçu pour le Javascript, ils se couplent alors parfaitement. Un objet JSON se présente de la manière suivante

{
  "parent": {
    "child_1": {
      "name": "Enfant Numero 1",
      "brother": "child_2"
    }
    "child_2": {
      "name": "Enfant Numero 2",
      "brother": "child_1"
    }
  }
}

Et ce n'est que tu texte au final, les tabs ainsi que les éspaces peuvent être supprimés sans que le sens (peut être plus pou nous, mais pour le javascript) ne change.

L'API de reddit suit exactement la même logique, en théorie elle devrait ressembler à un élément principal qui sera la page avec deux, trois informations puis ensuite une liste d'enfants qui seraient les posts qui nous intéressent. Puis chacun de ces enfants va avoir des informations comme le lien vers lequel le post dirige, le lien de l'image en miniature, le titre du post, l'auteur du post et le score du post (c'est une liste non exhaustive).

De l'API à notre programme

Déjà, je suppose que vous êtes sur un navigateur récent et que celui-ci possède donc de quoi interpréter du Javascript un minimum récent, lorsque vous allez tester le code présenté dans l'article je conseil vivement de faire les mises-à-jours si ce dernier ne fonctionne pas et si ça ne marche pas et que vous êtes pas sous chrome, changez temporairement de navigateur... Mais normalement ça ne devrait pas arriver je vous rassure !

Donc, pour pouvoir récuperer des données d'un serveur externe au notre il va falloir faire un fetch de ces données. Une des dernières version de javascript possède de quoi nous aider grandement lors de cette tache. La fonction fetch peut alors récupérer le résultat d'une requête (GET ou POST au choix) pour une adresse donnée. Cette fonction retourne ce qu'on appel une promesse, une fonction asynchrone à laquelle nous pouvons assigner une action pour le moment où cette action sera terminée. Le tout se fait de la manière suivante

fetch(`http://www.reddit.com/r/EarthPorn/.json`)
.then(response => ... )

Ici nous avons dit d'envoyer une requête à l'adresse `http://www.reddit.com/r/EarthPorn/.json` puis avec .then() nous pouvons donner une fonction qui sera exécutée une fois la réponse reçue.

Dans une des dernière version de javascript, async / await sont apparuts. Ces deux mots clés permettent d'aplatir les promesses histoire d'avoir un code un peu plus facile à comprendre. Le mot async permet de déclarer qu'une fonction précise est asynchrone et que ses actions ne suivront pas nécessairement la logique chronologique du code:

fetch(`http://www.reddit.com/r/EarthPorn/.json`)
.then(response => ... )

fetch(`http://www.reddit.com/r/EarthPorn/.json`)
.then(response => ... )

Dans un cas comme celui-ci, il n'est pas dit que la première requête soit terminée avant que la seconde se termine. Le code ne suit donc plus aucun ordre chronologique, à part biensûr l'ordre des .then d'une promesse. Par contre avec async et await le code suit alors l'ordre chronologique, voyez donc le code suivant:

async function startFetching() {
  const response_first = await fetch(`http://www.reddit.com/r/EarthPorn/.json`)
  const response_two = await fetch(`http://www.reddit.com/r/EarthPorn/.json`)
}

Dans ce cas là, le second fetch ne sera pas lancé avant que le premier n'ai répondu. Cependant le code doit être obligatoirement dans une fonction notée async

Voyons maintenant comment prendre la réponse du serveur reddit et obtenir un JSON.

async function main() {
  const response = await fetch(`https://www.reddit.com/r/EarthPorn/.json`)

  console.log(await response.json())
}

main()

Avec ceci nous obtenons dans la console du navigateur en message un objet que nous pouvons naviguer librement pour découvrir à quoi ressemble les réponses du serveur Reddit.

En éxplorant l'objet intéractif on découvre que pour accéder à la liste des posts reddit du subreddit choisit nous pouvons faire theObject.data.children. Il s'agit alors d'une liste d'objet chacun contenant les informations pour un post précis, nous n'avons alors plus qu'à boucler sur la liste et faire ce que nous voulons avec les informations obtenues.

Pour commencer je vais tout simplement faire en sorte que nous obtenions une liste de tous les titre de la première page de r/earthporn.

async function main() {
  const response = await fetch(`https://www.reddit.com/r/EarthPorn/.json`)
  const obj = await response.json()

  for (const post of obj.data.children) {
    const postDiv = document.createElement('div')
    postDiv.classList.add('post')
    postDiv.innerText = post.data.title

    document.body.appendChild(postDiv)
  }
}

main()

Dans la boucle for, pour chaque post nous créons une balise <div> dans laquelle le texte du titre y est inséré.

Pour y ajouter une image il ne reste plus qu'à suivre la même procédure que pour le titre, la variable postDiv qui est une balise <div> servira de parent dans laquelle seront les deux éléments postTitle et postImage qui respectivement afficheront le titre et l'image du post.

async function main() {
  const response = await fetch(`https://www.reddit.com/r/EarthPorn/.json`)
  const obj = await response.json()

  console.log(obj)
  for (const post of obj.data.children) {
    const postDiv = document.createElement('div')
    postDiv.classList.add('post')

    const postTitle = document.createElement('h3')
    postTitle.innerText = post.data.title
    postDiv.appendChild(postTitle)

    const postImage = document.createElement('img')
    postImage.setAttribute('src', post.data.url)
    postDiv.appendChild(postImage)

    document.body.appendChild(postDiv)
  }
}

main()

Vous pouvez dès maintenant voir le résultat de ce script, cependant ça ne sera pas très beau et les images auront chacunes des tailles différentes. Et pour ça nous allons voir un peu de CSS histoire que la page soit un minimum navigable et lisible.

Donc pour ça, comme vous l'avez peut-être remarqués chaque post possède une classe .post. Le CSS pourra alors cibler précisément chaque post et appliquer un style à leurs titres et images.

Je vais donc présenter un peu de CSS qui va faire que les images s'affichent dans une colonne une par une et où les images ne seront pas trop grandes sans pour autant modifier le ratio de ces images.

html, body {
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  color: rgb(30, 30, 30);
  font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif
}

div.post {
  display: block;
  padding: 2em 0;
  max-width: 50%;
}

div.post img {
  max-width: 100%;
}

div.post + div.post {
  border-top: solid 1px rgb(200, 200, 200);
}

Le premier style au selecteur html, body demande à ce que le body (l'élément maître d'un page html) affiche ses enfants dans une colonne et centrés dans cette colonne. Puis chaque div.post doit s'afficher les uns en dessous des autres et de doivent pas être plus large que 50% de la page. Les images des post ne doivent pas dépasser en largeur 100% de la largeur du post, soit 50% de la page. Et à la fin, si deux post se suivent, une légère bordure sera placée entre les deux post.

Insérez donc ça dans le fichier image.css et rechargez la page. Le résultat devrait être quasi-instantané (en fonction de la connexion) et cette fois chaque post est parfaitement aligné et lisible !

Liste des articles d'un subreddit

Maintenant que nous avons vu comment il était possible d'utiliser l'api de reddit pour obtenir une galerie d'images provenant d'un subreddit. Voyons comment faire pour prendre une liste d'articles provenant du subreddit r/programming, cette partie sera bien plus courte car je ne vais pas éxpliquer chaque détails comme j'ai fait lors de la première partie.

Commençons: Déjà, reprennons la même base HTML, CSS et JS que pour l'ancienne application. Et parlons de javascript dirèctement car pour le reste quasiment rien est à changer.

const tag = (type, opt = {}) => {
  const t = document.createElement(type)

  if (opt.className)
    t.classList.add(opt.className)

  if (opt.href)
    t.setAttribute('href', opt.href)

  if (opt.innerText)
    t.innerText = opt.innerText

  return t
}

async function main() {
  const response = await fetch(`https://www.reddit.com/r/programming/.json`)
  const obj = await response.json()

  console.log(obj)
  for (const post of obj.data.children) {
    const postDiv = document.createElement('div')
    postDiv.classList.add('post')

    const postTitle = tag('a', {
      className: 'title',
      href: post.data.url,
      innerText: post.data.title
    })
    postDiv.appendChild(postTitle)

    const postComments = tag('a', {
      innerText: `${post.data.num_comments} comments`,
      className: 'comments',
      href: `https://www.reddit.com/${post.data.permalink}`
    })
    postDiv.appendChild(postComments)

    const postAuthor = tag('a', {
      innerText: `by ${post.data.author}`,
      href: `https://www.reddit.com/u/${post.data.author}`,
      className: 'author'
    })
    postDiv.appendChild(postAuthor)

    document.body.appendChild(postDiv)
  }
}

main()

Je choisis un peu plus de données à afficher, et puisque le code serait un peu répétitif je crée alors une fonction utilitaire qui est tag qui permet de créer une balise du type choisit, avec le texte désiré, le lien désiré et la classe voulue pour qu'on puisse la cibler avec le CSS.

Après dans la fonction main c'est toujours le même fonctionnement sauf que cette fois-ci on prend pour chaque post le titre du post et le lien du post, le nombre de commentaires sur le post et un lien vers les commentaires reddits puis l'auteur du post en plus d'un lien vers son profil.

On y ajoute un peu de CSS (qui est très proche de celui de la galerie d'image) pour que le tout soit simple et efficace

html, body {
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  color: rgb(30, 30, 30);
  font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif
}

div.post {
  display: block;
  padding: 2em 0;
  width: 50%;
}

div.post a.title {
  display: block;
  font-size: 1.5em;
  font-weight: bold;
  text-decoration: none;
  color: inherit;
  margin-bottom: 0.5em;
}

div.post a.comments, div.post a.author {
  display: inline-block;
  text-decoration: none;
  color: inherit;
  margin-right: 0.4em;
}

div.post a:hover {
  text-decoration: underline;
}

div.post + div.post {
  border-top: solid 1px rgb(200, 200, 200);
}

Conclusion

L'api de reddit est au final très bien conçue et permet alors la récuparation de toutes les données dont nous pouvons avoir besoin très facilement grace au format employé, le JSON. Qui se couple parfaitement avec le Javascript, ce qui est un très bon choix lorsque nous désirons faire des interfaces dynamiques où les données doivent être chargées à la volée comme dans le cas des deux applications vu lors de cette article.

La première application récupérait alors une liste d'image provenant de reddit et d'une de ses communauté pour composer une galerie d'images de qualité.

La seconde former une liste des articles d'une des communautés de programmation de reddit, et ce dans un format compacte, propre et lisible avec le minimum d'informations dont nous aurions besoin pour une lecture efficace et rapide.

A propos de SUPINFO | Contacts & adresses | Enseigner à SUPINFO | Presse | Conditions d'utilisation & Copyright | Respect de la vie privée | Investir
Logo de la société Cisco, partenaire pédagogique de SUPINFO, la Grande École de l'informatique, du numérique et du management Logo de la société IBM, partenaire pédagogique de SUPINFO, la Grande École de l'informatique, du numérique et du management Logo de la société Sun-Oracle, partenaire pédagogique de SUPINFO, la Grande École de l'informatique, du numérique et du management Logo de la société Apple, partenaire pédagogique de SUPINFO, la Grande École de l'informatique, du numérique et du management Logo de la société Sybase, partenaire pédagogique de SUPINFO, la Grande École de l'informatique, du numérique et du management Logo de la société Novell, partenaire pédagogique de SUPINFO, la Grande École de l'informatique, du numérique et du management Logo de la société Intel, partenaire pédagogique de SUPINFO, la Grande École de l'informatique, du numérique et du management Logo de la société Accenture, partenaire pédagogique de SUPINFO, la Grande École de l'informatique, du numérique et du management Logo de la société SAP, partenaire pédagogique de SUPINFO, la Grande École de l'informatique, du numérique et du management Logo de la société Prometric, partenaire pédagogique de SUPINFO, la Grande École de l'informatique, du numérique et du management Logo de la société Toeic, partenaire pédagogique de SUPINFO, la Grande École de l'informatique, du numérique et du management Logo du IT Academy Program par Microsoft, partenaire pédagogique de SUPINFO, la Grande École de l'informatique, du numérique et du management

SUPINFO International University
Ecole d'Informatique - IT School
École Supérieure d'Informatique de Paris, leader en France
La Grande Ecole de l'informatique, du numérique et du management
Fondée en 1965, reconnue par l'État. Titre Bac+5 certifié au niveau I.
SUPINFO International University is globally operated by EDUCINVEST Belgium - Avenue Louise, 534 - 1050 Brussels