J'aimerais extraire le texte d'un fichier HTML à l'aide de Python. Je veux essentiellement le même résultat que si je copiais le texte depuis un navigateur et que je le collais dans le bloc-notes.
Je voudrais quelque chose de plus robuste que d'utiliser des expressions régulières pouvant échouer avec du HTML mal formé. J'ai vu beaucoup de gens recommander Beautiful Soup, mais j'ai eu quelques problèmes pour l'utiliser. D'une part, il a ramassé du texte indésirable, tel que la source JavaScript. En outre, il n'a pas interprété les entités HTML. Par exemple, je m'attendrais à ce que 'in HTML source soit converti en apostrophe en texte, comme si j'avais collé le contenu du navigateur dans le bloc-notes.
Mise à jourhtml2text
semble prometteur. Il gère correctement les entités HTML et ignore JavaScript. Cependant, cela ne produit pas exactement du texte brut; il produit un démarquage qui devrait ensuite être transformé en texte brut. Il ne contient aucun exemple ni documentation, mais le code est propre.
Questions connexes:
html2text est un programme Python qui fait un très bon travail à cet égard.
Le meilleur morceau de code que j'ai trouvé pour extraire du texte sans obtenir de javascript ou ne pas vouloir des choses:
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 vous suffit d'installer BeautifulSoup avant:
pip install beautifulsoup4
NOTE: NTLK ne prend plus en charge la fonction clean_html
Réponse originale ci-dessous, et une alternative dans les sections commentaires.
Utilisez NLTK
J'ai perdu mes 4-5 heures à résoudre les problèmes liés à 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)
Je me suis retrouvé face au même problème aujourd'hui. J'ai écrit un analyseur HTML très simple pour supprimer le contenu entrant de toutes les annotations, renvoyant le texte restant avec 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 __== '__main__':
main()
Voici une version de la réponse de xperroni qui est un peu plus complète. Il ignore les sections de script et de style et traduit les charrefs (par exemple, ') et les entités HTML (par exemple, & amp;).
Il inclut également un convertisseur inverse trivial de texte brut en 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 {'&':'&', "'":''', '"':'"', '<':'<', '>':'>'}.get(t)
return '<a href="%s">%s</a>' % (t, t)
return re.sub(r'https?://[^] ()"\';]+|[&\'"<>]', f, text)
Vous pouvez également utiliser la méthode html2text dans la bibliothèque de stripogrammes.
from stripogram import html2text
text = html2text(your_html_string)
Pour installer le stripogramme, lancez le stripogramme Sudo easy_install
Il existe une bibliothèque de motifs pour l'exploration de données.
http://www.clips.ua.ac.be/pages/pattern-web
Vous pouvez même décider quelles balises conserver:
s = URL('http://www.clips.ua.ac.be').download()
s = plaintext(s, keep={'h1':[], 'h2':[], 'strong':[], 'a':['href']})
print s
PyParsing fait un excellent travail. Le wiki PyParsing a été tué, voici donc un autre emplacement où il existe des exemples d'utilisation de PyParsing ( example link ). Une des raisons pour investir un peu de temps avec les pyparses est qu’il a également écrit un très bref manuel très bien organisé de O'Reilly Short Cut qui est également peu coûteux.
Cela dit, j'utilise beaucoup BeautifulSoup et il n'est pas difficile de traiter les problèmes d'entités, vous pouvez les convertir avant de lancer BeautifulSoup.
Bonne chance
Je sais qu’il ya déjà beaucoup de réponses, mais la solution la plus elegent et Pythonic que j’ai trouvée est décrite, en partie, ici .
from bs4 import BeautifulSoup
text = ''.join(BeautifulSoup(some_html_string, "html.parser").findAll(text=True))
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)
si vous avez besoin de plus de rapidité et de moins de précision, vous pouvez utiliser le format brut LXML.
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()
installer html2text using
pip installer 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!
Au lieu du module HTMLParser, consultez htmllib. Il a une interface similaire, mais fait plus de travail pour vous. (C'est assez ancien, donc ce n'est pas très utile pour se débarrasser de javascript et de css. Vous pouvez créer une classe dérivée, mais vous pouvez aussi ajouter des méthodes avec des noms comme start_script et end_style de le faire de manière fiable pour le HTML malformé.) Quoi qu'il en soit, voici quelque chose de simple qui affiche le texte brut 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)
Ce n'est pas exactement une solution Python, mais cela convertira du texte que Javascript générerait en texte, ce qui, à mon avis, est important (par exemple, google.com). Le navigateur Links (pas Lynx) a un moteur Javascript et convertira le source en texte avec l’option -dump.
Pour que vous puissiez 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()
Je recommande un paquet Python appelé goose-extractor Goose essaiera d'extraire les informations suivantes:
Texte principal d'un article Image principale de l'article Tous les films Youtube/Vimeo incorporés à l'article Description méta.
Une belle soupe convertit les entités html. C'est probablement votre meilleur choix é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 le code HTML en texte brut:
import BeautifulSoup
def getsoup(data, to_unicode=False):
data = data.replace(" ", " ")
# 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 ""
Je sais qu'il y a déjà beaucoup de réponses ici, mais je pense que journal3k mérite également une mention. J'ai récemment eu besoin de compléter une tâche similaire d'extraction du texte à partir d'articles sur le Web et cette bibliothèque a fait un excellent travail pour atteindre cet objectif dans mes tests. Il ignore le texte trouvé dans les éléments de menu et les barres latérales, ainsi que tout code JavaScript qui apparaît sur la page lorsque le PO demande.
from newspaper import Article
article = Article(url)
article.download()
article.parse()
article.text
Si vous avez déjà téléchargé les fichiers HTML, vous pouvez faire quelque chose comme ceci:
article = Article('')
article.set_html(html)
article.parse()
article.text
Il a même quelques fonctionnalités de la PNL pour résumer les sujets des articles:
article.nlp()
article.summary
Autre solution non python: Libre Office:
soffice --headless --invisible --convert-to txt input1.html
La raison pour laquelle je préfère celle-ci aux autres alternatives est que chaque paragraphe HTML est converti en une seule ligne de texte (sans saut de ligne), ce que je recherchais. D'autres méthodes nécessitent un post-traitement. Lynx produit bien Nice, mais pas exactement ce que je cherchais. En outre, Libre Office peut être utilisé pour convertir à partir de toutes sortes de formats ...
Une autre option consiste à exécuter le code HTML via un navigateur Web textuel et à 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 le texte du fichier HTML, mais selon votre cas d'utilisation, il peut être préférable à la sortie de html2text.
Quelqu'un a essayé bleach.clean(html,tags=[],strip=True)
avec eau de Javel ? ça marche pour moi.
La réponse de @ PeYoTIL à l'aide de BeautifulSoup et l'élimination du style et du contenu des scripts ne m'ont pas fonctionné. Je l'ai essayé en utilisant decompose
au lieu de extract
mais cela ne fonctionnait toujours pas. J'ai donc créé le mien qui formate également le texte en utilisant les balises <p>
et remplace les balises <a>
par le lien href. Copie également les liens dans le texte. Disponible sur this Gist avec une documentation de test intégrée.
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
En Python 3.x, vous pouvez le faire très facilement en important des packages "imaplib" et "email". Bien que ce soit un article plus ancien, ma réponse peut peut-être aider les nouveaux arrivants.
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
Vous pouvez maintenant imprimer une variable de corps et elle sera en texte brut :) Si cela vous convient, alors il serait agréable de le sélectionner comme réponse acceptée.
J'ai eu de bons résultats avec Apache Tika . Son but est d’extraire les métadonnées et le texte du contenu. L’analyseur sous-jacent est donc réglé en conséquence.
Tika peut être exécuté en tant que serveur , est simple à exécuter/déployer dans un conteneur Docker, et il est accessible depuis celui-ci via Python bindings .
de 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 celles trouvées par une chaîne vide
Bien que de nombreuses personnes aient mentionné l'utilisation de regex pour supprimer les balises HTML, il existe de nombreux inconvénients.
par exemple:
<p>hello world</p>I love you
Devrait être analysé à:
Hello world
I love you
Voici un extrait que j'ai créé, vous pouvez le personnaliser selon vos besoins spécifiques, et cela fonctionne comme un charme
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
Un autre exemple d'utilisation de BeautifulSoup4 dans Python 2.7.9+
comprend:
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'))
Expliqué:
Lisez les données url au format HTML (avec BeautifulSoup), supprimez tous les éléments de script et de style et récupérez simplement le texte avec .get_text (). Découpez des lignes et supprimez les espaces de début et de fin sur chacune d'elles, puis séparez plusieurs titres en une ligne, chaque fragment = (phrase.strip () pour ligne à ligne pour phrase à line.split ("")). Ensuite, en utilisant text = '\ n'.join, supprimez les lignes vierges, puis retournez comme sanctionné utf-8.
Notes:
Certains systèmes sur lesquels il est exécuté échoueront avec les connexions https: // en raison d'un problème SSL, vous pouvez désactiver la vérification pour résoudre ce problème. Exemple de correction: http://blog.pengyifan.com/how-to-fix-python-ssl-certificate_verify_failed/
Python <2.7.9 peut avoir un problème d’exécution
text.encode ('utf-8') peut laisser un encodage bizarre, peut vouloir simplement renvoyer str (text) à la place.
Perl way (désolée 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
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 aide.
Le commentaire du rédacteur LibreOffice a du mérite car l’application peut utiliser des macros python. Il semble offrir de multiples avantages, à la fois pour répondre à cette question et pour renforcer la base macro de LibreOffice. Si cette résolution est une implémentation unique, plutôt que d'être utilisée dans le cadre d'un programme de production plus étendu, ouvrir le code HTML dans l'écriture et enregistrer la page en tant que texte semblerait résoudre les problèmes abordés ici.
Le mieux travaillé pour moi est les inscriptions.
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
vous pouvez extraire uniquement du texte 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)