Extraire du texte d'un fichier HTML en utilisant Python

j'aimerais extraire le texte d'un fichier HTML en utilisant Python. Je veux essentiellement la même sortie que j'obtiendrais si je copiais le texte d'un navigateur et le collais dans le bloc-notes.

j'aimerais quelque chose de plus robuste que d'utiliser des expressions régulières qui peuvent échouer sur un HTML mal formé. J'ai vu beaucoup de gens recommandent Belle Soupe, mais j'ai eu quelques problèmes à l'utiliser. Pour un, il a ramassé des textes indésirables, tels que JavaScript source. Aussi, il n'a pas l'interpréter Entités HTML. Par exemple, je m'attendrais à ce que ' in HTML source soit converti en apostrophe dans le texte, tout comme si j'avais collé le contenu du navigateur dans le bloc-notes.

mise à Jour html2text semble prometteur. Il gère correctement les entités HTML et ignore le JavaScript. Cependant, il ne produit pas exactement du texte simple; il produit du markdown qui devrait alors être transformé en texte simple. Il est livré sans exemples ni documentation, mais le code semble propre.


questions connexes:

  • filtrer les balises HTML et résoudre les entités en python
  • Convertissez les entités XML / HTML en chaîne Unicode en Python
184
demandé sur Community 2008-11-30 05:28:04

29 réponses

html2text est un programme Python qui fait du bon travail à ce niveau.

102
répondu RexE 2018-06-30 20:54:46

le meilleur morceau de code que j'ai trouvé pour extraire du texte sans obtenir javascript ou des choses non désirées:

import urllib
from bs4 import BeautifulSoup

url = "http://news.bbc.co.uk/2/hi/health/2284783.stm"
html = urllib.urlopen(url).read()
soup = BeautifulSoup(html)

# kill all script and style elements
for script in soup(["script", "style"]):
    script.extract()    # rip it out

# get text
text = soup.get_text()

# break into lines and remove leading and trailing space on each
lines = (line.strip() for line in text.splitlines())
# break multi-headlines into a line each
chunks = (phrase.strip() for line in lines for phrase in line.split("  "))
# drop blank lines
text = '\n'.join(chunk for chunk in chunks if chunk)

print(text)

il suffit d'installer BeautifulSoup avant:

pip install beautifulsoup4
102
répondu PeYoTlL 2014-07-07 19:18:20

NOTE: NTLK ne supporte plus clean_html fonction

réponse originale ci-dessous, et une alternative dans les sections des commentaires.


Use NLTK

j'ai perdu mes 4-5 heures à résoudre les problèmes avec html2text. Heureusement, j'ai pu rencontrer NLTK.

Cela fonctionne comme par magie.

import nltk   
from urllib import urlopen

url = "http://news.bbc.co.uk/2/hi/health/2284783.stm"    
html = urlopen(url).read()    
raw = nltk.clean_html(html)  
print(raw)
102
répondu Shatu 2016-10-22 15:27:39

a été confronté au même problème aujourd'hui. J'ai écrit un analyseur HTML très simple pour supprimer le contenu entrant de tous les markups, en retournant le texte restant avec seulement un minimum de formatage.

from HTMLParser import HTMLParser
from re import sub
from sys import stderr
from traceback import print_exc

class _DeHTMLParser(HTMLParser):
    def __init__(self):
        HTMLParser.__init__(self)
        self.__text = []

    def handle_data(self, data):
        text = data.strip()
        if len(text) > 0:
            text = sub('[ \t\r\n]+', ' ', text)
            self.__text.append(text + ' ')

    def handle_starttag(self, tag, attrs):
        if tag == 'p':
            self.__text.append('\n\n')
        elif tag == 'br':
            self.__text.append('\n')

    def handle_startendtag(self, tag, attrs):
        if tag == 'br':
            self.__text.append('\n\n')

    def text(self):
        return ''.join(self.__text).strip()


def dehtml(text):
    try:
        parser = _DeHTMLParser()
        parser.feed(text)
        parser.close()
        return parser.text()
    except:
        print_exc(file=stderr)
        return text


def main():
    text = r'''
        <html>
            <body>
                <b>Project:</b> DeHTML<br>
                <b>Description</b>:<br>
                This small script is intended to allow conversion from HTML markup to 
                plain text.
            </body>
        </html>
    '''
    print(dehtml(text))


if __name__ == '__main__':
    main()
52
répondu xperroni 2010-10-21 13:14:38

Voici une version de la réponse de xperroni qui est un peu plus complète. Il saute les sections script et style et traduit des charrefs (e.g., ') et des entités HTML (e.g., &).

comprend également un convertisseur inversé trivial simple-texte-html.

"""
HTML <-> text conversions.
"""
from HTMLParser import HTMLParser, HTMLParseError
from htmlentitydefs import name2codepoint
import re

class _HTMLToText(HTMLParser):
    def __init__(self):
        HTMLParser.__init__(self)
        self._buf = []
        self.hide_output = False

    def handle_starttag(self, tag, attrs):
        if tag in ('p', 'br') and not self.hide_output:
            self._buf.append('\n')
        elif tag in ('script', 'style'):
            self.hide_output = True

    def handle_startendtag(self, tag, attrs):
        if tag == 'br':
            self._buf.append('\n')

    def handle_endtag(self, tag):
        if tag == 'p':
            self._buf.append('\n')
        elif tag in ('script', 'style'):
            self.hide_output = False

    def handle_data(self, text):
        if text and not self.hide_output:
            self._buf.append(re.sub(r'\s+', ' ', text))

    def handle_entityref(self, name):
        if name in name2codepoint and not self.hide_output:
            c = unichr(name2codepoint[name])
            self._buf.append(c)

    def handle_charref(self, name):
        if not self.hide_output:
            n = int(name[1:], 16) if name.startswith('x') else int(name)
            self._buf.append(unichr(n))

    def get_text(self):
        return re.sub(r' +', ' ', ''.join(self._buf))

def html_to_text(html):
    """
    Given a piece of HTML, return the plain text it contains.
    This handles entities and char refs, but not javascript and stylesheets.
    """
    parser = _HTMLToText()
    try:
        parser.feed(html)
        parser.close()
    except HTMLParseError:
        pass
    return parser.get_text()

def text_to_html(text):
    """
    Convert the given text to html, wrapping what looks like URLs with <a> tags,
    converting newlines to <br> tags and converting confusing chars into html
    entities.
    """
    def f(mo):
        t = mo.group()
        if len(t) == 1:
            return {'&':'&amp;', "'":'&#39;', '"':'&quot;', '<':'&lt;', '>':'&gt;'}.get(t)
        return '<a href="%s">%s</a>' % (t, t)
    return re.sub(r'https?://[^] ()"\';]+|[&\'"<>]', f, text)
13
répondu bit4 2013-05-07 16:04:21

vous pouvez également utiliser la méthode html2text dans la bibliothèque stripogram.

from stripogram import html2text
text = html2text(your_html_string)

Pour installer stripogram lancer la commande sudo easy_install stripogram

8
répondu GeekTantra 2009-09-23 03:21:58

il y a une bibliothèque de modèles pour l'exploration de données.

http://www.clips.ua.ac.be/pages/pattern-web

vous pouvez même décider quelles étiquettes à garder:

s = URL('http://www.clips.ua.ac.be').download()
s = plaintext(s, keep={'h1':[], 'h2':[], 'strong':[], 'a':['href']})
print s
7
répondu Nuncjo 2012-11-29 19:28:38

PyParsing fait du bon travail. Le wiki de PyParsing a été tué donc voici un autre endroit où il y a des exemples d'utilisation de PyParsing ( exemple de lien ). Une raison pour investir un peu de temps avec pyparsing est qu'il a également écrit un très bref manuel très bien organisé O'Reilly Short Cut qui est également peu coûteux.

cela dit, J'utilise BeautifulSoup beaucoup et il n'est pas si difficile de traiter avec les questions d'entités, vous pouvez convertissez-les avant de courir BeautifulSoup.

Goodluck

6
répondu PyNEwbie 2018-08-16 13:18:06

je sais qu'il y a déjà beaucoup de réponses, mais la plus elegent et Pythonic solution que j'ai trouvé est décrite, en partie, ici .

from bs4 import BeautifulSoup

text = ''.join(BeautifulSoup(some_html_string, "html.parser").findAll(text=True))

mise à Jour

D'après le commentaire de Fraser, voici une solution plus élégante:

from bs4 import BeautifulSoup

clean_text = ''.join(BeautifulSoup(some_html_string, "html.parser").stripped_strings)
5
répondu Floyd 2018-05-01 00:26:20

ce n'est pas exactement une solution Python, mais cela convertira le texte que Javascript générera en texte, ce qui est important à mon avis (E. G. google.com). Le navigateur Links (pas Lynx) a un moteur Javascript, et convertira source en texte avec l'option-dump.

pour que tu puisses faire quelque chose comme:

fname = os.tmpnam()
fname.write(html_source)
proc = subprocess.Popen(['links', '-dump', fname], 
                        stdout=subprocess.PIPE,
                        stderr=open('/dev/null','w'))
text = proc.stdout.read()
4
répondu Andrew 2012-05-18 10:02:26

au lieu du module HTMLParser, Vérifiez htmllib. Il a une interface similaire, mais fait plus de travail pour vous. (Il est assez ancien, donc il n'est pas beaucoup d'aide en termes de se débarrasser de javascript et css. Vous pouvez faire une classe dérivée, mais et ajouter des méthodes avec des noms comme start_script et end_style (voir les docs python pour plus de détails), mais il est difficile de le faire de manière fiable pour html malformé. En tout cas, voici quelque chose de simple qui imprime le texte en clair sur la console

from htmllib import HTMLParser, HTMLParseError
from formatter import AbstractFormatter, DumbWriter
p = HTMLParser(AbstractFormatter(DumbWriter()))
try: p.feed('hello<br>there'); p.close() #calling close is not usually needed, but let's play it safe
except HTMLParseError: print ':(' #the html is badly malformed (or you found a bug)
4
répondu Mark 2015-07-19 00:57:25

si vous avez besoin de plus de vitesse et moins de précision, vous pouvez utiliser le lxml brut.

import lxml.html as lh
from lxml.html.clean import clean_html

def lxml_to_text(html):
    doc = lh.fromstring(html)
    doc = clean_html(doc)
    return doc.text_content()
4
répondu Anton Shelin 2016-08-30 11:21:42

installer html2text à l'aide

pip install html2text

puis,

>>> import html2text
>>>
>>> h = html2text.HTML2Text()
>>> # Ignore converting links from HTML
>>> h.ignore_links = True
>>> print h.handle("<p>Hello, <a href='http://earth.google.com/'>world</a>!")
Hello, world!
4
répondu Pravitha V 2017-04-05 07:16:30

belle soupe ne convertit entités html. C'est probablement votre meilleur pari étant donné que HTML est souvent bogué et rempli de problèmes d'encodage unicode et html. C'est le code que j'utilise pour convertir html en texte brut:

import BeautifulSoup
def getsoup(data, to_unicode=False):
    data = data.replace("&nbsp;", " ")
    # Fixes for bad markup I've seen in the wild.  Remove if not applicable.
    masssage_bad_comments = [
        (re.compile('<!-([^-])'), lambda match: '<!--' + match.group(1)),
        (re.compile('<!WWWAnswer T[=\w\d\s]*>'), lambda match: '<!--' + match.group(0) + '-->'),
    ]
    myNewMassage = copy.copy(BeautifulSoup.BeautifulSoup.MARKUP_MASSAGE)
    myNewMassage.extend(masssage_bad_comments)
    return BeautifulSoup.BeautifulSoup(data, markupMassage=myNewMassage,
        convertEntities=BeautifulSoup.BeautifulSoup.ALL_ENTITIES 
                    if to_unicode else None)

remove_html = lambda c: getsoup(c, to_unicode=True).getText(separator=u' ') if c else ""
3
répondu speedplane 2012-11-30 08:23:23

je recommande un paquet Python appelé goose-extractor Goose tentera d'extraire les renseignements suivants:

texte Principal d'un article Image principale de l'article Tous les films Youtube/Vimeo inclus dans l'article Méta Description Meta tags

plus : https://pypi.python.org/pypi/goose-extractor /

3
répondu Li Yingjun 2015-11-25 13:05:59

une autre option est d'exécuter le html à travers un navigateur web basé sur le texte et de le vider. Par exemple (en utilisant Lynx):

lynx -dump html_to_convert.html > converted_html.txt

cela peut être fait dans un script python comme suit:

import subprocess

with open('converted_html.txt', 'w') as outputFile:
    subprocess.call(['lynx', '-dump', 'html_to_convert.html'], stdout=testFile)

il ne vous donnera pas exactement juste le texte du fichier HTML, mais selon votre cas d'utilisation, il peut être préférable à la sortie de html2text.

2
répondu John Lucas 2014-08-08 02:29:50

une autre solution non-python: Libre Office:

soffice --headless --invisible --convert-to txt input1.html

la raison pour laquelle je préfère celle-ci plutôt que d'autres alternatives est que chaque paragraphe HTML est converti en une seule ligne de texte (pas de sauts de ligne), ce que je cherchais. D'autres méthodes nécessitent un post-traitement. Lynx produit bel output, mais pas exactement ce que je cherchais. En outre, Libre Office peut être utilisé pour convertir à partir de toutes sortes de formats...

2
répondu YakovK 2015-12-11 04:11:45

je sais qu'il y a déjà beaucoup de réponses ici, mais je pense que newspaper3k mérite également une mention. J'ai récemment eu besoin d'accomplir une tâche similaire d'extraire le texte des articles sur le web et cette bibliothèque a fait un excellent travail de réaliser cela jusqu'à présent dans mes tests. Il ignore le texte trouvé dans les éléments de menu et les barres latérales ainsi que tout JavaScript qui apparaît sur la page comme le demande L'OP.

from newspaper import Article

article = Article(url)
article.download()
article.parse()
article.text

Si vous avez déjà les fichiers HTML téléchargés vous pouvez faire quelque chose comme ceci:

article = Article('')
article.set_html(html)
article.parse()
article.text

Il a même un peu de PNL fonctionnalités permettant de résumer les sujets d'articles:

article.nlp()
article.summary
2
répondu spatel4140 2018-02-18 13:36:16

d'une manière simple

import re

html_text = open('html_file.html').read()
text_filtered = re.sub(r'<(.*?)>', '', html_text)

ce code trouve toutes les parties de html_text commencées par ' < 'et se terminant par' > 'et remplace toutes les parties trouvées par une chaîne vide

1
répondu David Fraga 2016-06-02 15:04:54

la réponse de @PeYoTIL en utilisant BeautifulSoup et en éliminant le style et le contenu du script n'a pas fonctionné pour moi. J'ai essayé avec decompose au lieu de extract mais ça n'a pas marché. J'ai donc créé le mien qui formate également le texte en utilisant les tags <p> et remplace <a> tags avec le lien href. Aussi copes avec des liens à l'intérieur du texte. Disponible à l' ce gist avec un test de doc intégré.

from bs4 import BeautifulSoup, NavigableString

def html_to_text(html):
    "Creates a formatted text email message as a string from a rendered html template (page)"
    soup = BeautifulSoup(html, 'html.parser')
    # Ignore anything in head
    body, text = soup.body, []
    for element in body.descendants:
        # We use type and not isinstance since comments, cdata, etc are subclasses that we don't want
        if type(element) == NavigableString:
            # We use the assumption that other tags can't be inside a script or style
            if element.parent.name in ('script', 'style'):
                continue

            # remove any multiple and leading/trailing whitespace
            string = ' '.join(element.string.split())
            if string:
                if element.parent.name == 'a':
                    a_tag = element.parent
                    # replace link text with the link
                    string = a_tag['href']
                    # concatenate with any non-empty immediately previous string
                    if (    type(a_tag.previous_sibling) == NavigableString and
                            a_tag.previous_sibling.string.strip() ):
                        text[-1] = text[-1] + ' ' + string
                        continue
                elif element.previous_sibling and element.previous_sibling.name == 'a':
                    text[-1] = text[-1] + ' ' + string
                    continue
                elif element.parent.name == 'p':
                    # Add extra paragraph formatting newline
                    string = '\n' + string
                text += [string]
    doc = '\n'.join(text)
    return doc
1
répondu racitup 2016-12-06 15:06:19

Quelqu'un a essayé bleach.clean(html,tags=[],strip=True) avec eau de Javel ? c'est de travailler pour moi.

1
répondu rox 2017-01-16 14:10:24

En Python 3.x vous pouvez le faire très facilement en important des paquets' imaplib 'et' email'. Bien que ce soit un poste plus ancien, mais peut-être que ma réponse peut aider les nouveaux venus sur ce poste.

status, data = self.imap.fetch(num, '(RFC822)')
email_msg = email.message_from_bytes(data[0][1]) 
#email.message_from_string(data[0][1])

#If message is multi part we only want the text version of the body, this walks the message and gets the body.

if email_msg.is_multipart():
    for part in email_msg.walk():       
        if part.get_content_type() == "text/plain":
            body = part.get_payload(decode=True) #to control automatic email-style MIME decoding (e.g., Base64, uuencode, quoted-printable)
            body = body.decode()
        elif part.get_content_type() == "text/html":
            continue

Maintenant, vous pouvez imprimer la variable body et elle sera en format texte :) si elle est assez bonne pour vous, alors il serait agréable de la sélectionner comme réponse acceptée.

1
répondu Wahib Ul Haq 2017-05-16 20:25:35

j'ai eu de bons résultats avec Apache Tika . Son but est d'extraire des métadonnées et du texte du contenu, de sorte que l'analyseur sous-jacent est accordé en conséquence.

Tika peut être exécuté comme un server , est trivial pour exécuter / déployer dans un conteneur Docker, et de là peut être accédé via Python bindings .

1
répondu u-phoria 2018-05-07 11:07:18

voici le code que j'utilise régulièrement.

from bs4 import BeautifulSoup
import urllib.request


def processText(webpage):

    # EMPTY LIST TO STORE PROCESSED TEXT
    proc_text = []

    try:
        news_open = urllib.request.urlopen(webpage.group())
        news_soup = BeautifulSoup(news_open, "lxml")
        news_para = news_soup.find_all("p", text = True)

        for item in news_para:
            # SPLIT WORDS, JOIN WORDS TO REMOVE EXTRA SPACES
            para_text = (' ').join((item.text).split())

            # COMBINE LINES/PARAGRAPHS INTO A LIST
            proc_text.append(para_text)

    except urllib.error.HTTPError:
        pass

    return proc_text

j'espère que ça aidera.

0
répondu troymyname00 2017-10-25 00:14:19

le mieux travaillé pour moi est les inscripteurs .

https://github.com/weblyzard/inscriptis

import urllib.request
from inscriptis import get_text

url = "http://www.informationscience.ch"
html = urllib.request.urlopen(url).read().decode('utf-8')

text = get_text(html)
print(text)

les résultats sont vraiment bons

0
répondu Vimal Thickvijayan Vims 2018-04-06 03:14:41

vous pouvez extraire seulement le texte de HTML avec BeautifulSoup

url = "https://www.geeksforgeeks.org/extracting-email-addresses-using-regular-expressions-python/"
con = urlopen(url).read()
soup = BeautifulSoup(con,'html.parser')
texts = soup.get_text()
print(texts)
0
répondu saigopi 2018-04-13 11:03:57

le commentaire de LibreOffice writer a du Mérite puisque l'application peut employer des macros python. Il semble offrir de multiples avantages à la fois pour répondre à cette question et pour renforcer la macro-base de LibreOffice. Si cette résolution est une mise en œuvre ponctuelle, plutôt que d'être utilisée dans le cadre d'un programme de production plus vaste, l'ouverture du HTML dans le rédacteur et la sauvegarde de la page comme texte sembleraient résoudre les problèmes discutés ici.

0
répondu 1of7 2018-05-15 22:54:37

Perl way (désolé maman, Je ne le ferai jamais en production).

import re

def html2text(html):
    res = re.sub('<.*?>', ' ', html, flags=re.DOTALL | re.MULTILINE)
    res = re.sub('\n+', '\n', res)
    res = re.sub('\r+', '', res)
    res = re.sub('[\t ]+', ' ', res)
    res = re.sub('\t+', '\t', res)
    res = re.sub('(\n )+', '\n ', res)
    return res
0
répondu brunql 2018-07-06 11:36:06

je réalise quelque chose comme ça.

>>> import requests
>>> url = "http://news.bbc.co.uk/2/hi/health/2284783.stm"
>>> res = requests.get(url)
>>> text = res.text
-1
répondu Waqar Detho 2016-08-07 17:27:43