Télécharger les podcasts de Radio France, épisode 2

Le retour

En 2013, j’avais écris un petit script python pour télécharger les podcasts de Radio France.

Depuis, ils ont remplacé leur player Flash maison par un player Flash open source : Jplayer. Mon script ne fonctionne plus.

Je vais saisir cette occasion pour réécrire le script de manière plus modulaire, testable, et en faire un vrai projet Github.

Échafaudages

Avant de réécrire le code, on va écrire les tests unitaires qui formalisent ce que l’on en attend. On appelle cela le Test Driven Development.

Dans la philosophie Unix du “one task one tool”, on attend du script qu’il prenne en entrée l’adresse de la page Web d’une émission et nous donne en sortie l’adresse directe du fichier mp3 correspondant.

Je fournis : http://www.franceculture.fr/emission-le-tete-a-tete-sophie-calle-rediffusion-de-l-emission-du-30-septembre-2012-2013-08-18

J’obtiens : http://www.franceculture.fr/sites/default/files/sons/2013/08/s33/RF_257BABE0-01E9-449D-AFF8-3A771876A60A_GENE.MP3

J’utilise la bibliothèque unittest pour expliciter ces attentes :

import unittest

from radiofrance import *

class KnownValues(unittest.TestCase):                          
    knownValuesPodcast = (
        ( 'http://www.franceculture.fr/emission-le-tete-a-tete-sophie-calle-rediffusion-de-l-emission-du-30-septembre-2012-2013-08-18',
          'http://www.franceculture.fr/sites/default/files/sons/2013/08/s33/RF_257BABE0-01E9-449D-AFF8-3A771876A60A_GENE.MP3' ), )

    def testKnownValuesPodcast(self):
        for page, podcast in self.knownValuesPodcast:
            result = get_podcast(page)
            self.assertEqual(result, podcast)

if __name__ == "__main__":
    unittest.main()

Voici le squelette de radiofrance.py avec juste une fonction get_podcast() :

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import codecs, locale, os, sys
sys.stdout = codecs.getwriter('utf-8')(sys.stdout)

def get_podcast(page):
    return ''

def main():
    global VERBOSE

    from optparse import OptionParser

    usage="""
%prog [ options ]

Exemple:

$ python radiofrance.py http://www.franceculture.fr/emission-le-tete-a-tete-sophie-calle-rediffusion-de-l-emission-du-30-septembre-2012-2013-08-18
  """[1:-3]

    parser = OptionParser(usage=usage)
    parser.add_option('--verbose',
                      help='verbose',
                      default=VERBOSE,
                      action='store_true',
                      dest='verbose')

    options, args = parser.parse_args()

    VERBOSE = options.verbose

    if not args:
        print usage

    for arg in args:
        print get_podcast(arg)

if __name__ == '__main__':
    sys.exit(main())

Découper pour mieux régner

Pour l’instant, le script prend bien des arguments en ligne de commande mais ne fait rien. Si on lance le test, il FAIL. On va y remédier. On va découper la fonction get_podcast() en autant d’étapes testables que l’on a pu observer dans la phase de recherche :

  • get_content_id_from_page() : récupérer l’identifiant unique de l’émission dans le code HTML de la page de l’émission ;
  • get_data_from_iframe() : parser le contenu de l’iframe à la recherche des informations qui nous intéressent.

On ajoute la fonction get_opener() qui initialise notre navigateur dans le script. J’utilise le module urlib2 par habitude mais sachez qu’un module de plus haut niveau, mieux pensé existe : requests.

Certains sites se protègent du scraping en utilisant des variables de session ou en filtrant l’entête User-Agent envoyée par le client HTTP au serveur pour se présenter. On prend donc nos précautions en activant les cookies et en spoofant le User-Agent de Internet Explorer 5.5.

Photo finish

Le script final est sur Github : http://github.com/nilshamerlinck/radiofrance-podcasts. À noter que lancé avec l’option —verbose, il affichera une commande wget toute prête pour télécharger le podcast dans le répertoire courant avec un nom explicite.