web-dev-qa-db-de.com

Extrahieren von Text aus der HTML-Datei mit Python

Ich möchte den Text mit Python aus einer HTML-Datei extrahieren. Ich möchte im Wesentlichen die gleiche Ausgabe, die ich erhalten würde, wenn ich den Text aus einem Browser kopiert und in den Editor eingefügt habe. 

Ich möchte etwas robusteres als reguläre Ausdrücke, die bei schlecht formatiertem HTML möglicherweise nicht funktionieren. Ich habe gesehen, dass viele Leute Beautiful Soup empfohlen haben, aber ich hatte einige Probleme mit der Verwendung. Zum einen hat es unerwünschten Text wie JavaScript-Quelltext aufgenommen. Außerdem wurden keine HTML-Entitäten interpretiert. Ich würde zum Beispiel erwarten, dass 'in HTML-Quelltext in einen Apostroph in Text umgewandelt wird, als ob ich den Browserinhalt in den Editor eingefügt hätte.

Updatehtml2text sieht vielversprechend aus. Er behandelt HTML-Entitäten korrekt und ignoriert JavaScript. Es erzeugt jedoch nicht gerade Klartext. es erzeugt Abschriften, die dann in Klartext umgewandelt werden müssen. Es enthält keine Beispiele oder Dokumentation, aber der Code sieht sauber aus.


Verwandte Fragen:

195
John D. Cook

html2text ist ein Python-Programm, das hier ziemlich gute Arbeit leistet.

110
RexE

Der beste Code, den ich für das Extrahieren von Text gefunden habe, ohne Javascript zu erhalten oder nicht gewollt zu werden:

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)

Sie müssen BeautifulSoup nur installieren, bevor Sie:

pip install beautifulsoup4
110
PeYoTlL

HINWEIS: NTLK unterstützt die clean_html-Funktion nicht mehr

Originalantwort unten und eine Alternative in den Kommentaren.


Verwenden Sie NLTK

Ich habe meine 4-5 Stunden damit verschwendet, die Probleme mit html2text zu beheben. Zum Glück konnte ich NLTK begegnen.
Es funktioniert magisch. 

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)
100
Shatu

Habe mich heute mit demselben Problem konfrontiert. Ich habe einen sehr einfachen HTML-Parser geschrieben, um den eingehenden Inhalt aller Markups zu entfernen und den restlichen Text mit einem Minimum an Formatierung zurückzugeben.

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 __== '__main__':
    main()
52
xperroni

Hier ist eine Version von xperronis Antwort, die etwas vollständiger ist. Es überspringt Skript- und Stilabschnitte und übersetzt Zeichen (z. B. ') und HTML-Entitäten (z. B. & amp;).

Es enthält auch einen trivialen Klartext-zu-HTML-Umkehrkonverter.

"""
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)
14
bit4

Sie können die html2text-Methode auch in der Stripogrammbibliothek verwenden.

from stripogram import html2text
text = html2text(your_html_string)

Um das Stripogramm zu installieren, führen Sie Sudo easy_install stripogram aus

8
GeekTantra

Es gibt eine Pattern-Bibliothek für Data Mining.

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

Sie können sogar entscheiden, welche Tags beibehalten werden sollen:

s = URL('http://www.clips.ua.ac.be').download()
s = plaintext(s, keep={'h1':[], 'h2':[], 'strong':[], 'a':['href']})
print s
7
Nuncjo

PyParsing macht einen guten Job. Das PyParsing-Wiki wurde getötet. Es gibt also einen anderen Ort, an dem es Beispiele für die Verwendung von PyParsing gibt ( beispiel link ). Ein Grund, etwas Zeit mit dem Pyparsing zu verbringen, ist, dass er auch ein sehr kurzes, gut organisiertes O'Reilly Short Cut-Handbuch geschrieben hat, das auch kostengünstig ist.

Trotzdem benutze ich BeautifulSoup viel und es ist nicht so schwierig, mit den Entitätenproblemen umzugehen. Sie können sie konvertieren, bevor Sie BeautifulSoup ausführen. 

Viel Glück 

6
PyNEwbie

Ich weiß, dass es bereits viele Antworten gibt, aber die meiste elegent - und Pythonic - Lösung, die ich gefunden habe, wird zum Teil hier beschrieben.

from bs4 import BeautifulSoup

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

Aktualisieren

Basierend auf Frasers Kommentar gibt es hier eine elegantere Lösung:

from bs4 import BeautifulSoup

clean_text = ''.join(BeautifulSoup(some_html_string, "html.parser").stripped_strings)
5
Floyd

wenn Sie mehr Geschwindigkeit und weniger Genauigkeit benötigen, können Sie rohe Lxml verwenden.

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
Anton Shelin

install html2text using 

pip install html2text

dann,

>>> 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
Pravitha V

Überprüfen Sie statt des HTMLParser-Moduls htmllib. Es hat eine ähnliche Oberfläche, erledigt aber mehr für Sie. (Es ist ziemlich alt, daher ist es keine große Hilfe, um Javascript und CSS loszuwerden. Sie können eine abgeleitete Klasse erstellen, aber Methoden mit Namen wie start_script und end_style hinzufügen (siehe die Python-Dokumente), aber es ist schwer um dies zuverlässig für fehlerhaftes HTML zu tun.) Wie auch immer, hier ist etwas ganz einfaches, das den Klartext auf die Konsole druckt

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
Mark

Dies ist nicht gerade eine Python-Lösung, aber es wird Text konvertieren, den Javascript in Text generieren würde, was meiner Meinung nach wichtig ist (E.G. google.com). Der Browser Links (nicht Lynx) verfügt über eine Javascript-Engine und konvertiert die Quelle mit der Option -dump in Text.

Du könntest also so etwas tun:

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
Andrew

Ich empfehle ein Python-Paket namens goose-extractor Goose versucht, die folgenden Informationen zu extrahieren:

Haupttext eines Artikels Hauptabbildung des Artikels Alle in Artikel Meta-Beschreibung Meta-Tags eingebetteten Youtube/Vimeo-Filme

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

3
Li Yingjun

Schöne Suppe konvertiert HTML-Entitäten. Es ist wahrscheinlich die beste Wahl, da HTML oft fehlerhaft ist und mit Unicode- und HTML-Codierungsproblemen gefüllt ist. Dies ist der Code, den ich zum Konvertieren von HTML in Rohtext verwende:

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
speedplane

Ich weiß, dass es hier schon viele Antworten gibt, aber ich denke newspaper3k verdient auch eine Erwähnung. Ich musste vor kurzem eine ähnliche Aufgabe ausführen, um den Text aus Artikeln im Web zu extrahieren, und diese Bibliothek hat dies in meinen Tests bisher sehr gut gemacht. Der in Menüelementen und Seitenleisten gefundene Text sowie JavaScript, das auf der Seite als OP-Anforderungen angezeigt wird, werden ignoriert. 

from newspaper import Article

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

Wenn Sie die HTML-Dateien bereits heruntergeladen haben, können Sie Folgendes tun:

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

Es gibt sogar einige NLP-Funktionen, um die Themen der Artikel zusammenzufassen:

article.nlp()
article.summary
2
spatel4140

Eine andere Nicht-Python-Lösung: Libre Office:

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

Der Grund, warum ich diesen gegenüber anderen Alternativen vorziehen sollte, ist, dass jeder HTML-Absatz in eine einzelne Textzeile (keine Zeilenumbrüche) umgewandelt wird. Dies ist, wonach ich gesucht habe. Andere Methoden erfordern eine Nachbearbeitung. Lynx liefert zwar schöne Ergebnisse, aber nicht genau das, wonach ich gesucht habe. Außerdem kann Libre Office verwendet werden, um aus allen möglichen Formaten zu konvertieren ...

2
YakovK

Eine andere Möglichkeit besteht darin, die HTML-Datei über einen textbasierten Webbrowser auszuführen und sie zu sichern. Zum Beispiel (mit Lynx):

lynx -dump html_to_convert.html > converted_html.txt

Dies kann wie folgt in einem Python-Skript erfolgen:

import subprocess

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

Sie erhalten nicht genau den Text aus der HTML-Datei, aber je nach Anwendungsfall kann dies der Ausgabe von html2text vorzuziehen sein. 

2
John Lucas

Jeder hat bleach.clean(html,tags=[],strip=True) mit Bleach ? Ausprobiert. es funktioniert für mich.

2
rox

@ PeYoTILs Antwort mit BeautifulSoup und dem Entfernen von Stil- und Skriptinhalten funktionierte für mich nicht. Ich habe es mit decompose anstelle von extract ausprobiert, aber es hat immer noch nicht funktioniert. Also habe ich meine eigene erstellt, die den Text auch mit den <p>-Tags formatiert und <a>-Tags durch den href-Link ersetzt. Kopiert auch Links im Text. Verfügbar unter this Gist mit eingebettetem Testdokument.

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
racitup

In Python 3.x können Sie dies auf sehr einfache Weise tun, indem Sie imaplib- und email-Pakete importieren. Dies ist zwar ein älterer Beitrag, aber vielleicht kann meine Antwort Neulingen in diesem Beitrag helfen.

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

Jetzt können Sie die Körpervariable drucken und es wird im Klartextformat erstellt. :) Wenn es für Sie gut genug ist, wäre es nett, es als akzeptierte Antwort auszuwählen.

1
Wahib Ul Haq

Ich habe gute Ergebnisse mit Apache Tika erzielt. Sein Zweck ist das Extrahieren von Metadaten und Text aus dem Inhalt. Daher wird der zugrunde liegende Parser entsprechend angepasst.

Tika kann als Server ausgeführt werden, ist in einem Docker - Container einfach auszuführen/bereitzustellen und von dort aus über Python - Bindungen zugänglich.

1
u-phoria

auf einfache Weise

import re

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

dieser Code findet alle Teile des HTML-Textes, die mit '<' beginnen und mit '>' enden, und ersetzt alle gefundenen Zeichen durch einen leeren String

1
David Fraga

Während viele Leute die Verwendung von Regex zum Entfernen von HTML-Tags erwähnt haben, gibt es viele Nachteile.

zum Beispiel:

<p>hello&nbsp;world</p>I love you

Sollte analysiert werden zu:

Hello world
I love you

Hier ist ein Ausschnitt, den ich mir ausgedacht habe, du kannst ihn an deine speziellen Bedürfnisse anpassen und funktioniert wie ein Zauber

import re
import html
def html2text(htm):
    ret = html.unescape(htm)
    ret = ret.translate({
        8209: ord('-'),
        8220: ord('"'),
        8221: ord('"'),
        160: ord(' '),
    })
    ret = re.sub(r"\s", " ", ret, flags = re.MULTILINE)
    ret = re.sub("<br>|<br />|</p>|</div>|</h\d>", "\n", ret, flags = re.IGNORECASE)
    ret = re.sub('<.*?>', ' ', ret, flags=re.DOTALL)
    ret = re.sub(r"  +", " ", ret)
    return ret
0
Uri Goren

Ein weiteres Beispiel für die Verwendung von BeautifulSoup4 in Python 2.7.9+

beinhaltet:

import urllib2
from bs4 import BeautifulSoup

Code:

def read_website_to_text(url):
    page = urllib2.urlopen(url)
    soup = BeautifulSoup(page, 'html.parser')
    for script in soup(["script", "style"]):
        script.extract() 
    text = soup.get_text()
    lines = (line.strip() for line in text.splitlines())
    chunks = (phrase.strip() for line in lines for phrase in line.split("  "))
    text = '\n'.join(chunk for chunk in chunks if chunk)
    return str(text.encode('utf-8'))

Erklärt:

Lesen Sie die URL-Daten als HTML ein (mit BeautifulSoup), entfernen Sie alle Skript- und Stilelemente und rufen Sie mit .get_text () auch nur den Text ab. Brechen Sie in Zeilen auf und entfernen Sie das führende und nachfolgende Leerzeichen. Brechen Sie dann die Mehrfach-Überschriften in eine Zeile, die jeder Block = (phrase.strip () für Zeile in Zeilen für Phrase in line.split ("")) enthält. Fügen Sie dann mit text = '\ n'.join leere Zeilen ein und kehren Sie schließlich als sanktioniertes utf-8 zurück.

Anmerkungen:

  • Einige Systeme, auf denen dies ausgeführt wird, schlagen aufgrund eines SSL-Problems mit https: // -Verbindungen fehl. Sie können die Überprüfung deaktivieren, um dieses Problem zu beheben. Beispielfix: http://blog.pengyifan.com/how-to-fix-python-ssl-certificate_verify_failed/

  • Bei Python <2.7.9 kann dies zu Problemen führen

  • text.encode ('utf-8') kann eine seltsame Codierung hinterlassen. Möglicherweise möchten Sie stattdessen nur str (text) zurückgeben.

0
Mike Q

Perl way (sorry Mom, ich mache das nie in der Produktion).

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
brunql

Hier ist der Code, den ich regelmäßig verwende.

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

Ich hoffe das hilft.

0
troymyname00

Der LibreOffice-Writer-Kommentar hat Vorteile, da die Anwendung Python-Makros verwenden kann. Es scheint mehrere Vorteile zu bieten, sowohl für die Beantwortung dieser Frage als auch für die Förderung der Makrobasis von LibreOffice. Wenn es sich bei dieser Lösung um eine einmalige Implementierung handelt und nicht als Teil eines größeren Produktionsprogramms verwendet werden soll, scheint das Öffnen des HTML-Codes in Writer und das Speichern der Seite als Text die hier beschriebenen Probleme zu lösen.

0
1of7

Am besten funktioniert für mich die Schrift. 

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)

Die Ergebnisse sind wirklich gut

sie können mit BeautifulSoup nur Text aus HTML extrahieren

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
saigopi