Преобразование html в текст с помощью Python
Я пытаюсь преобразовать html-блок в текст с помощью Python.
Ввод:
<div class="body"><p><strong></strong></p>
<p><strong></strong>Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa</p>
<p>Consectetuer adipiscing elit. <a href="#" onclick="location.href='http://example.com/'; return false;" target="_blank" class="source">Some Link</a> Aenean commodo ligula eget dolor. Aenean massa</p>
<p>Aenean massa.Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa</p>
<p>Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa</p>
<p>Consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa</p></div>
Желаемый вывод:
Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa
Consectetuer adipiscing elit. Некоторые Ссылка Aenean como ligula eget dolor. Энеанская масса
Аенеан massa.Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean como ligula eget dolor. Aenean massa
Лорим ipsum dolor сидеть amet, consectetuer adipiscing elit. Aenean como ligula eget dolor. Aenean massa
Consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa
Я пробовал использовать html2text-модуль без особого успеха (я довольно новичок в python:))
вот что я пробовал:
#!/usr/bin/env python
import urllib2
import html2text
from BeautifulSoup import BeautifulSoup
soup = BeautifulSoup(urllib2.urlopen('http://example.com/page.html').read())
txt = soup.find('div', {'class' : 'body'})
print html2text.html2text(txt)
объект "txt" создает html-блок выше. Я хотел бы преобразовать его в текст и распечатать его на экране.
Любая помощь с частью кода будет очень оценена.
Ответы
Ответ 1
Чего мне не хватает? soup.get_text()
дает точно такой же вывод, который вы хотели...
from bs4 import BeautifulSoup
soup = BeautifulSoup(html)
print(soup.get_text())
выход
Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa
Consectetuer adipiscing elit. Some Link Aenean commodo ligula eget dolor. Aenean massa
Aenean massa.Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa
Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa
Consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa
ОБНОВЛЕНИЕ - И сохранять переводы строк, как указано в @t-8ch:
print(soup.get_text('\n'))
PS! Если быть точным, вы можете заменить символ новой строки на двойной - тогда он идентичен вашему примеру :)
soup.get_text().replace('\n','\n\n')
Ответ 2
Это возможно с использованием стандарта Python html.parser
:
from html.parser import HTMLParser
class HTMLFilter(HTMLParser):
text = ""
def handle_data(self, data):
self.text += data
f = HTMLFilter()
f.feed(data)
print(f.text)
Ответ 3
Вы можете использовать регулярное выражение... но не рекомендуется...
Следующий код просто удаляет все теги HTML в ваших данных, предоставляя вам текст.
import re
data = """<div class="body"><p><strong></strong></p>
<p><strong></strong>Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa</p>
<p>Consectetuer adipiscing elit. <a href="http://example.com/" target="_blank" class="source">Some Link</a> Aenean commodo ligula eget dolor. Aenean massa</p>
<p>Aenean massa.Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa</p>
<p>Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa</p>
<p>Consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa</p></div>"""
data = re.sub(r'<.*?>', '', data)
print data
Выход
Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa
Consectetuer adipiscing elit. Some Link Aenean commodo ligula eget dolor. Aenean massa
Aenean massa.Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa
Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa
Consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa
Ответ 4
'\n'
помещает новую строку между абзацами.
from bs4 import Beautifulsoup
soup = Beautifulsoup(text)
print(soup.get_text('\n'))
Ответ 5
Мне понадобился способ сделать это в клиентской системе, не загружая дополнительные библиотеки. Я не нашел хорошего решения, поэтому создал свой собственный. Не стесняйтесь использовать это, если хотите.
import urllib
def html2text(strText):
str1 = strText
int2 = str1.lower().find("<body")
if int2>0:
str1 = str1[int2:]
int2 = str1.lower().find("</body>")
if int2>0:
str1 = str1[:int2]
list1 = ['<br>', '<tr', '<td', '</p>', 'span>', 'li>', '</h', 'div>' ]
list2 = [chr(13), chr(13), chr(9), chr(13), chr(13), chr(13), chr(13), chr(13)]
bolFlag1 = True
bolFlag2 = True
strReturn = ""
for int1 in range(len(str1)):
str2 = str1[int1]
for int2 in range(len(list1)):
if str1[int1:int1+len(list1[int2])].lower() == list1[int2]:
strReturn = strReturn + list2[int2]
if str1[int1:int1+7].lower() == '<script' or str1[int1:int1+9].lower() == '<noscript':
bolFlag1 = False
if str1[int1:int1+6].lower() == '<style':
bolFlag1 = False
if str1[int1:int1+7].lower() == '</style':
bolFlag1 = True
if str1[int1:int1+9].lower() == '</script>' or str1[int1:int1+11].lower() == '</noscript>':
bolFlag1 = True
if str2 == '<':
bolFlag2 = False
if bolFlag1 and bolFlag2 and (ord(str2) != 10) :
strReturn = strReturn + str2
if str2 == '>':
bolFlag2 = True
if bolFlag1 and bolFlag2:
strReturn = strReturn.replace(chr(32)+chr(13), chr(13))
strReturn = strReturn.replace(chr(9)+chr(13), chr(13))
strReturn = strReturn.replace(chr(13)+chr(32), chr(13))
strReturn = strReturn.replace(chr(13)+chr(9), chr(13))
strReturn = strReturn.replace(chr(13)+chr(13), chr(13))
strReturn = strReturn.replace(chr(13), '\n')
return strReturn
url = "http://www.theguardian.com/world/2014/sep/25/us-air-strikes-islamic-state-oil-isis"
html = urllib.urlopen(url).read()
print html2text(html)
Ответ 6
Если вам нужно без каких-либо библиотек, я сегодня кодировал это:
https://github.com/iFA88/python-html-to-text
Не используйте и не изменяйте.
Работает с python 2.x, python 3 не тестировался. Никаких библиотек не требуется.
Пример использования:
def htmlToText(html):
def _getElement(subhtml,name,end=None):
ename = "<"+name+">"
a = subhtml.lower().find(ename)
if a == -1:
ename = "<"+name+" "
a = subhtml.lower().find(ename)
if a == -1: return
if end == None: end = "</"+name+">"
b = subhtml.lower()[a+len(ename):].find(end)+a+len(end)+len(ename)
if b-a-len(end)-len(ename) == -1:
b = subhtml[a+len(ename):].find('>')+a+len('>')+len(ename)
return subhtml[a:b]
def _getElementAttribute(element,name):
a = element.lower().find(name+'="')+len(name+'="')
if a == -1: return
b = element[a:].find('"')+a
return element[a:b]
def _getElementContent(element):
a = element.find(">")+len(">")
if a == -1: return
b = len(element)-element[::-1].find('<')-1
return element[a:b]
ret = ""
#if you wish get Title
headElement = _getElement(html,'head')
if headElement:
titleElement = _getElement(headElement, 'title')
if titleElement:
titleContent = _getElementContent(titleElement)
if titleContent:
ret += titleContent+"\n\n"
#get body content
bodyElement = _getElement(html,'body')
if bodyElement:
bodyContent = _getElementContent(bodyElement)
if bodyContent:
ret += bodyContent
#remove javascript
while True:
scriptElement = _getElement(ret, 'script')
if not scriptElement: scriptElement = _getElement(ret, 'script', '</noscript>')
if not scriptElement: break
ret = ret.replace(scriptElement, '')
#remove style
while True:
styleElement = _getElement(ret, 'style')
if not styleElement: break
ret = ret.replace(styleElement, '')
#replace links
while True:
linkElement = _getElement(ret, 'a')
if not linkElement: break
linkElementContent = _getElementContent(linkElement)
if linkElementContent:
#this will replace: '<a href="some.site">text</a>' -> 'text'
# ret = ret.replace(linkElement, linkElementContent)
#this will replace: '<a href="some.site">link</a>' -> 'some.site'
# linkElementHref = _getElementAttribute(linkElement, 'href')
# if linkElementHref:
# ret = ret.replace(linkElement, linkElementHref)
#this will replace: '<a href="some.site">link</a>' -> 'text ( some.site )'
linkElementHref = _getElementAttribute(linkElement, 'href')
if linkElementHref:
ret = ret.replace(linkElement, linkElementContent+' ( '+linkElementHref+' )')
#replace paragraphs
while True:
paragraphElement = _getElement(ret, 'p')
if not paragraphElement: break
paragraphElementContent = _getElementContent(paragraphElement)
if paragraphElementContent:
ret = ret.replace(paragraphElement, '\n\n'+paragraphElementContent+'\n\n')
else:
ret = ret.replace(paragraphElement, '')
#replace line breaks
ret = ret.replace('<br>', '\n')
ret = ret.replace('<br/>', '\n')
#replace bolds
while True:
boldElement = _getElement(ret, 'b')
if not boldElement: break
boldElementContent = _getElementContent(boldElement)
if boldElementContent:
ret = ret.replace(boldElement, boldElementContent.upper())
else:
ret = ret.replace(boldElement, '')
#replace images
while True:
imgElement = _getElement(ret, 'img')
if not imgElement: break
imgElementSrc = _getElementAttribute(imgElement, 'src')
if imgElementSrc:
ret = ret.replace(imgElement, '[IMG] '+imgElementSrc+' [IMG]')
else:
ret = ret.replace(imgElement, '')
#remove rest elements
while True:
a = ret.find("<")
if a == -1: break
b = ret[a:].find(">")+a
if b-a == -1: break
b2 = ret[b:].find(">")+b
if b2-b == -1: break
element = _getElement(ret, ret[a+1:b2])
if element:
elementContent = _getElementContent(element)
if elementContent:
ret = ret.replace(element, elementContent)
else:
ret = ret.replace(element, '')
return ret
html = """
<html>
<head>
<meta charset="UTF-8">
<title>I'm a nice website title</title>
<script src='script.js'></script>
<link rel="icon" type="image/x-icon" href="favicon.ico">
<style>
body {
display: inline-block;
font-family: Verdana;
margin: 0;
overflow-x: hidden;
padding-bottom: 20px;
padding-top: 20px;
text-align: center;
width: 850px;
}
</style>
</head>
<body>
<style>
p {
font-size: 1em;
}
</style>
<script>
document.write('Yes, i\'m a javascript!');
</script>
<p>
Im a text with <b>bold</b> and <i>italic</i> content.<br>If you <span style="font-size:2em">like</span> this visit my <a href="ethereumlottery.net" target="_blank">site</a>.
</p>
Here is a image: <img src="veryniceimage"/><br>
Here is a image with other format: <img src="veryniceimage"><br>
Here is a image with link: <a href="ethereumlottery.net"><img src="veryniceimage"/></a><br>
</body>
</html>""".replace('\n','').replace('\t','')
print htmlToText(html)
Результат:
I'm a nice website title
Im a text with BOLD and italic content.
If you like this visit my site ( ethereumlottery.net ).
Here is a image: [IMG] veryniceimage [IMG]
Here is a image with other format: [IMG] veryniceimage [IMG]
Here is a image with link: [IMG] veryniceimage [IMG] ( ethereumlottery.net )
Ответ 7
Можно использовать BeautifulSoup для удаления нежелательных сценариев и т.д., хотя вам может потребоваться поэкспериментировать с несколькими разными сайтами, чтобы убедиться, что вы охватили различные типы вещей, которые вы хотите исключить. Попробуйте следующее:
from requests import get
from bs4 import BeautifulSoup as BS
response = get('http://news.bbc.co.uk/2/hi/health/2284783.stm')
soup = BS(response.content, "html.parser")
for child in soup.body.children:
if child.name == 'script':
child.decompose()
print(soup.body.get_text())