{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Parseurs\n", "\n", "Dans ce notebook nous utiliserons le parseur [lxml](http://lxml.de/) qui est un binding de libxml2 et [Beautiful Soup](https://www.crummy.com/software/BeautifulSoup/) " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Parser de l'html\n", "\n", "Beautiful Soup nous permet de parser simplement du contenu html. Même si le contenu est mal formé, le module bs reconstitue un arbre et offre des fonctions faciles à utiliser pour parcourir l'arbre ou y rechercher des éléments. \n", "Beautiful Soup n'est pas un parseur, il utilise les parseurs et offre une API simplifiée à ses utilisateurs." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Nous travaillerons directement avec du contenu en ligne. Fini les exercices bidons, cette fois nous allons nous confronter à une question essentielle : combien d'accordages *open tuning* Neil Young utilise et comment sont-ils répartis dans son oeuvre ? \n", "On trouve les infos sur les chansons de Neil Young et les accordages sur le fabuleux site [songx.se](http://songx.se/index.php) (le site ayant changé d'interface, nous utiliserons une archive de [wayback archive](http://web.archive.org/))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Avec le module `requests` [que nous avons déjà utilisé](./python-4.ipynb), nous allons pouvoir instancier un objet Beautiful Soup sans trop d'efforts" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import requests\n", "from bs4 import BeautifulSoup\n", "\n", "url = \"http://web.archive.org/web/20180430090903/http://songx.se/index.php\" # le lien vers le site\n", "html = requests.get(url) # on récupère le contenu\n", "soup = BeautifulSoup(html.text, 'lxml') # on crée un objet pour traiter la page" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Voilà nous avons maintenant un objet `soup` de classe [Beautiful Soup](https://www.crummy.com/software/BeautifulSoup/bs4/doc/#beautifulsoup). \n", "La [doc](https://www.crummy.com/software/BeautifulSoup/bs4/doc/) est très claire." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# je cherche l'élement avec le tag 'title'\n", "print(soup.title)\n", "# le tag de l'élément\n", "print(soup.title.name)\n", "# le contenu textuel de l'élément\n", "print(soup.title.string)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Les informations qui nous intéressent sont contenues dans des éléments comme celui-ci (formatté pour la lisibilité) : \n", "\n", "```html\n", "
\n",
"je reponse a ton aimableux lettres
\n",
"que nous a fait plaisir en naprenas
\n",
"que tu et enbonne santes car il
\n",
"anais de maime pour nous
\n",
"
` en utilisant la méthode [findall](http://effbot.org/zone/element.htm#searching-for-subelements). la méthode `findall` renvoie une liste avec tous les éléments correspondant au chemin argument."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"body = root.findall(\"./[...]\", namespaces={'tei':\"http://www.tei-c.org/ns/1.0\"})\n",
"for elem in body: # tout le texte ne s'affichera pas, c'est normal !\n",
" print(elem.text)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Ici on ne récupère que les noeuds text précédant les éléments `
`.\n",
"\n",
"Utilisez la fonction `xpath` pour récupérer tous les noeuds text du corps de la lettre. Vous intégrerez dans votre requête la fonction `text` (vue un peu plus haut) dans votre chemin xpath (vous pouvez _aussi_ fouiller [par ici](https://lxml.de/xpathxslt.html) pour avoir de la documentation supplémentaire)."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"body = root.xpath(\"[...]\", namespaces={'tei':\"http://www.tei-c.org/ns/1.0\"})\n",
"for text in body:\n",
" print(text, end=\"\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Exercice 2**\n",
"\n",
"Écrivez une requête xpath pour récupérer tous les éléments raturés de la lettre de Joséphine."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## avec DOM"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"L'API `ElementTree` est propre à Python, `DOM` ([le site officiel](https://www.w3.org/DOM/) et [des informations en français](https://developer.mozilla.org/fr/docs/Web/API/Document_Object_Model)) est une API indépendante d'un langage de programmation. Il existe des implémentations `DOM` dans la plupart des langages de programmation modernes. "
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"from xml.dom import minidom\n",
"dom = minidom.parse(\"josephine-1-150119.xml\")\n",
"# l'objet Document\n",
"dom"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"title = dom.getElementsByTagNameNS(\"http://www.tei-c.org/ns/1.0\", 'title')[0] # un seul élément 'title' dans le document\n",
"print(title) # title est un objet Element, pour accèder au contenu textuel il faut récupérer le noeud texte\n",
"print(title.lastChild.nodeName)\n",
"print(title.lastChild.nodeValue)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"idem pour la source, sauf qu'on ne peut pas se permettre de rechercher tous les éléments `p`. \n",
"Il faut trouver l'élément `p` fils de `sourceDesc`"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"sourceDesc = dom.getElementsByTagNameNS(\"http://www.tei-c.org/ns/1.0\", 'sourceDesc')[0]\n",
"for node in sourceDesc.childNodes:\n",
" if node.localName == \"p\":\n",
" print(node.lastChild.nodeValue)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Et maintenant le contenu et ses éléments milestones. \n",
"Pour garder la forme vous réécrirez les boucles `for` suivies de `if` en listes en intension."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"body = dom.getElementsByTagNameNS(\"http://www.tei-c.org/ns/1.0\", 'body')[0]\n",
"for node in body.childNodes:\n",
" if node.localName == \"p\" or \"opener\":\n",
" for in_node in node.childNodes:\n",
" if in_node.nodeName == \"#text\":\n",
" print(in_node.nodeValue, end=\"\")"
]
}
],
"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.3"
}
},
"nbformat": 4,
"nbformat_minor": 1
}