{ "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 à l'aide du module `requests`. Si vous ne l'avez pas en magasin, installez le. \n", "Décembre c'est le mois des listes, nous nous attacherons à la liste des 100 meilleures chansons de l'année de NPR la radio publique américaine : https://www.npr.org/2020/12/03/931771524/the-100-best-songs-of-2020-page-1 \n", "Allez y faire un tour." ] }, { "cell_type": "code", "execution_count": 31, "metadata": {}, "outputs": [], "source": [ "import requests\n", "from bs4 import BeautifulSoup\n", "\n", "url = \"https://www.npr.org/2020/12/03/931771524/the-100-best-songs-of-2020-page-1\"\n", "r = requests.get(url)\n", "soup = BeautifulSoup(r.text, 'lxml')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Voilà nous avons maintenant un objet `soup` de classe Beautiful Soup. \n", "La [doc](https://www.crummy.com/software/BeautifulSoup/bs4/doc/) est très claire." ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "NPR Cookie Consent and Choices\n", "title\n", "NPR Cookie Consent and Choices\n" ] } ], "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": [ "Ah mais non. C'est pas ça qu'on veut. Comment faire pour s'épargner cette page de consentement ?" ] }, { "cell_type": "code", "execution_count": 34, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "NPR's 100 Best Songs Of 2020, Ranked : NPR\n", "title\n", "NPR's 100 Best Songs Of 2020, Ranked : NPR\n" ] } ], "source": [ "url = \"https://www.npr.org/2020/12/03/931771524/the-100-best-songs-of-2020-page-1\"\n", "cookies = {\n", " 'choiceVersion':'1',\n", " 'dateOfChoice':'1607425093937g',\n", " 'trackingChoice':'true'\n", "}\n", "r = requests.get(url, cookies=cookies)\n", "soup = BeautifulSoup(r.text, 'lxml')\n", "# 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": [ "Ah voilà, très bien ces petits cookies.\n", "\n", "On cherche à récupérer la liste des 100 chansons : rang, titre, interprète. Puis on les affichera par ordre croissant. \n", "Il faut inspecter le code source et repérer les élements html et les classes utilisées pour le contentu qui nous intéresse. \n", "Exemple avec le premier, enfin le 100ème : BTS. Dynamite. 🎶 Cos I… I… I'm in the stars tonight 🎸 🎶\n", "\n", "```html\n", "
100.
\n", "

BTS

\n", "

\"Dynamite\"

\n", "```" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Exemple pour récupérer les éléments h6 class='edTag'\n", "for item in soup.find_all('h6', attrs={'class':'edTag'}):\n", " print(item.text)\n", "# on peut aussi utiliser la notation suivante\n", "#for item in soup.find_all('h6', class_=\"edTag\"):\n", "# print(item.text)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### ✍️ Exo ✍️\n", "Maintenant à vous de jouer. Il faut parcourir la [doc](https://www.crummy.com/software/BeautifulSoup/bs4/doc/) pour trouver la fonction qui vous permettra de récupérer les deux éléments h3 suivants et seulement ceux-là. \n", "Je vous laisse chercher un peu ![Alt Text](https://media.giphy.com/media/l2SpZkQ0XT1XtKus0/giphy.gif)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Encore un peu ![on cherche](https://media.giphy.com/media/JO9pi3EeHzyBu5YNMK/giphy.gif)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "for item in soup.find_all('h6', attrs={'class':'edTag'}):\n", " rang = item.text\n", " h3s = item.find_next_siblings('h3', attrs={'class':'edTag'}, limit=2)\n", " artist = h3s[0].text\n", " title = h3s[1].text\n", " print(f\"{rang}, {artist}, {title}\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### ✍️ Exo ✍️\n", "\n", "C'est bien mais pas suffisant. Il reste :\n", " 1. nettoyer les rangs, c-a-d supprimmer le point qui traîne à la fin et l'espace des fois.\n", " 2. stocker les infos dans une donnée structurée. Utilisez une classe à vous ou plus simple un `namedtuple`\n", " 3. faire l'opération pour toutes les pages web afin d'avoir le classement de 1 à 100." ] }, { "cell_type": "code", "execution_count": 29, "metadata": {}, "outputs": [], "source": [ "urls = [\n", " \"https://www.npr.org/2020/12/03/931771524/the-100-best-songs-of-2020-page-1\",\n", " \"https://www.npr.org/2020/12/03/934634561/the-100-best-songs-of-2020-page-2?utm_source=page1&utm_campaign=next&utm_term=bottom&utm_medium=internal\",\n", " \"https://www.npr.org/2020/12/03/934634607/the-100-best-songs-of-2020-page-3?utm_source=page2&utm_campaign=next&utm_term=bottom&utm_medium=internal\",\n", " \"https://www.npr.org/2020/12/03/934634855/the-100-best-songs-of-2020-page-4?utm_source=page3&utm_campaign=next&utm_term=bottom&utm_medium=internal\",\n", " \"https://www.npr.org/2020/12/03/934634998/the-100-best-songs-of-2020-page-5?utm_source=page4&utm_campaign=next&utm_term=bottom&utm_medium=internal\"\n", "]\n", "# à vous" ] }, { "cell_type": "code", "execution_count": 46, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "1 Cardi B (feat. Megan Thee Stallion) \"WAP\"\n", "2 Christine and the Queens \"People, I've been sad\"\n", "3 Megan Thee Stallion (feat. Beyoncé) \"Savage Remix\"\n", "4 Mickey Guyton \"Black Like Me\"\n", "5 Bad Bunny (feat. Jowell & Randy and Ñengo Flow) \"Safaera\"\n", "6 Adrianne Lenker \"anything\"\n", "7 Bob Dylan \"Murder Most Foul\"\n", "8 Thundercat \"Dragonball Durag\" \n", "9 Ana Tijoux \"Antifa Dance\"\n", "10 Adia Victoria \"South Gotta Change\"\n", "11 Sam Hunt \"Hard to Forget\"\n", "12 Jazmine Sullivan \"Lost One\"\n", "13 Caylee Hammack \"Small Town Hypocrite\"\n", "14 J Hus (feat. Koffee) \"Repeat\"\n", "15 Perfume Genius \"On The Floor\"\n", "16 Chris Stapleton \"Starting Over\"\n", "17 Phoebe Bridgers \"I Know The End\"\n", "18 Víkingur Ólafsson \"The Arts and the Hours\"\n", "19 Joshua Redman, Brad Mehldau, Christian McBride & Brian Blade \"Right Back Round Again\"\n", "20 SZA (feat. Ty Dolla $ign) \"Hit Different\" \n", "21 Lil Baby \"The Bigger Picture\"\n", "22 Taylor Swift \"invisible string\"\n", "23 Childish Gambino \"47.48\"\n", "24 Fiona Apple \"I Want You To Love Me\"\n", "25 Goodie Mob \"4 My Ppl\"\n", "26 The Chicks \"Gaslighter\"\n", "27 Noname \"Song 33\"\n", "28 Stephanie Lambring \"Joy of Jesus\"\n", "29 Lianne La Havas \"Can't Fight\"\n", "30 William Prince \"Gospel First Nation\"\n", "31 Inbal Segev & London Philharmonic Orchestra \"DANCE, I. when you're broken open\"\n", "32 Angelica Garcia \"Agua de Rosa\"\n", "33 SAULT \"Wildfires\"\n", "34 Jason Isbell and The 400 Unit \"Letting You Go\"\n", "35 Kllo \"Still Here\"\n", "36 Immanuel Wilkins \"Ferguson - An American Tradition\"\n", "37 Sun-El Musician (feat. Azana) \"Uhuru\"\n", "38 Soccer Mommy \"circle the drain\"\n", "39 Dua Lipa \"Break My Heart\"\n", "40 Lil Baby (feat. 42 Dugg) \"We Paid\"\n", "41 Tyler Childers \"Long Violent History\"\n", "42 Clarice Jensen \"Holy Mother\"\n", "43 RMR \"Rascal\"\n", "44 Chucky73 & Fetti031 \"Dili\"\n", "45 Lakecia Benjamin (feat. Marcus Strickland & Brandee Younger) \"Going Home\"\n", "46 Deep Sea Diver \"Stop Pretending\"\n", "47 Jayda G \"Both Of Us\"\n", "48 Helado Negro (feat. Xenia Rubinos) \"I Fell In Love\"\n", "49 Arlo Parks \"Eugene\"\n", "50 Romy \"Lifetime\"\n", "51 Emma Ruth Rundle & Thou \"Ancestral Recall\"\n", "52 Bad Moves \"Local Radio\"\n", "53 Jeff Parker \"Go Away\"\n", "54 Freddie Gibbs / The Alchemist (feat. Rick Ross) \"Scottie Beam\"\n", "55 Shemekia Copeland \"Walk Until I Ride\"\n", "56 Steady Holiday \"Living Life\"\n", "57 Disclosure (feat. slowthai & Aminé) \"My High\"\n", "58 Moses Boyd (feat. Joe Armon-Jones) \"2 Far Gone\"\n", "59 Fontaines D.C. \"A Hero's Death\"\n", "60 City Girls \"Jobs\"\n", "61 Sam Sweeney \"Steppy Downs Road\"\n", "62 Moor Jewelry \"Look Alive\"\n", "63 Lido Pimienta \"Eso Que Tu Haces\"\n", "64 Sarah Jarosz \"Johnny\"\n", "65 Rolf Lislevand \"Tombeau pour Mesdemoiselles De Visée\"\n", "66 Meet Me @ The Altar \"Garden\"\n", "67 La Doña \"Quién Me La Paga\"\n", "68 Patrice Roberts \"Tender\"\n", "69 Joy Oladokun \"i see america\"\n", "70 Aly & AJ \"Joan of Arc on the Dance Floor\"\n", "71 Ruston Kelly \"Radio Cloud\"\n", "72 Zara McFarlane \"Everything Is Connected\"\n", "73 Ashley McBryde \"Hang In There Girl\"\n", "74 Xavier Omär \"SURF\"\n", "75 Mireya Ramos \"Angelitos Negros\"\n", "76 Jessie Ware \"Save A Kiss\"\n", "77 Hum \"Waves\"\n", "78 Terrace Martin, Robert Glasper, 9th Wonder & Kamasi Washington (feat. Phoelix) \"Freeze Tag\"\n", "79 BeatKing (feat. Queendom Come) \"Then Leave\"\n", "80 Popcaan (feat. Drake & PARTYNEXTDOOR) \"TWIST & TURN\"\n", "81 Arlo McKinley \"Die Midwestern\"\n", "82 Sweeping Promises \"Hunger For A Way Out\"\n", "83 Ultraísta \"Tin King\"\n", "84 Ashnikko (feat. Grimes) \"Cry\"\n", "85 Rita Indiana (feat. Kiko El Crazy) \"Mandinga Times\"\n", "86 Moneybagg Yo \"Said Sum\"\n", "87 Giveon \"Still Your Best\"\n", "88 Roomful Of Teeth \"Just Constellations No. 1, The Opening Constellation (Summer)\"\n", "89 Swamp Dogg \"Billy\"\n", "90 The 1975 \"If You're Too Shy (Let Me Know)\"\n", "91 Hayley Williams \"Simmer\"\n", "92 Yumi Zouma \"Cool For A Second\"\n", "93 Leon Bridges (feat. Terrace Martin) \"Sweeter\"\n", "94 Breland (feat. Sam Hunt) \"My Truck (Remix)\"\n", "95 Tiwa Savage \"Dangerous Love (DJ Tunez & D3an Remix)\"\n", "96 Chicano Batman \"Color my life\"\n", "97 Busta Rhymes (feat. Kendrick Lamar) \"Look Over Your Shoulder\"\n", "98 Ariana Grande \"pov\"\n", "99 Sturgill Simpson \"Living The Dream\"\n", "100 BTS \"Dynamite\"\n" ] } ], "source": [ "from collections import namedtuple\n", "\n", "Song = namedtuple('Song', ['rang', 'artiste', 'titre'])\n", "songs = []\n", "cookies = {\n", " 'choiceVersion':'1',\n", " 'dateOfChoice':'1607425093937g',\n", " 'trackingChoice':'true'\n", "}\n", "for url in urls:\n", " r = requests.get(url, cookies=cookies)\n", " soup = BeautifulSoup(r.text, 'lxml')\n", " for item in soup.find_all('h6', attrs={'class':'edTag'}):\n", " rang = item.text.rstrip(\". \").lstrip()\n", " h3s = item.find_next_siblings('h3', attrs={'class':'edTag'}, limit=2)\n", " artiste = h3s[0].text\n", " titre = h3s[1].text\n", " songs.append(Song(rang, artiste, titre))\n", "\n", "for song in sorted(songs, key=lambda x:int(x.rang)):\n", " print(song.rang, song.artiste, song.titre)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Parser de l'xml" ] }, { "cell_type": "markdown", "metadata": { "collapsed": true }, "source": [ "Nous allons travailler sur un fichier au format TEI extrait du corpus *Corpus 14* \n", "PRAXILING - UMR 5267 (PRAXILING) (2014). Corpus 14 [Corpus]. ORTOLANG (Open Resources and TOols for LANGuage) - www.ortolang.fr, https://hdl.handle.net/11403/corpus14/v1. " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Le fichier se nomme ``josephine-1-150119.xml``. Il s'agit d'une lettre d'une femme de soldat à son époux. \n", "Nous allons extraire du fichier TEI les informations suivantes : \n", "- titre (``/TEI/teiHeader/fileDesc/titleStmt/title``)\n", "- source (``/TEI/teiHeader/fileDesc/sourceDesc/p``)\n", "- contenu de la lettre (``/TEI/text/body``)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Avec lxml" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Pourquoi `lxml` et pas `xml.etree.ElementTree` ? Parce que : [1](http://lxml.de/intro.html) et surtout [2](http://lxml.de/performance.html) \n", "La bonne nouvelle c'est que votre code sera aussi compatible avec `xml.etree.ElementTree` ou `xml.etree.cElementTree` parce que xml utilise l'API ElementTree. Sauf pour la méthode `xpath` qui est propre à `libxml`." ] }, { "cell_type": "code", "execution_count": 57, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\n", "{http://www.tei-c.org/ns/1.0}teiHeader\n", "\n", "{http://www.tei-c.org/ns/1.0}facsimile\n", "{http://www.tei-c.org/ns/1.0}text\n" ] } ], "source": [ "from lxml import etree\n", "tree = etree.parse('data/josephine-1-150119.xml')\n", "root = tree.getroot()\n", "\n", "# Parcours des enfants de la racine (commentaires et éléments)\n", "for child in root:\n", " print(child.tag)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Le fichier utilise l'espace de nom TEI : ````, nous devrons l'indiquer dans nos instructions de recherche. \n", "Voyons ça pour le titre (``/TEI/teiHeader/fileDesc/titleStmt/title``)" ] }, { "cell_type": "code", "execution_count": 58, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Tag : {http://www.tei-c.org/ns/1.0}title\n", "Texte : Joséphine Pouchet à son époux le 19-01-1915 depuis Baillargues\n" ] } ], "source": [ "# la méthode find renvoie le premier élément qui correspond au chemin argument (ElementPath et non Xpath)\n", "title = root.find(\"./tei:teiHeader/tei:fileDesc/tei:titleStmt/tei:title\", namespaces={'tei':\"http://www.tei-c.org/ns/1.0\"})\n", "print(\"Tag : {}\".format(title.tag))\n", "print(\"Texte : {}\".format(title.text))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Même traitement pour la source :" ] }, { "cell_type": "code", "execution_count": 59, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Tag : {http://www.tei-c.org/ns/1.0}p\n", "Texte : Correspondance de Joséphine Pouchet, numérisée par les Archives Départementales de l'Hérault.\n" ] } ], "source": [ "source = root.find(\"./tei:teiHeader/tei:fileDesc/tei:sourceDesc/tei:p\", namespaces={'tei':\"http://www.tei-c.org/ns/1.0\"})\n", "print(\"Tag : {}\".format(source.tag))\n", "print(\"Texte : {}\".format(source.text))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "lxml a aussi une méthode ``xpath`` qui permet d'utiliser directement des expressions xpath (sans oublier les espace de noms pour notre fichier) :" ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\n", "Correspondance de Joséphine Pouchet, numérisée par les Archives Départementales de l'Hérault.\n", "Correspondance de Joséphine Pouchet, numérisée par les Archives Départementales de l'Hérault.\n" ] } ], "source": [ "source = root.xpath(\"/tei:TEI/tei:teiHeader/tei:fileDesc/tei:sourceDesc/tei:p\", namespaces={'tei':'http://www.tei-c.org/ns/1.0'})\n", "print(type(source)) #xpath retourne une liste\n", "print(source[0].text)\n", "#ou bien\n", "source = root.xpath(\"/tei:TEI/tei:teiHeader/tei:fileDesc/tei:sourceDesc/tei:p/text()\", namespaces={'tei':'http://www.tei-c.org/ns/1.0'})\n", "print(source[0])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Pour le contenu il faut ruser. La difficulté ici tient à l'utilisation d'élements `` de type 'milestones' pour noter les retours à la ligne : \n", "```xml\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", "

\n", "```" ] }, { "cell_type": "code", "execution_count": 116, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\n", "cher Laurent\n", "\n", "je reponse a ton aimableux lettres\n", "\n", "cher Laurent je repons a la cartes\n" ] } ], "source": [ "# la méthode findall renvoie une liste avec tous les éléments correspondant au chemin argument\n", "body = root.findall(\"./tei:text/tei:body/tei:p\", namespaces={'tei':\"http://www.tei-c.org/ns/1.0\"})\n", "for elem in body:\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", "Une requête `xpath` va nous permettre de récupérer tous les noeuds text" ] }, { "cell_type": "code", "execution_count": 12, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\n", "\n", "Baillargues Le 19 janvier 1915\n", "\n", "\n", "cher Laurent\n", "\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", "\n", "\n", "cher Laurent je repons a la cartes\n", "de ma mère quelles et venue au\n", "jourdhui pour de faire partire\n", "partir un paquet quil aillae\n", "les chosette sausice chocolas une\n", "paire de chosette pour Louis\n", "je pense que vous magerè ensenbleus\n", "tu feras repons a la maison te\n", "suite que tu rese vras le paquet\n", "je te dirais que ten le midi il\n", "fait frois il fait du vent glasais\n", "et toi au pas de calais tu nous\n", "dit quil pleus mai tu nous parles\n", "pas si tu a ases pour te garendir\n", "du froit cil te maque quelles chose\n", "\n", "tu nas que ledire quon de len verras\n", "verras tu nous dit que charles ta\n", "Ecrie et ta soeux et ta dit que\n", "je lui et envoiez la photot\n", "plurien a te dire pour le moment\n", "que de ten voiez une grose\n", "carriese de tous et boutounase\n", "de ton petit enge \n", "adorè Albert encorre une foi\n", "te plui Milles baisées te tous\n", "ta fenme pour la vie\n", "Josephine Pouchet\n", "bien le bonjour de la familles\n", "et casimir et de tous le voisin\n", "et amie et de marie la perdigailles\n", "\n" ] } ], "source": [ "body = root.xpath(\"//tei:text/tei:body//text()\", namespaces={'tei':\"http://www.tei-c.org/ns/1.0\"})\n", "for text in body:\n", " print(text, end=\"\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## avec DOM" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "L'API `ElementTree` est propre à Python, `DOM` 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": 60, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "" ] }, "execution_count": 60, "metadata": {}, "output_type": "execute_result" } ], "source": [ "from xml.dom import minidom\n", "dom = minidom.parse(\"data/josephine-1-150119.xml\")\n", "# l'objet Document\n", "dom" ] }, { "cell_type": "code", "execution_count": 61, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\n", "#text\n", "Joséphine Pouchet à son époux le 19-01-1915 depuis Baillargues\n" ] } ], "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": 62, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Correspondance de Joséphine Pouchet, numérisée par les Archives Départementales de l'Hérault.\n" ] } ], "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": { "collapsed": true }, "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 compréhension." ] }, { "cell_type": "code", "execution_count": 63, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\n", "Baillargues Le 19 janvier 1915\n", "\n", "cher Laurent\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", "\n", "cher Laurent je repons a la cartes\n", "de ma mère quelles et venue au\n", "jourdhui pour de faire \n", "partir un paquet quil \n", "les chosette sausice chocolas une\n", "paire de chosette pour Louis\n", "je pense que vous magerè ensenbleus\n", "tu feras repons a la maison te\n", "suite que tu rese vras le paquet\n", "je te dirais que ten le midi il\n", "fait frois il fait du vent glasais\n", "et toi au pas de calais tu nous\n", "dit quil pleus mai tu nous parles\n", "pas si tu a ases pour te garendir\n", "du froit cil te maque quelles chose\n", "\n", "tu nas que ledire quon de len \n", "verras tu nous dit que charles ta\n", "Ecrie et ta soeux et ta dit que\n", "je lui et envoiez la photot\n", "plurien a te dire pour le moment\n", "que de ten voiez une grose\n", "carriese de tous et boutounase\n", "de ton petit enge \n", "adorè Albert encorre une foi\n", "te plui Milles baisées te tous\n", "ta fenme pour la vie\n", "Josephine Pouchet\n", "bien le bonjour de la familles\n", "et casimir et de tous le voisin\n", "et amie et de marie la perdigailles\n" ] } ], "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=\"\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Avec lxml et Beautiful Soup" ] }, { "cell_type": "code", "execution_count": 49, "metadata": {}, "outputs": [], "source": [ "from bs4 import BeautifulSoup\n", "\n", "with open(\"data/josephine-1-150119.xml\") as fp:\n", " soup = BeautifulSoup(fp, 'lxml')" ] }, { "cell_type": "code", "execution_count": 50, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "'Joséphine Pouchet à son époux le 19-01-1915 depuis Baillargues'" ] }, "execution_count": 50, "metadata": {}, "output_type": "execute_result" } ], "source": [ "soup.title.text" ] }, { "cell_type": "code", "execution_count": 51, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "\"Correspondance de Joséphine Pouchet, numérisée par les Archives Départementales de l'Hérault.\"" ] }, "execution_count": 51, "metadata": {}, "output_type": "execute_result" } ], "source": [ "soup.sourcedesc.p.text" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Pour le contenu de la lettre il y a la merveilleuse fonction `get_text()`" ] }, { "cell_type": "code", "execution_count": 52, "metadata": {}, "outputs": [], "source": [ "soup.get_text?" ] }, { "cell_type": "code", "execution_count": 92, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\n", "\n", "\n", "\n", "Baillargues Le 19 janvier 1915\n", "\n", "\n", "cher Laurent\n", "\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", "\n", "\n", "cher Laurent je repons a la cartes\n", "de ma mère quelles et venue au\n", "jourdhui pour de faire partire\n", "partir un paquet quil aillae\n", "les chosette sausice chocolas une\n", "paire de chosette pour Louis\n", "je pense que vous magerè ensenbleus\n", "tu feras repons a la maison te\n", "suite que tu rese vras le paquet\n", "je te dirais que ten le midi il\n", "fait frois il fait du vent glasais\n", "et toi au pas de calais tu nous\n", "dit quil pleus mai tu nous parles\n", "pas si tu a ases pour te garendir\n", "du froit cil te maque quelles chose\n", "\n", "tu nas que ledire quon de len verras\n", "verras tu nous dit que charles ta\n", "Ecrie et ta soeux et ta dit que\n", "je lui et envoiez la photot\n", "plurien a te dire pour le moment\n", "que de ten voiez une grose\n", "carriese de tous et boutounase\n", "de ton petit enge \n", "adorè Albert encorre une foi\n", "te plui Milles baisées te tous\n", "ta fenme pour la vie\n", "Josephine Pouchet\n", "bien le bonjour de la familles\n", "et casimir et de tous le voisin\n", "et amie et de marie la perdigailles\n", "\n", "\n", "\n" ] } ], "source": [ "text = soup.find('text')\n", "print(text.getText())" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "lxml est rapide, Beautiful Soup simple à utiliser. Le combo diablement efficace.\n", "\n", "Il y a un autre module super pour le web que nous ne verrons pas ici mais que je me dois de vous indiquer : https://selenium-python.readthedocs.io/ \n", "Selenium va vous permettre d'automatiser des actions sur un navigateur. Je vous conseille d'essayer, c'est assez plaisant de voir votre navigateur piloté par un script." ] } ], "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.4" } }, "nbformat": 4, "nbformat_minor": 1 }