Partage
  • Partager sur Facebook
  • Partager sur Twitter

Parser une page web

    5 août 2011 à 10:48:26

    Bonjour.

    Je souhaiterais faire un petit script assez simple en python (voire plusieurs si je m'en sors bien) qui aura simplement pour but d'aller récupérer des informations sur une page web.

    Sans m'y connaître beaucoup, je sais qu'une page web est composée de html essentiellement, et que ce langage propose des balises, qui identifient des champs. Je cherche donc un module python (simple, pas forcément performant) qui me permettra de parser une page html sans que je me prenne la tête.

    Je sais qu'il existe des modules du genre HTMLparser, beautifulSoup et autres, mais je voudrais votre avis: connaissez vous un module simple, qui fasse bien son travail?
    • Partager sur Facebook
    • Partager sur Twitter
    http://maymayhem.fr/
    Anonyme
      5 août 2011 à 11:12:04

      Citation

      Je cherche donc un module python (simple, pas forcément performant) qui me permettra de parser une page html sans que je me prenne la tête.



      :lol:

      Citation

      Je sais qu'il existe des modules du genre HTMLparser, beautifulSoup et autres, mais je voudrais votre avis: connaissez vous un module simple, qui fasse bien son travail?



      Ne le sont-ils pas? Sinon oui il y a autre chose mais c'est plus complexe et ça dépend de ce que tu cherches, les expressions régulières.

      • Partager sur Facebook
      • Partager sur Twitter
        5 août 2011 à 11:27:19

        :)

        Le principe c'est que justement, je ne veux pas me farcir d'expression régulière.
        Je demandais juste si vous utilisiez quelque chose de particulier pour faire ça. Un avis informé est toujours bon à prendre.
        • Partager sur Facebook
        • Partager sur Twitter
        http://maymayhem.fr/
          5 août 2011 à 15:44:52

          lxml est bien, beautifulsoup aussi.
          • Partager sur Facebook
          • Partager sur Twitter

          Blond, bouclé, toujours le sourire aux lèvres...

            5 août 2011 à 16:42:46

            Oui, j'avais noté beautifulSoup, mais il n'est pas bien adapté pour python3.

            J'ai essayé lxml. Bon alors ne vous moquez pas, mais je ne sais pas bien comment je dois procéder. Les infos que je trouve sont en anglais et pas appropriées la plupart du temps. Je vous donne mon code comme point de départ, qui récupère la page d'accueil d'allocine:

            #!/usr/bin/python
            
            
            
            import urllib.request
            from lxml import etree
            
            url = urllib.request.urlopen('http://www.allocine.fr/') 
            page = str(url.read())
            url.close()
            
            
            #page_accueil = open("page_accueil.txt","w")
            #page_accueil.write(page)
            #page_accueil.close()
            
            print(page)
            


            Voilà. Ça ça me donne un bon gros pavé de texte bien illisible. Je voudrais commencer souple:

            -Dans un premier temps, afficher ça avec l'indention qu'il faut.
            -Dans un deuxième temps, pouvoir extraire les infos d'un champ en particulier. Je ne sais pas, les titres des films qui sont dans la section "bandes annonces" par exemple.

            J'ai déja essayé les exemples de la doc de lxml, mais ça ne donne rien de bon:

            >>> broken_html = "page"
            
            >>> parser = etree.HTMLParser()
            >>> tree   = etree.parse(StringIO(page), parser)
            
            >>> result = etree.tostring(tree.getroot(),
            ...                         pretty_print=True, method="html")
            >>> print(result)
            <html>
              <head>
                <title>test</title>
              </head>
              <body>
                <h1>page title</h1>
              </body>
            </html>
            


            Sauf que ce code me renvoie: NameError: name 'StringIO' is not defined



            Est ce que vous pourriez m'éclairer un peu s'il vous plaît? M'apporter des conseils ou des indications sur la marche à suivre?
            • Partager sur Facebook
            • Partager sur Twitter
            http://maymayhem.fr/
            Anonyme
              5 août 2011 à 16:45:50

              Citation

              NameError: name 'StringIO' is not defined



              StringIO est un module, comme lxml ou urllib.request
              • Partager sur Facebook
              • Partager sur Twitter
                5 août 2011 à 17:00:57

                Bien vu. Pour python3, il fait partie du module io.

                Ce qui donne:

                #!/usr/bin/python
                
                
                
                import urllib.request
                from lxml import etree
                from io import StringIO
                
                
                url = urllib.request.urlopen('http://www.allocine.fr/') 
                page = str(url.read())
                
                parser = etree.HTMLParser()
                tree   = etree.parse(StringIO(page), parser)
                result = etree.tostring(tree.getroot(), pretty_print=True, method="html")
                
                print(result)
                
                
                url.close()
                
                
                #page_accueil = open("page_accueil.txt","w")
                #page_accueil.write(page)
                #page_accueil.close()
                
                #print(page)
                


                Donc ok, là ça marche sans erreur. Ça fait exactement comme mon premier code, et je n'ai toujours pas d'indention. Mais c'est déjà un bon début.
                • Partager sur Facebook
                • Partager sur Twitter
                http://maymayhem.fr/
                Anonyme
                  5 août 2011 à 22:25:13

                  Citation

                  et je n'ai toujours pas d'indention



                  En version 2.7

                  import urllib
                  from BeautifulSoup import BeautifulSoup
                  
                  url = # ton url
                  data = urllib.urlopen(url).read()
                  soup = BeautifulSoup(data)
                  print soup.prettify()
                  
                  • Partager sur Facebook
                  • Partager sur Twitter
                    6 août 2011 à 8:45:31

                    Je voudrais le faire avec python3. Je n'avais pas vu, mais beautifulSoup n'est pas compatible. C'est bien dommage d'ailleurs.
                    • Partager sur Facebook
                    • Partager sur Twitter
                    http://maymayhem.fr/
                      6 août 2011 à 12:11:47

                      Y'a-t'il une bonne raison pour laquelle tu veuilles absolument travailler avec une version de Python qui n'est pas mature ?
                      • Partager sur Facebook
                      • Partager sur Twitter
                      Zeste de Savoir, le site qui en a dans le citron !
                      Anonyme
                        6 août 2011 à 13:15:23

                        Tu risques de ne pas avoir que ce module non compatible avec python3, je te conseille la version 2.7, maintenant si tu veux absolument la version 3, rien ne t'empêche de trouver son équivalent sur le net ;)

                        • Partager sur Facebook
                        • Partager sur Twitter
                          7 août 2011 à 20:55:20

                          La seule raison qui me pousse à utiliser python 3, bin c'est que j'ai appris le python avec le tuto du sdz, qui présente la version 3.

                          En plus de ça, sur Arch, tous les modules et bibliothèques s'installent dans le dossier de python 3 (et c'est cette version l'interpréteur pas défaut). Alors c'est pas grand chose, mais ça m'evite de devoir bien indiquer le chemin de python, et d'installer les bibliothèques ailleurs.

                          Et puis je ne sais pas quelles différences il y a entre la 2.7 et la 3.2. Ça a vraiment changé?
                          • Partager sur Facebook
                          • Partager sur Twitter
                          http://maymayhem.fr/
                            7 août 2011 à 20:59:21

                            Sous arch, tu peux installer nativement les paquets pour python 2 et python 3 (tente un yaourt -Ss python2, tu verras), et tu n'as pas à spécifier les chemins. Il suffit juste de taper python2 tonscript.py (ou bien de spécifier un shebang du style #!/usr/bin/env python2)…

                            C'est probablement la distro sur laquelle il est le plus facile de travailler avec les 2 versions de Python.

                            En ce qui concerne les différences, oui, plusieurs trucs ont changé, mais moyennant la doc sous les yeux, ça n'est pas non plus un handicap énorme (on s'habitue vite aux différences).
                            • Partager sur Facebook
                            • Partager sur Twitter
                            Zeste de Savoir, le site qui en a dans le citron !
                              8 août 2011 à 13:48:58

                              Ok, j'avoue, ça marche bien avec beautifulSoup et python 2.7.

                              En plus j'avais pas fait attention, mais dans les dépôts de Arch il y a python2-pip et python-pip, ce qui fait que je peux installer des modules spécifiquement pour une version sans me prendre la tête.

                              Mais c'est quand même bien dommage que je ne puisse pas faire ça avec python3. On m'avait conseillé lxml qui marche pour python3 normalement.

                              Vous vous préférez utiliser python2 à ce que j'ai compris? Mais est ce que quelqu'un pourrait se pencher sur mon code ci-dessus et me dire s'il y a un truc que je fais mal?
                              • Partager sur Facebook
                              • Partager sur Twitter
                              http://maymayhem.fr/
                              Anonyme
                                8 août 2011 à 14:22:04

                                En regardant la doc je dirais un truc comme ça, attention code non testé

                                import urllib.request
                                from lxml import etree
                                from StringIO import StringIO
                                
                                data = urllib.request.urlopen(url).read() # url est ton url
                                parser = etree.HTMLParser()
                                tree   = etree.parse(StringIO(data), parser)
                                result = etree.tostring(tree.getroot(),pretty_print=True,
                                                        method="html")
                                print(result)
                                


                                Il y a peut-être des erreurs mais c'est cohérent, ça se tente
                                • Partager sur Facebook
                                • Partager sur Twitter
                                  8 août 2011 à 17:23:06

                                  @fred1599, merci mais est ce que tu pourrais faire un peu attention à ce que j'écris quand même?

                                  J'ai posté plus ou moins le même code il y a 3 messages, donc la doc tu peux supposer que je l'ai lue.
                                  Mon seul problème c'est que:

                                  tree   = etree.parse(StringIO(data), parser)
                                  


                                  donne cette erreur:
                                  tree = etree.parse(StringIO(data), parser)
                                  TypeError: initial_value must be str or None, not bytes

                                  Voilà pourquoi dans mon code je faisais:

                                  page = str(url.read())
                                  [...]
                                  tree   = etree.parse(StringIO(page), parser)
                                  


                                  Ça marche sans erreur, mais ça ne renvoie pas la même chose que BeautifulSoup, à savoir la page avec l'indention. J'ai bien le code HTML, mais non formaté, en vrac.
                                  • Partager sur Facebook
                                  • Partager sur Twitter
                                  http://maymayhem.fr/
                                  Anonyme
                                    8 août 2011 à 17:27:21

                                    bytes.decode(encoding)

                                    with urlopen(url) as file:
                                        pagedata = file.read()
                                    page = pagedata.decode(encoding)
                                    
                                    • Partager sur Facebook
                                    • Partager sur Twitter
                                    Anonyme
                                      8 août 2011 à 17:32:14

                                      Hmmm dommage parce-que en version 2.7, j'ai testé et ça fonctionne.

                                      Oui en regardant plus haut en effet le code est identique.

                                      rajoute l'encoding UTF-8

                                      result = etree.tostring(tree.getroot(),pretty_print=True,
                                                              method="html", encoding='UTF-8')
                                      
                                      • Partager sur Facebook
                                      • Partager sur Twitter
                                        8 août 2011 à 17:48:21

                                        Voilà ce que j'ai essayé, toujours sans succès :(

                                        #!/usr/bin/python
                                        
                                        
                                        import urllib.request
                                        from lxml import etree
                                        from io import StringIO
                                        
                                        with urllib.request.urlopen('http://www.allocine.fr/') as file:
                                        	pagedata = file.read()
                                        page = pagedata.decode('UTF-8')
                                        
                                        parser = etree.HTMLParser()
                                        tree   = etree.parse(StringIO(page), parser)
                                        result = etree.tostring(tree.getroot(), pretty_print=True, method="html", encoding='UTF-8')
                                        print(result)
                                        


                                        Pas d'erreur, juste un gros pavé de texte désordonné...
                                        C'ets bien comme ça que s'utilise la fonction decode() non? J'ai regardé le code source de la page, et j'ai trouvé une balise tout en haut avec text/html; charset=UTF-8 à l'intérieur.
                                        • Partager sur Facebook
                                        • Partager sur Twitter
                                        http://maymayhem.fr/
                                        Anonyme
                                          8 août 2011 à 17:54:12

                                          Oui, c'est comme ça...

                                          Pour être sûr du charset utilisé dans la page :
                                          with urlopen(url) as file:
                                              encoding = file.headers.getparam('charset')
                                              page = file.read().decode(encoding)
                                          
                                          • Partager sur Facebook
                                          • Partager sur Twitter
                                            8 août 2011 à 18:09:18

                                            Traceback (most recent call last):
                                              File "/home/djipey/Desktop/python/allocine/allocine.py", line 22, in <module>
                                                encoding = file.headers.getparam('charset')
                                            AttributeError: 'HTTPMessage' object has no attribute 'getparam'
                                            
                                            le shell a retourné 1
                                            
                                            Appuyez sur ENTRÉE ou tapez une commande pour continuer


                                            Il me manque encore un module?
                                            • Partager sur Facebook
                                            • Partager sur Twitter
                                            http://maymayhem.fr/
                                            Anonyme
                                              8 août 2011 à 18:34:01

                                              Non c'est moi qui me suis trompé... :honte:

                                              charset = file.info().get_charset()
                                              if not charset:
                                                  charset = "ascii"
                                              
                                              • Partager sur Facebook
                                              • Partager sur Twitter
                                                8 août 2011 à 18:43:30

                                                Comme ça donc?:

                                                with urllib.request.urlopen('http://www.allocine.fr/') as file:
                                                	charset = file.info().get_charset()
                                                	if not charset:
                                                		    charset = "ascii"
                                                	pagedata = file.read()
                                                page = pagedata.decode('ascii')
                                                


                                                Mais ça ne va pas poser problème pour les caractères spéciaux? (Ceux qui dépassent 255 en ascii)
                                                • Partager sur Facebook
                                                • Partager sur Twitter
                                                http://maymayhem.fr/
                                                  12 août 2011 à 15:12:42

                                                  Bon, j'ai fini par suivre vos conseils, j'ai préféré faire ce script avec python 2.7. Plus de modules compatibles, plus facile.

                                                  Je voudrais vous montrer un bout de code, avec les modules beautifulSoup et Requests, qui me simplifient beaucoup la tâche. Pouvez vous me dire si ma manière de récupérer les informations importantes est bonne, et ce qu'il y aurait à améliorer?

                                                  Pour l'instant le script charge une page que j'ai choisie (harry potter). Je n'ai pas encore fait de gestion d'erreurs, et c'est du code un peu dégueulasse. Je me suis basé sur le code source de la page, et j'ai cherché des éléments qui me permettent de dégager les informations, comme des chaînes de caractères spécifiques, ou certains tags.

                                                  Qu'en pensez vous?

                                                  #!/usr/bin/python
                                                  # -*-coding:Utf-8 -* 
                                                  
                                                  
                                                  
                                                  import requests
                                                  from BeautifulSoup import BeautifulSoup
                                                  import re
                                                  
                                                  
                                                  req = requests.get('http://www.allocine.fr/film/fichefilm_gen_cfilm=134925.html')
                                                  page = req.content
                                                  
                                                  soup = BeautifulSoup(page)
                                                  #print soup.prettify()
                                                  
                                                  
                                                  # Renvoie le titre du film
                                                  titre=soup.findAll('title')
                                                  print(titre[0].renderContents())
                                                  
                                                  # Renvoie le synopsis
                                                  #soup.findAll('p', limit=1)
                                                  synopsis=soup.findAll(property='v:summary')
                                                  print(synopsis[0].renderContents())
                                                  
                                                  # Renvoie le genre
                                                  genre=soup.findAll(href=re.compile(".?/film/tous/genre.?"))
                                                  print(genre[0].renderContents())
                                                  
                                                  # Renvoie la durée
                                                  duree=soup.findAll(text=re.compile(".?Dur.?e.?"))
                                                  duree=str(duree[0])
                                                  print(duree[10:18])
                                                  
                                                  # Renvoie l'année de production
                                                  annee=soup.findAll(href=re.compile(".?/film/tous/decennie.?"))
                                                  print(annee[0].renderContents())
                                                  
                                                  # Renvoie la compagnie qui produit le film
                                                  distributeur=soup.findAll(href=re.compile(".?/societe/fichesociete.?"))
                                                  print(distributeur[0].renderContents())
                                                  
                                                  # Renvoie les notes du film
                                                  notes=soup.findAll(attrs={"class" : "moreinfo"})
                                                  print(notes[0].renderContents())
                                                  print(notes[1].renderContents())
                                                  print(notes[2].renderContents())
                                                  
                                                  # Renvoie le réalisateur
                                                  realisateur=soup.findAll(rel='v:directedBy')
                                                  print(realisateur[0].renderContents())
                                                  
                                                  # Renvoie le titre original
                                                  titre_original=soup.findAll(attrs={"class" : "purehtml"})
                                                  titre_original=titre_original[0].renderContents()
                                                  titre_original=titre_original[4:]
                                                  titre_original=titre_original[:len(titre_original)-5]
                                                  print(titre_original)
                                                  
                                                  
                                                  # Renvoie le casting
                                                  acteurs=soup.findAll(rel='v:starring')
                                                  for case in acteurs:
                                                  	print(case.renderContents())
                                                  
                                                  • Partager sur Facebook
                                                  • Partager sur Twitter
                                                  http://maymayhem.fr/
                                                  Anonyme
                                                    12 août 2011 à 15:39:01

                                                    En version 2.7 ce n'est pas une fonction print
                                                    • Partager sur Facebook
                                                    • Partager sur Twitter
                                                      12 août 2011 à 15:48:29

                                                      Je sais. Mais là en l’occurrence, je ne vois pas bien ce que ça change. Je devrais écrire print case.renderContents()?
                                                      • Partager sur Facebook
                                                      • Partager sur Twitter
                                                      http://maymayhem.fr/
                                                      Anonyme
                                                        12 août 2011 à 18:12:36

                                                        Citation

                                                        Je sais. Mais là en l’occurrence, je ne vois pas bien ce que ça change. Je devrais écrire print case.renderContents()?



                                                        Simplement que tu vires tes parenthèses après tes print ;)
                                                        • Partager sur Facebook
                                                        • Partager sur Twitter
                                                          12 août 2011 à 18:19:28

                                                          C'est fait. Et pour ce qui est du reste?
                                                          • Partager sur Facebook
                                                          • Partager sur Twitter
                                                          http://maymayhem.fr/
                                                          Anonyme
                                                            12 août 2011 à 18:42:43

                                                            tu es au moins prof...
                                                            • Partager sur Facebook
                                                            • Partager sur Twitter
                                                            http://maymayhem.fr/

                                                            Parser une page web

                                                            × Après avoir cliqué sur "Répondre" vous serez invité à vous connecter pour que votre message soit publié.
                                                            × Attention, ce sujet est très ancien. Le déterrer n'est pas forcément approprié. Nous te conseillons de créer un nouveau sujet pour poser ta question.
                                                            • Editeur
                                                            • Markdown