{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "Dans ce billet, nous allons [comme il y a quelques semaines](http://flothesof.github.io/podcast-rendez-vous-avec-X-fr.html), construire un flux RSS pour mon émission radio préférée : *Sur les épaules de Darwin*, animée par l'extraordinaire Jean Claude Ameisen.\n", "\n", "Afin de faire ceci, nous allons partir du [travail de Clément Grimal](http://clementgrimal.fr/darwin/), qui maintient une liste à jour des émissions avec des liens de téléchargement. \n", "\n", "Une question, avant de commencer. Pourquoi faire ce travail, alors que le site de Clément permet déjà d'écouter à volonté ces émissions ? Parce que son site ne permet pas de faire une recherche plein texte sur le contenu des émissions et également parce que j'aime écouter les émissions dans un logiciel de podcast afin de garder un historique de mes écoutes (j'utilise [Podcast Addict](https://play.google.com/store/apps/details?id=com.bambuna.podcastaddict&hl=en)).\n", "\n", "\n", "**TL;DR**\n", "Le lien vers le flux RSS généré à partir du présent notebook est ici : *https://raw.githubusercontent.com/flothesof/posts/master/files/podcast_Sur_les_epaules_de_Darwin.xml*" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Date de dernière mise à jour : *avril 2020*." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Récupération des liens vers les émissions" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Comme je le disais, notre point de départ est le site de Clément Grimal. Nous analysons sa source avec `beautifulsoup4` pour en extraire les éléments qui nous intéressent :\n", "\n", "\n", "- titre de l'émission\n", "- date de diffusion\n", "- lien de téléchargement\n", "- lien vers la page du site France Inter" ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "import requests\n", "from bs4 import BeautifulSoup\n", "\n", "\n", "base_url = \"http://clementgrimal.fr/darwin/\"\n", "\n", "r = requests.get(base_url)\n", "r.encoding = 'utf-8'\n", "\n", "soup = BeautifulSoup(r.text, 'html.parser')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "L'objet `soup` nous permet d'extraire les différents éléments de la page web, de manière structurée. On obtient la liste de toutes les émissions de cette manière :" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "516" ] }, "execution_count": 2, "metadata": {}, "output_type": "execute_result" } ], "source": [ "all_shows = ([item for item in (li.find('table') for li in soup.findAll('li')) if item])\n", "\n", "len(all_shows)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "A partir du tag `li` (item de liste en HTML), on peut obtenir le lien vers le fichier mp3 ainsi que le site France Inter, de même que le titre et la date de diffusion.\n", "\n", "Par exemple avec le dernier épisode : " ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "'./files/2020-04-11 - aux-origines-du-chocolat-8.mp3'" ] }, "execution_count": 3, "metadata": {}, "output_type": "execute_result" } ], "source": [ "show = all_shows[-1]\n", "\n", "show.find('a', class_='download-link').attrs['href'].strip()" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "'https://www.franceinter.fr/emissions/sur-les-epaules-de-darwin/sur-les-epaules-de-darwin-11-avril-2020'" ] }, "execution_count": 4, "metadata": {}, "output_type": "execute_result" } ], "source": [ "show.find('span').find('a').attrs['href']" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "'Aux Origines du Chocolat (8)'" ] }, "execution_count": 5, "metadata": {}, "output_type": "execute_result" } ], "source": [ "show.find('span').find('a').text" ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "'11 Avril 2020'" ] }, "execution_count": 6, "metadata": {}, "output_type": "execute_result" } ], "source": [ "str(show.find('span').find('a').next_sibling)[14:]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Ecrivons maintenant une boucle pour extraire ces informations pour l'intégralité des épisodes :" ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "lien de téléchargement indisponible pour l'émission https://www.franceinter.fr/emissions/sur-les-epaules-de-darwin/sur-les-epaules-de-darwin-12-janvier-2013\n", "lien de téléchargement indisponible pour l'émission https://www.franceinter.fr/emissions/sur-les-epaules-de-darwin/sur-les-epaules-de-darwin-28-mars-2015\n", "lien de téléchargement indisponible pour l'émission https://www.franceinter.fr/emissions/sur-les-epaules-de-darwin/sur-les-epaules-de-darwin-04-avril-2015\n", "lien de téléchargement indisponible pour l'émission https://www.franceinter.fr/emissions/sur-les-epaules-de-darwin/sur-les-epaules-de-darwin-11-avril-2015\n", "lien de téléchargement indisponible pour l'émission https://www.franceinter.fr/emissions/sur-les-epaules-de-darwin/sur-les-epaules-de-darwin-02-juin-2019\n", "lien de téléchargement indisponible pour l'émission https://www.franceinter.fr/emissions/sur-les-epaules-de-darwin/sur-les-epaules-de-darwin-09-juin-2019\n", "lien de téléchargement indisponible pour l'émission https://www.franceinter.fr/emissions/sur-les-epaules-de-darwin/sur-les-epaules-de-darwin-29-juin-2019\n", "lien de téléchargement indisponible pour l'émission https://www.franceinter.fr/emissions/sur-les-epaules-de-darwin/sur-les-epaules-de-darwin-06-juillet-2019\n", "lien de téléchargement indisponible pour l'émission https://www.franceinter.fr/emissions/sur-les-epaules-de-darwin/sur-les-epaules-de-darwin-13-juillet-2019\n", "lien de téléchargement indisponible pour l'émission https://www.franceinter.fr/emissions/sur-les-epaules-de-darwin/sur-les-epaules-de-darwin-20-juillet-2019\n", "lien de téléchargement indisponible pour l'émission https://www.franceinter.fr/emissions/sur-les-epaules-de-darwin/sur-les-epaules-de-darwin-27-juillet-2019\n", "lien de téléchargement indisponible pour l'émission https://www.franceinter.fr/emissions/sur-les-epaules-de-darwin/sur-les-epaules-de-darwin-03-aout-2019\n", "lien de téléchargement indisponible pour l'émission https://www.franceinter.fr/emissions/sur-les-epaules-de-darwin/sur-les-epaules-de-darwin-10-aout-2019\n", "lien de téléchargement indisponible pour l'émission https://www.franceinter.fr/emissions/sur-les-epaules-de-darwin/sur-les-epaules-de-darwin-17-aout-2019\n", "lien de téléchargement indisponible pour l'émission https://www.franceinter.fr/emissions/sur-les-epaules-de-darwin/sur-les-epaules-de-darwin-24-aout-2019\n", "lien de téléchargement indisponible pour l'émission https://www.franceinter.fr/emissions/sur-les-epaules-de-darwin/sur-les-epaules-de-darwin-30-novembre-2019\n", "lien de téléchargement indisponible pour l'émission https://www.franceinter.fr/emissions/sur-les-epaules-de-darwin/sur-les-epaules-de-darwin-14-decembre-2019\n", "lien de téléchargement indisponible pour l'émission https://www.franceinter.fr/emissions/sur-les-epaules-de-darwin/sur-les-epaules-de-darwin-21-decembre-2019\n", "lien de téléchargement indisponible pour l'émission https://www.franceinter.fr/emissions/sur-les-epaules-de-darwin/sur-les-epaules-de-darwin-01-fevrier-2020\n" ] } ], "source": [ "show_data = []\n", "for show in all_shows:\n", " download_link = show.find('a', class_='download-link')\n", " description_link = show.find('span').find('a').attrs['href']\n", " title = show.find('span').find('a').text\n", " date = str(show.find('span').find('a').next_sibling)[14:]\n", " if download_link:\n", " if download_link.attrs['href'].strip().startswith('.'):\n", " download_link = \"http://clementgrimal.fr/darwin\" + download_link.attrs['href'].strip()[1:]\n", " else:\n", " download_link = download_link.attrs['href'].strip()\n", " show_data.append([download_link, description_link, title, date])\n", " else: \n", " print(f\"lien de téléchargement indisponible pour l'émission {show.find('a').attrs['href']}\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Construisons une dataframe `pandas` avec ces données :" ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
lien mp3lien descriptiontitredate
0http://prevost.pascal.free.fr/public/podcast/s...http://www.franceinter.fr/em/sur-les-epaules-d...La théorie de l'évolution de Charles Darwin4 Septembre 2010
1http://prevost.pascal.free.fr/public/podcast/s...http://www.franceinter.fr/em/sur-les-epaules-d...Le propre de l'homme?11 Septembre 2010
2http://prevost.pascal.free.fr/public/podcast/s...http://www.franceinter.fr/em/sur-les-epaules-d...Nos mémoires18 Septembre 2010
3http://prevost.pascal.free.fr/public/podcast/s...http://www.franceinter.fr/em/sur-les-epaules-d...Nos émotions25 Septembre 2010
4http://prevost.pascal.free.fr/public/podcast/s...http://www.franceinter.fr/em/sur-les-epaules-d...Longévité, jeunesse et vieillissement2 Octobre 2010
5http://prevost.pascal.free.fr/public/podcast/s...http://www.franceinter.fr/em/sur-les-epaules-d...MORT CELLULAIRE ET SCULPTURE DU VIVANT9 Octobre 2010
6http://prevost.pascal.free.fr/public/podcast/s...http://www.franceinter.fr/em/sur-les-epaules-d...NAISSANCES16 Octobre 2010
7http://prevost.pascal.free.fr/public/podcast/s...http://www.franceinter.fr/em/sur-les-epaules-d...Biodiversité23 Octobre 2010
8http://prevost.pascal.free.fr/public/podcast/s...http://www.franceinter.fr/em/sur-les-epaules-d...mort cellulaire et sculpture du vivant (2)30 Octobre 2010
9http://prevost.pascal.free.fr/public/podcast/s...http://www.franceinter.fr/em/sur-les-epaules-d...Un voyage avec Oliver Sacks ( 1 )6 Novembre 2010
\n", "
" ], "text/plain": [ " lien mp3 \\\n", "0 http://prevost.pascal.free.fr/public/podcast/s... \n", "1 http://prevost.pascal.free.fr/public/podcast/s... \n", "2 http://prevost.pascal.free.fr/public/podcast/s... \n", "3 http://prevost.pascal.free.fr/public/podcast/s... \n", "4 http://prevost.pascal.free.fr/public/podcast/s... \n", "5 http://prevost.pascal.free.fr/public/podcast/s... \n", "6 http://prevost.pascal.free.fr/public/podcast/s... \n", "7 http://prevost.pascal.free.fr/public/podcast/s... \n", "8 http://prevost.pascal.free.fr/public/podcast/s... \n", "9 http://prevost.pascal.free.fr/public/podcast/s... \n", "\n", " lien description \\\n", "0 http://www.franceinter.fr/em/sur-les-epaules-d... \n", "1 http://www.franceinter.fr/em/sur-les-epaules-d... \n", "2 http://www.franceinter.fr/em/sur-les-epaules-d... \n", "3 http://www.franceinter.fr/em/sur-les-epaules-d... \n", "4 http://www.franceinter.fr/em/sur-les-epaules-d... \n", "5 http://www.franceinter.fr/em/sur-les-epaules-d... \n", "6 http://www.franceinter.fr/em/sur-les-epaules-d... \n", "7 http://www.franceinter.fr/em/sur-les-epaules-d... \n", "8 http://www.franceinter.fr/em/sur-les-epaules-d... \n", "9 http://www.franceinter.fr/em/sur-les-epaules-d... \n", "\n", " titre date \n", "0 La théorie de l'évolution de Charles Darwin 4 Septembre 2010 \n", "1 Le propre de l'homme? 11 Septembre 2010 \n", "2 Nos mémoires 18 Septembre 2010 \n", "3 Nos émotions 25 Septembre 2010 \n", "4 Longévité, jeunesse et vieillissement 2 Octobre 2010 \n", "5 MORT CELLULAIRE ET SCULPTURE DU VIVANT 9 Octobre 2010 \n", "6 NAISSANCES 16 Octobre 2010 \n", "7 Biodiversité 23 Octobre 2010 \n", "8 mort cellulaire et sculpture du vivant (2) 30 Octobre 2010 \n", "9 Un voyage avec Oliver Sacks ( 1 ) 6 Novembre 2010 " ] }, "execution_count": 8, "metadata": {}, "output_type": "execute_result" } ], "source": [ "import pandas as pd\n", "\n", "df = pd.DataFrame(show_data, columns=['lien mp3', 'lien description', 'titre', 'date'])\n", "df.head(10)" ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
lien mp3lien descriptiontitredate
count497497497497
unique497497394497
tophttp://clementgrimal.fr/darwin/files/2016-10-0...https://www.franceinter.fr/emissions/sur-les-e...Les battements du temps (21)11 Juin 2016
freq1131
\n", "
" ], "text/plain": [ " lien mp3 \\\n", "count 497 \n", "unique 497 \n", "top http://clementgrimal.fr/darwin/files/2016-10-0... \n", "freq 1 \n", "\n", " lien description \\\n", "count 497 \n", "unique 497 \n", "top https://www.franceinter.fr/emissions/sur-les-e... \n", "freq 1 \n", "\n", " titre date \n", "count 497 497 \n", "unique 394 497 \n", "top Les battements du temps (21) 11 Juin 2016 \n", "freq 3 1 " ] }, "execution_count": 9, "metadata": {}, "output_type": "execute_result" } ], "source": [ "df.describe()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Nous voilà maintenant en possession des liens vers 497 épisodes publiés avec lien de téléchargement sur le site de Clément Grimal. On remarque que certaines d'entre elles sont des rediffusions (394 titres uniques sur 497). " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Téléchargeons maintenant les descriptions des émissions, afin de construire un fil RSS avec celles-ci." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Téléchargement des descriptions à partir du site de France Inter " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Nous avons les liens vers la page de chaque émission : si on lit celle-ci, on peut en extraire le contenu que nous cherchons, c'est-à-dire la description de l'épisode.\n", "\n", "Essayons de faire cela avec le dernier épisode en date." ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "'https://www.franceinter.fr/emissions/sur-les-epaules-de-darwin/sur-les-epaules-de-darwin-11-avril-2020'" ] }, "execution_count": 10, "metadata": {}, "output_type": "execute_result" } ], "source": [ "link = df['lien description'].iloc[-1]\n", "link" ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [], "source": [ "soup = BeautifulSoup(requests.get(link).text, 'html.parser')" ] }, { "cell_type": "code", "execution_count": 12, "metadata": { "scrolled": false }, "outputs": [ { "data": { "text/plain": [ "
\n", "

\n", " Merci à vous, (un million six cent quatre vingt mille) auditeurs fidèles \n", "Portez-vous bien ainsi que tous les vôtres\n", "

\n", "
\"le
le chocolat © Getty / kolderal
\n", "

Une rediffusion de l'émission du 19 janvier 2019

\n", "

Jean Claude Ameisen, en écoutant l'émission, nous signale qu'il a fait un lapsus à la fin de la deuxième partie. Il voulait dire \"3 siècles\" (et non \"3 ans\" !) avant le règne du grand roi Hammourabi\".

\n", "

Articles scientifiques :

\n", "\n", "

Livres : 

\n", "\n", "

Chansons diffusées :

\n", "\n", "
" ] }, "execution_count": 12, "metadata": {}, "output_type": "execute_result" } ], "source": [ "article_tag = soup.find('article')\n", "article_tag" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Il se trouve que des balises script sont contenues dans les épisodes (elles affichent de la publicité sur le site de France Inter)." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Si on reformate les tags HTML, on obtient le résultat suivant :" ] }, { "cell_type": "code", "execution_count": 13, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "\"AccueilÉmissions\\n Sur les épaules de Darwin\\n Aux Origines du Chocolat (8) Sur les épaules de Darwin\\n samedi 11 avril 2020\\n par\\n Jean Claude AmeisenAux Origines du Chocolat (8)\\n 53 minutes\\n réécouters'abonnerPodcastRSSréagirContact\"" ] }, "execution_count": 13, "metadata": {}, "output_type": "execute_result" } ], "source": [ "soup.find_all('section')[0].text" ] }, { "cell_type": "code", "execution_count": 14, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "
\n", "

\n", " Merci à vous, (un million six cent quatre vingt mille) auditeurs fidèles \n", "Portez-vous bien ainsi que tous les vôtres\n", "

\n", "
\"le
le chocolat © Getty / kolderal
\n", "

Une rediffusion de l'émission du 19 janvier 2019

\n", "

Jean Claude Ameisen, en écoutant l'émission, nous signale qu'il a fait un lapsus à la fin de la deuxième partie. Il voulait dire \"3 siècles\" (et non \"3 ans\" !) avant le règne du grand roi Hammourabi\".

\n", "

Articles scientifiques :

\n", "\n", "

Livres : 

\n", "\n", "

Chansons diffusées :

\n", "\n", "
" ] }, "execution_count": 14, "metadata": {}, "output_type": "execute_result" } ], "source": [ "def clean(soup, tags_to_clean=['script']):\n", " \"\"\"Nettoie le HTML de l'émission en enlevant les balises inutiles.\"\"\"\n", " for tag in tags_to_clean:\n", " for item in soup.find_all(tag):\n", " item.decompose()\n", " return soup.article\n", "\n", "clean(soup)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "On peut afficher un aperçu de notre description :" ] }, { "cell_type": "code", "execution_count": 15, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", "

\n", " Merci à vous, (un million six cent quatre vingt mille) auditeurs fidèles \r\n", "Portez-vous bien ainsi que tous les vôtres\n", "

\n", "
\"le
le chocolat © Getty / kolderal
\n", "

Une rediffusion de l'émission du 19 janvier 2019

\n", "

Jean Claude Ameisen, en écoutant l'émission, nous signale qu'il a fait un lapsus à la fin de la deuxième partie. Il voulait dire \"3 siècles\" (et non \"3 ans\" !) avant le règne du grand roi Hammourabi\".

\n", "

Articles scientifiques :

\n", "\n", "

Livres : 

\n", "\n", "

Chansons diffusées :

\n", "\n", "
" ], "text/plain": [ "" ] }, "execution_count": 15, "metadata": {}, "output_type": "execute_result" } ], "source": [ "from IPython.display import HTML\n", "\n", "HTML(str(clean(soup)))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Ceci étant fait, on peut écrire une boucle pour extraire toutes les descriptions d'épisode :" ] }, { "cell_type": "code", "execution_count": 16, "metadata": {}, "outputs": [], "source": [ "from functools import lru_cache\n", "\n", "@lru_cache(maxsize=None)\n", "def link_getter(description_link):\n", " \"\"\"Récupère la page de l'émission en question, stocke le résultat pour un accès ultérieur plus rapide.\"\"\"\n", " return requests.get(description_link)" ] }, { "cell_type": "code", "execution_count": 17, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "100%|██████████| 497/497 [07:11<00:00, 1.15it/s]\n" ] } ], "source": [ "import tqdm\n", "\n", "descriptions = []\n", "for description_link in tqdm.tqdm(df['lien description']):\n", " soup = BeautifulSoup(link_getter(description_link).text, 'html.parser')\n", " descriptions.append(clean(soup))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "On peut compléter notre `dataframe` avec les descriptions obtenues :" ] }, { "cell_type": "code", "execution_count": 18, "metadata": {}, "outputs": [], "source": [ "df['description'] = descriptions\n", "\n", "df['description_text'] = [d.text for d in descriptions]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Afin de vérifier que tout va bien, nous allons faire un pré-affichage rapide avec `ipywidgets`." ] }, { "cell_type": "code", "execution_count": 19, "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { "model_id": "3d1607cf1b8945e58f0f0c68284f06c5", "version_major": 2, "version_minor": 0 }, "text/plain": [ "interactive(children=(IntSlider(value=248, description='n', max=496), Output()), _dom_classes=('widget-interac…" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "from ipywidgets import interact\n", "\n", "@interact\n", "def preview_html(n=(0, df.shape[0] - 1)):\n", " return HTML(f\"lien\" + str(df.description.iloc[n]))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Rajouter d'autres sections " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Comme on peut le voir sur cet épisode [https://www.franceinter.fr/emissions/sur-les-epaules-de-darwin/sur-les-epaules-de-darwin-16-mai-2015], parfois les références sont cachées dans un tag `section`, avec le nom de l'équipe de production. Comptons le nombre de tag `section` :" ] }, { "cell_type": "code", "execution_count": 20, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "100%|██████████| 497/497 [00:45<00:00, 10.90it/s]\n" ] } ], "source": [ "sections = []\n", "for description_link in tqdm.tqdm(df['lien description']):\n", " soup = BeautifulSoup(link_getter(description_link).text, 'html.parser')\n", " sections.append(len(soup.find('div', class_='main-content').find_all('section')))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Et regardons le comptage :" ] }, { "cell_type": "code", "execution_count": 21, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "2 295\n", "1 189\n", "3 10\n", "0 3\n", "dtype: int64" ] }, "execution_count": 21, "metadata": {}, "output_type": "execute_result" } ], "source": [ "pd.Series(sections).value_counts()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "On peut donc proposer un deuxième algorithme : on va chercher les sections supplémentaires en-dessous de `main-content` et on les nettoie elles-aussi." ] }, { "cell_type": "code", "execution_count": 22, "metadata": {}, "outputs": [], "source": [ "def clean2(soup, tags_to_clean=['script']):\n", " \"\"\"Nettoie le HTML de l'émission en enlevant les balises inutiles.\"\"\"\n", " for tag in tags_to_clean:\n", " for item in soup.find_all(tag):\n", " item.decompose()\n", " return str(soup.find('div', class_='main-content').article) + \"\\n\".join([str(item) for item in soup.find('div', class_='main-content').find_all('section', class_='content-body-more-details')])\n" ] }, { "cell_type": "code", "execution_count": 23, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", "

\n", " Merci à vous, (un million six cent quatre vingt mille) auditeurs fidèles \r\n", "Portez-vous bien ainsi que tous les vôtres\n", "

\n", "
\"le
le chocolat © Getty / kolderal
\n", "

Une rediffusion de l'émission du 19 janvier 2019

\n", "

Jean Claude Ameisen, en écoutant l'émission, nous signale qu'il a fait un lapsus à la fin de la deuxième partie. Il voulait dire \"3 siècles\" (et non \"3 ans\" !) avant le règne du grand roi Hammourabi\".

\n", "

Articles scientifiques :

\n", "\n", "

Livres : 

\n", "\n", "

Chansons diffusées :

\n", "\n", "
\n", " \n", " L'équipe\n", "\n", " \n", "
" ], "text/plain": [ "" ] }, "execution_count": 23, "metadata": {}, "output_type": "execute_result" } ], "source": [ "HTML(clean2(soup))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "On met à jour les descriptions." ] }, { "cell_type": "code", "execution_count": 24, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "100%|██████████| 497/497 [00:51<00:00, 9.64it/s]\n" ] } ], "source": [ "descriptions = []\n", "for description_link in tqdm.tqdm(df['lien description']):\n", " soup = BeautifulSoup(link_getter(description_link).text, 'html.parser')\n", " descriptions.append(clean2(soup))" ] }, { "cell_type": "code", "execution_count": 31, "metadata": {}, "outputs": [], "source": [ "df['description'] = descriptions\n", "\n", "df['description_text'] = [BeautifulSoup(desc, 'html.parser').text for desc in descriptions]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Et on vérifie qu'on a le résultat attendu." ] }, { "cell_type": "code", "execution_count": 32, "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { "model_id": "3ef78f0ad3ac449780aee360357c9068", "version_major": 2, "version_minor": 0 }, "text/plain": [ "interactive(children=(IntSlider(value=248, description='n', max=496), Output()), _dom_classes=('widget-interac…" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "@interact\n", "def preview_html(n=(0, df.shape[0] - 1)):\n", " return HTML(f\"lien\" + str(df.description.iloc[n]))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Un dernier test :" ] }, { "cell_type": "code", "execution_count": 27, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", "

\n", " Des animaux pharmaciens et médecins (2)\n", "

\n", "

Articles scientifiques :

\n", "

Luncz LV, Boesch C. The extent of cultural variation between adjacent chimpanzee (Pan troglodytes verus ) communities; a microecological approach. American Journal of Physical Anthropology  2015, 156:67-75.

\n", "

Gendron CM, Kuo TH, Harvanek ZM, et coll . Drosophila life span and physiology are modulated by sexual perception and reward. Science  2014, 343:544-8.

\n", "
\n", "\n", "

Shurkin J. News feature: Animals that self-medicate. Proceedings of the National Academy of Sciences of the USA 2014, 111:17339-41.

\n", "

Underwood E. The taste of things to come. Science  2014, 345:750-1.

\n", "

Kacsoh BZ, Lynch ZR, Mortimer NT, et coll . Fruit flies medicate offspring after seeing parasites. Science  2013, 339:947-50.

\n", "

de Roode J, Lefèvre T, Hunter M. Self-medication in animals. Science 2013, 340:150-1.

\n", "

Boesch C. Christophe Boesch. Current Biology  2012, 22:R936-7.

\n", "

Milan NF, Kacsoh BZ, Schlenke TA. Alcohol consumption as self-medication against blood-borne parasites in the fruit fly. Current Biology  2012, 22:488-93.

\n", "

Shohat-Ophir G, Kaun KR, Azanchi R, et coll . Sexual deprivation increases ethanol intake in Drosophila. Science  2012, 335:1351-5.

\n", "

Zars T. Physiology. She said no, pass me a beer. Science  2012, 335:1309-10.

\n", "

Luncz LV, Mundry R, Boesch C. Evidence for cultural differences between neighboring chimpanzee communities. Current Biology  2012, 22:922-6.

\n", "

Kaun KR, Azanchi R, Maung Z, et coll . A Drosophila model for alcohol reward. Nature Neuroscience  2011, 14:612-9.

\n", "

Morell V. Called ‘trimates’, three bold women shaped their field. Science 1993, 260:420-5.

\n", "

Morell V. Seeing nature through the lens of gender. Science  1993, 260:428-9.

\n", "

Autres publications :

\n", "

Dossier n°86 Pour la Science , janvier - mars 2015. Nos cousins les grands singes.

\n", "

Christophe Boesch. Les chimpanzés : des grands singes pétris de culture. Dossier n°86 Pour la Science , janvier - mars 2015. Nos cousins les grands singes, pp50-5.

\n", "

Letter from Charles Darwin to Asa Grey, 22 May 1860: http://www\\.darwinproject\\.ac\\.uk/letter/entry\\-2814

\n", "

Agenda :

\n", "

Exposition « Sur la piste des Grands Singes » , à la Grande Galerie de l’Évolution du Muséum national d’Histoire naturelle, Jardin des Plantes, Paris, du 11 février au 21 mars 2015. http://www.mnhn.fr/fr/visitez/agenda/exposition/piste-grands-singes

\n", "

Tout le programme de « La saison des Grands Singes » :  http://www\\.mnhn\\.fr/fr/visitez/agenda/saison/saison\\-grands\\-singes

\n", "
\n", " \n", " Les références\n", "\n", " \n", "
\n", "
\n", " \n", " L'équipe\n", "\n", " \n", "
" ], "text/plain": [ "" ] }, "execution_count": 27, "metadata": {}, "output_type": "execute_result" } ], "source": [ "soup = BeautifulSoup(link_getter(df['lien description'].iloc[230]).text, 'html.parser')\n", "\n", "HTML(clean2(soup))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Il nous manque encore la récupération des tailles de fichier mp3, qui sera inscrite dans le flux RSS :" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Taille des fichiers mp3 " ] }, { "cell_type": "code", "execution_count": 28, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "100%|██████████| 497/497 [00:14<00:00, 34.02it/s]\n" ] } ], "source": [ "byte_lengths = []\n", "for mp3_link in tqdm.tqdm(df['lien mp3']):\n", " r = requests.head(mp3_link)\n", " byte_lengths.append(r.headers['content-length'])" ] }, { "cell_type": "code", "execution_count": 29, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
lien mp3lien descriptiontitredatedescriptiondescription_textbyte_length
0http://prevost.pascal.free.fr/public/podcast/s...http://www.franceinter.fr/em/sur-les-epaules-d...La théorie de l'évolution de Charles Darwin4 Septembre 2010<article class=\"content-body\">\\n<p><strong>Bib...<article class=\"content-body\">\\n<p><strong>Bib...51120256
1http://prevost.pascal.free.fr/public/podcast/s...http://www.franceinter.fr/em/sur-les-epaules-d...Le propre de l'homme?11 Septembre 2010<article class=\"content-body\">\\n<h3><strong>pr...<article class=\"content-body\">\\n<h3><strong>pr...52287616
2http://prevost.pascal.free.fr/public/podcast/s...http://www.franceinter.fr/em/sur-les-epaules-d...Nos mémoires18 Septembre 2010<article class=\"content-body\">\\n<h3><strong>pr...<article class=\"content-body\">\\n<h3><strong>pr...51320960
3http://prevost.pascal.free.fr/public/podcast/s...http://www.franceinter.fr/em/sur-les-epaules-d...Nos émotions25 Septembre 2010<article class=\"content-body\">\\n<h3><strong>pr...<article class=\"content-body\">\\n<h3><strong>pr...54608000
4http://prevost.pascal.free.fr/public/podcast/s...http://www.franceinter.fr/em/sur-les-epaules-d...Longévité, jeunesse et vieillissement2 Octobre 2010<article class=\"content-body\">\\n<h3><strong>pr...<article class=\"content-body\">\\n<h3><strong>pr...52762752
\n", "
" ], "text/plain": [ " lien mp3 \\\n", "0 http://prevost.pascal.free.fr/public/podcast/s... \n", "1 http://prevost.pascal.free.fr/public/podcast/s... \n", "2 http://prevost.pascal.free.fr/public/podcast/s... \n", "3 http://prevost.pascal.free.fr/public/podcast/s... \n", "4 http://prevost.pascal.free.fr/public/podcast/s... \n", "\n", " lien description \\\n", "0 http://www.franceinter.fr/em/sur-les-epaules-d... \n", "1 http://www.franceinter.fr/em/sur-les-epaules-d... \n", "2 http://www.franceinter.fr/em/sur-les-epaules-d... \n", "3 http://www.franceinter.fr/em/sur-les-epaules-d... \n", "4 http://www.franceinter.fr/em/sur-les-epaules-d... \n", "\n", " titre date \\\n", "0 La théorie de l'évolution de Charles Darwin 4 Septembre 2010 \n", "1 Le propre de l'homme? 11 Septembre 2010 \n", "2 Nos mémoires 18 Septembre 2010 \n", "3 Nos émotions 25 Septembre 2010 \n", "4 Longévité, jeunesse et vieillissement 2 Octobre 2010 \n", "\n", " description \\\n", "0
\\n

Bib... \n", "1

\\n

pr... \n", "2
\\n

pr... \n", "3
\\n

pr... \n", "4
\\n

pr... \n", "\n", " description_text byte_length \n", "0
\\n

Bib... 51120256 \n", "1

\\n

pr... 52287616 \n", "2
\\n

pr... 51320960 \n", "3
\\n

pr... 54608000 \n", "4
\\n

pr... 52762752 " ] }, "execution_count": 29, "metadata": {}, "output_type": "execute_result" } ], "source": [ "df['byte_length'] = byte_lengths\n", "\n", "df.head()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Ecriture du fichier RSS " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Maintenant que nous avons collecté toutes les émissions, nous pouvons écrire un ficher RSS, comme nous l'avions déjà fait [dans le cas de l'émission Rendez vous avec X](http://flothesof.github.io/podcast-rendez-vous-avec-X-fr.html)." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "L'un des points importants est que nous voulons écrire du HTML dans la description du podcast. Pour ce faire, nous devons utiliser un champ dédié : `` alors que le champ `` ne doit contenir que du texte (source ici : [https://podnews.net/article/how-podcast-show-notes-display](https://podnews.net/article/how-podcast-show-notes-display))." ] }, { "cell_type": "code", "execution_count": 34, "metadata": {}, "outputs": [], "source": [ "import dateparser\n", "import xml.etree.cElementTree as ET\n", "\n", "rss = ET.Element(\"rss\", version=\"2.0\")\n", "channel = ET.SubElement(rss, \"channel\")\n", "title = ET.SubElement(channel, \"title\")\n", "title.text = 'Podcast Sur les épaules de Darwin'\n", "description = ET.SubElement(channel, \"description\")\n", "description.text = \"Podcast inofficiel de l'émission Sur les épaules de Darwin, tiré du site http://clementgrimal.fr/darwin/ et https://www.franceinter.fr/emissions/sur-les-epaules-de-darwin\"\n", "for index, row in df.iterrows():\n", " item = ET.SubElement(channel, \"item\")\n", " item_title = ET.SubElement(item, \"title\")\n", " item_title.text = row['titre']\n", " item_description = ET.SubElement(item, \"description\")\n", " item_description.text = row['description_text'].strip()\n", " item_content_encoded = ET.SubElement(item, \"content:encoded\")\n", " item_content_encoded.text = row['description']\n", " item_pubdate = ET.SubElement(item, \"pubDate\")\n", " item_pubdate.text = dateparser.parse(row.date).date().strftime('%a, %d %b %Y 11:00:00')\n", " item_enclosure = ET.SubElement(item, \"enclosure\", url='{}'.format(row['lien mp3']),\n", " length=row.byte_length,\n", " type=\"audio/mpeg\")\n", "tree = ET.ElementTree(rss)\n", "tree.write(\"files/podcast_Sur_les_epaules_de_Darwin.xml\", encoding='utf-8')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Le lien vers le fichier généré est ici : **https://raw.githubusercontent.com/flothesof/posts/master/files/podcast_Sur_les_epaules_de_Darwin.xml**." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "*Ce billet a été écrit à l'aide d'un notebook Jupyter. Son contenu est sous licence BSD. Une vue statique de ce notebook peut être consultée et téléchargée ici : [20170908_PodcastEpaulesDeDarwin.ipynb](http://nbviewer.ipython.org/urls/raw.github.com/flothesof/posts/master/20170908_PodcastEpaulesDeDarwin.ipynb).*" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.7.6" }, "toc": { "colors": { "hover_highlight": "#DAA520", "navigate_num": "#000000", "navigate_text": "#333333", "running_highlight": "#FF0000", "selected_highlight": "#FFD700", "sidebar_border": "#EEEEEE", "wrapper_background": "#FFFFFF" }, "moveMenuLeft": true, "nav_menu": { "height": "84px", "width": "253px" }, "navigate_menu": true, "number_sections": true, "sideBar": true, "threshold": 4, "toc_cell": false, "toc_section_display": "block", "toc_window_display": false, "widenNotebook": false } }, "nbformat": 4, "nbformat_minor": 2 }