แยกข้อความจากไฟล์ HTML โดยใช้ Python


243

ฉันต้องการแยกข้อความจากไฟล์ HTML โดยใช้ Python ฉันต้องการเอาท์พุทเดียวกับที่ฉันจะได้รับถ้าฉันคัดลอกข้อความจากเบราว์เซอร์และวางลงในแผ่นจดบันทึก

ฉันต้องการบางสิ่งที่แข็งแกร่งกว่าการใช้นิพจน์ทั่วไปที่อาจล้มเหลวใน HTML ที่มีรูปแบบไม่ดี ฉันเคยเห็นหลายคนแนะนำ Beautiful Soup แต่ฉันมีปัญหาเล็กน้อยในการใช้ สำหรับหนึ่งมันหยิบข้อความที่ไม่ต้องการเช่นแหล่งที่มาของ JavaScript นอกจากนี้มันไม่ได้แปลเอนทิตี HTML ตัวอย่างเช่นฉันคาดหวัง & # 39; ในซอร์ส HTML ที่จะแปลงเป็นเครื่องหมายอัญประกาศเดี่ยวในข้อความราวกับว่าฉันได้วางเนื้อหาของเบราว์เซอร์ลงในสมุดบันทึก

การปรับปรุง html2textดูมีแนวโน้ม มันจัดการเอนทิตี HTML อย่างถูกต้องและละเว้น JavaScript อย่างไรก็ตามมันไม่ได้ผลิตข้อความล้วน มันสร้างมาร์กดาวน์ที่จะต้องเปลี่ยนเป็นข้อความธรรมดา มันมาพร้อมกับตัวอย่างหรือเอกสารไม่ แต่รหัสดูสะอาด


คำถามที่เกี่ยวข้อง:


ในขณะที่บางคนดูเหมือนจะหาคำตอบ NLTK ของฉัน (ค่อนข้างเร็ว ๆ นี้) จะมีประโยชน์มากดังนั้นคุณอาจต้องการพิจารณาเปลี่ยนคำตอบที่ยอมรับ ขอบคุณ!
Shatu

1
ฉันไม่เคยคิดเลยว่าจะเจอคำถามที่ผู้เขียนบล็อกโปรดของฉันถาม! ความพยายาม!
Ryan G

1
@Shatu เมื่อโซลูชันของคุณไม่สามารถใช้งานได้อีกต่อไปคุณอาจต้องการลบความคิดเห็นของคุณ ขอบคุณ! ;)
Sнаđошƒаӽ

คำตอบ:


136

html2textเป็นโปรแกรม Python ที่ทำได้ค่อนข้างดี


5
bit เป็น gpl 3.0 ซึ่งหมายความว่าอาจเข้ากันไม่ได้
frog32

138
! ที่น่าตื่นตาตื่นใจ มันเป็นผู้เขียนคือ RIP Aaron Swartz
Atul Arvind

2
ไม่มีใครพบทางเลือกใด ๆ กับ html2text เนื่องจาก GPL 3.0 หรือไม่
jontsai

1
GPL ไม่เลวเท่าที่คนต้องการ แอรอนรู้ดีที่สุด
Steve K

2
ฉันลองทั้ง html2text และ nltk แต่มันไม่ได้ผลสำหรับฉัน ฉันลงเอยด้วย Beautiful Soup 4 ซึ่งใช้งานได้ดี
Ryan

150

โค้ดที่ดีที่สุดที่ฉันพบสำหรับการแยกข้อความโดยไม่ได้รับจาวาสคริปต์หรือไม่ต้องการสิ่ง:

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)

คุณต้องติดตั้ง BeautifulSoup ก่อน:

pip install beautifulsoup4

2
ถ้าเราต้องการเลือกบางบรรทัดแค่พูดว่าบรรทัด # 3
hepidad

3
สคริปต์การฆ่าบิตผู้ช่วยให้รอด !!
Nanda

2
หลังจากผ่านคำตอบ stackoverflow จำนวนมากฉันรู้สึกว่านี่เป็นตัวเลือกที่ดีที่สุดสำหรับฉัน ปัญหาหนึ่งที่ฉันพบคือมีการเพิ่มบรรทัดเข้าด้วยกันในบางกรณี ฉันสามารถเอาชนะมันได้โดยการเพิ่มตัวคั่นในฟังก์ชัน get_text:text = soup.get_text(separator=' ')
Joswin KJ

5
แทนที่จะsoup.get_text()ใช้ฉันsoup.body.get_text()จึงไม่ได้รับข้อความใด ๆ จาก<headองค์ประกอบ> เช่นชื่อเรื่อง
Sjoerd

10
สำหรับ Python 3from urllib.request import urlopen
Jacob Kalakal Joseph

99

หมายเหตุ: NTLK ไม่รองรับclean_htmlฟังก์ชั่นอีกต่อไป

คำตอบเดิมด้านล่างและทางเลือกในส่วนความคิดเห็น


ใช้NLTK

ฉันเสียเวลา 4-5 ชั่วโมงในการแก้ไขปัญหาด้วย html2text โชคดีที่ฉันเจอ NLTK
มันใช้งานได้อย่างน่าอัศจรรย์

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)

8
บางครั้งนั่นก็เพียงพอแล้ว :)
Sharmila

8
ฉันต้องการที่จะขึ้นคะแนนนี้พันครั้ง ฉันติดอยู่ในนรก regex แต่ดูเถิดตอนนี้ฉันเห็นภูมิปัญญาของ NLTK
BenDundee

26
เห็นได้ชัดว่าไม่สนับสนุน clean_html อีกต่อไป: github.com/nltk/nltk/commit/…
alexanderlukanin13

5
การนำเข้าไลบรารีจำนวนมากอย่าง nltk สำหรับงานง่ายๆเช่นนี้จะมากเกินไป
ริชชี่

54
@ alexanderlukanin13 จากแหล่งที่มา:raise NotImplementedError ("To remove HTML markup, use BeautifulSoup's get_text() function")
Chris Arena

54

พบว่าตัวเองกำลังเผชิญกับปัญหาเดียวกันในวันนี้ ฉันเขียนตัวแยกวิเคราะห์ HTML ที่ง่ายมากเพื่อตัดเนื้อหาขาเข้าของมาร์กอัปทั้งหมดโดยส่งคืนข้อความที่เหลือด้วยการจัดรูปแบบขั้นต่ำ

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()

5
นี่เป็นวิธีที่ง่ายที่สุดในการทำเช่นนี้ใน Python (2.7) โดยใช้เฉพาะโมดูลเริ่มต้น ซึ่งมันโง่จริง ๆ เพราะนี่เป็นสิ่งที่ต้องการกันโดยทั่วไปและไม่มีเหตุผลที่ดีว่าทำไมไม่มี parser สำหรับสิ่งนี้ในโมดูล HTMLParser เริ่มต้น
Ingmar Hupp

2
ฉันไม่คิดว่าจะแปลงอักขระ html เป็น unicode ใช่ไหม ตัวอย่างเช่น&amp;จะไม่ถูกแปลงเป็น&ใช่มั้ย
speedplane

สำหรับ Python 3 ให้ใช้from html.parser import HTMLParser
sebhaase

14

นี่คือเวอร์ชันของคำตอบของ xperroni ที่สมบูรณ์กว่านี้เล็กน้อย มันข้ามส่วนของสคริปต์และสไตล์และแปล charrefs (เช่น & # 39;) และเอนทิตี HTML (เช่น & amp;)

นอกจากนี้ยังมีตัวแปลงผกผันข้อความธรรมดาเป็น 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)


ใน get_text '' .join ควรเป็น '' .join ควรมีพื้นที่ว่างมิฉะนั้นข้อความบางส่วนจะรวมเข้าด้วยกัน
Obinna Nnenanya

1
นอกจากนี้สิ่งนี้จะไม่จับข้อความทั้งหมดยกเว้นคุณรวมแท็กคอนเทนเนอร์ข้อความอื่น ๆ เช่น H1, H2 .... , ระยะห่าง ฯลฯ ฉันต้องบิดมันเพื่อให้ครอบคลุมได้ดีขึ้น
Obinna Nnenanya

11

ฉันรู้ว่ามีจำนวนมากที่มีคำตอบอยู่แล้ว แต่ส่วนใหญ่elegentและpythonicวิธีการแก้ปัญหาที่ฉันได้พบมีการอธิบายในส่วนที่นี่

from bs4 import BeautifulSoup

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

ปรับปรุง

จากความคิดเห็นของ Fraser นี่เป็นคำตอบที่ดีกว่า:

from bs4 import BeautifulSoup

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

2
เพื่อหลีกเลี่ยงการเตือนระบุ parser สำหรับ BeautifulSoup ที่จะใช้:text = ''.join(BeautifulSoup(some_html_string, "lxml").findAll(text=True))
Floyd

คุณสามารถใช้ตัวสร้าง stripped_strings เพื่อหลีกเลี่ยงพื้นที่สีขาวมากเกินไป - เช่นclean_text = ''.join(BeautifulSoup(some_html_string, "html.parser").stripped_strings
Fraser

8

คุณสามารถใช้วิธี html2text ในไลบรารี stripogram ได้เช่นกัน

from stripogram import html2text
text = html2text(your_html_string)

ในการติดตั้ง stripogram ให้เรียกใช้ sudo easy_install stripogram


23
โมดูลนี้ตามที่หน้า pypiเลิกใช้แล้ว: "ถ้าคุณไม่มีเหตุผลทางประวัติศาสตร์สำหรับการใช้แพ็คเกจนี้
intuited

7

มีไลบรารีรูปแบบสำหรับการขุดข้อมูล

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

คุณสามารถตัดสินใจได้ว่าจะเก็บแท็กอะไร:

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

6

PyParsing ทำได้ดีมาก วิกิ PyParsing ถูกฆ่าดังนั้นนี่คืออีกตำแหน่งหนึ่งที่มีตัวอย่างการใช้ PyParsing ( ลิงก์ตัวอย่าง ) เหตุผลหนึ่งในการลงทุนเพียงเล็กน้อยกับ pyparsing ก็คือเขาได้เขียนคู่มือ O'Reilly Short Cut ที่จัดระเบียบอย่างดีมาก ๆ ซึ่งก็มีราคาไม่แพง

ต้องบอกว่าฉันใช้ BeautifulSoup เป็นจำนวนมากและไม่ยากที่จะจัดการกับปัญหาเอนทิตีคุณสามารถแปลงมันก่อนที่คุณจะเรียกใช้ BeautifulSoup

โชคดี


1
ลิงก์ตายหรือมีรสเปรี้ยว
Yvette

4

นี่ไม่ใช่โซลูชันของ Python แต่จะแปลงข้อความ Javascript ที่จะสร้างเป็นข้อความซึ่งฉันคิดว่าสำคัญ (EG google.com) ลิงค์เบราว์เซอร์ (ไม่ใช่ Lynx) มีเอนจิ้น Javascript และจะแปลงซอร์สเป็นข้อความด้วยตัวเลือก -dump

ดังนั้นคุณสามารถทำสิ่งที่ชอบ:

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

แทนที่จะเป็นโมดูล HTMLParser ให้ตรวจสอบ htmllib มันมีอินเทอร์เฟซที่คล้ายกัน แต่ทำงานให้คุณได้มากกว่า (มันค่อนข้างเก่าดังนั้นมันจึงไม่ค่อยมีความช่วยเหลือในแง่ของการกำจัด javascript และ css คุณสามารถสร้างคลาสที่ได้รับ แต่และเพิ่มเมธอดที่มีชื่อเช่น start_script และ end_style (ดูเอกสาร python สำหรับรายละเอียด) แต่มันยาก เพื่อทำสิ่งนี้ได้อย่างน่าเชื่อถือสำหรับ HTML ที่มีรูปแบบไม่ถูกต้อง) อย่างไรก็ตามนี่เป็นสิ่งที่ง่ายที่พิมพ์ข้อความธรรมดาไปยังคอนโซล

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)

หมายเหตุ: HTMLError และ HTMLParserError ควรอ่าน HTMLParseError ใช้งานได้ แต่ทำงานได้ไม่ดีในการรักษาตัวแบ่งบรรทัด
Dave Knight

4

ฉันแนะนำแพ็คเกจ Python ที่เรียกว่า goose-extractor Goose จะพยายามดึงข้อมูลต่อไปนี้:

ข้อความหลักของบทความภาพหลักของบทความภาพยนตร์ใด ๆ ของ Youtube / Vimeo ที่ฝังอยู่ในบทความ Meta คำอธิบาย Meta tags tags

เพิ่มเติม: https://pypi.python.org/pypi/goose-extractor/


4

หากคุณต้องการความเร็วมากขึ้นและมีความแม่นยำน้อยลงคุณสามารถใช้ 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()

4

ติดตั้งhtml2textโดยใช้

pip ติดตั้ง html2text

แล้ว

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

ฉันรู้ว่ามีคำตอบมากมายที่นี่แล้ว แต่ฉันคิดว่าnewspaper3kก็สมควรได้รับการกล่าวถึงเช่นกัน เมื่อเร็ว ๆ นี้ฉันต้องการทำงานที่คล้ายกันในการแยกข้อความออกจากบทความบนเว็บและห้องสมุดนี้ทำงานได้อย่างยอดเยี่ยมในการบรรลุถึงสิ่งนี้ในการทดสอบของฉัน มันจะละเว้นข้อความที่พบในรายการเมนูและแถบด้านข้างรวมถึง JavaScript ใด ๆ ที่ปรากฏบนหน้าเว็บตามคำขอ OP

from newspaper import Article

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

หากคุณมีไฟล์ HTML ที่ดาวน์โหลดมาแล้วคุณสามารถทำสิ่งนี้ได้:

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

มันยังมีคุณสมบัติ NLP บางอย่างสำหรับการสรุปหัวข้อของบทความ:

article.nlp()
article.summary

3

ซุปที่สวยงามจะแปลงเอนทิตี html อาจเป็นทางออกที่ดีที่สุดของคุณเมื่อพิจารณาว่า HTML มักเป็นรถบั๊กกี้และเต็มไปด้วยปัญหาการเข้ารหัสแบบ unicode และ html นี่คือรหัสที่ฉันใช้ในการแปลง html เป็นข้อความดิบ:

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

อีกทางเลือกหนึ่งคือการเรียกใช้ html ผ่านเว็บเบราว์เซอร์ที่ใช้ข้อความและทิ้งมัน ตัวอย่างเช่น (ใช้ Lynx):

lynx -dump html_to_convert.html > converted_html.txt

สิ่งนี้สามารถทำได้ภายในสคริปต์ไพ ธ อนดังนี้:

import subprocess

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

มันจะไม่ให้คุณแค่ข้อความจากไฟล์ HTML แต่ขึ้นอยู่กับการใช้งานของคุณมันอาจจะดีกว่าที่จะออกของ html2text



2

โซลูชันอื่นที่ไม่ใช่ไพ ธ อน: Libre Office:

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

เหตุผลที่ฉันชอบอันนี้มากกว่าทางเลือกอื่น ๆ ก็คือทุกย่อหน้า HTML จะถูกแปลงเป็นบรรทัดข้อความเดียว (ไม่มีการแบ่งบรรทัด) ซึ่งเป็นสิ่งที่ฉันกำลังมองหา วิธีการอื่น ๆ ต้องการการโพสต์ คมสร้างผลผลิตที่ดี แต่ไม่ใช่สิ่งที่ฉันกำลังมองหา นอกจากนี้ Libre Office สามารถใช้ในการแปลงจากทุกรูปแบบ ...


2

ใครเคยลองbleach.clean(html,tags=[],strip=True)ใช้สารฟอกขาว ? มันใช้งานได้สำหรับฉัน


ดูเหมือนว่าจะทำงานให้ฉันด้วย แต่พวกเขาไม่แนะนำให้ใช้เพื่อจุดประสงค์นี้: "ฟังก์ชั่นนี้เป็นฟังก์ชั่นที่เน้นความปลอดภัยซึ่งมีจุดประสงค์เพียงอย่างเดียวคือการลบเนื้อหาที่เป็นอันตรายออกจากสตริง หน้า." -> bleach.readthedocs.io/en/latest/clean.html#bleach.clean
Loktopus

2

ฉันได้มีผลดีกับApache Tika โดยมีวัตถุประสงค์คือการแยกข้อมูลเมตาและข้อความจากเนื้อหาดังนั้นโปรแกรมแยกวิเคราะห์พื้นฐานจะถูกปรับตามความเหมาะสมนอกกรอบ

Tika สามารถทำงานเป็นเซิร์ฟเวอร์เป็นเล็ก ๆ น้อย ๆ ที่จะเรียกใช้ / การปรับใช้ในภาชนะหางและจากที่นั่นสามารถเข้าถึงได้ผ่านการผูกหลาม


1

ในวิธีที่ง่าย

import re

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

รหัสนี้จะค้นหาชิ้นส่วนทั้งหมดของ html_text ที่ขึ้นต้นด้วย '<' และลงท้ายด้วย '>' และแทนที่ทั้งหมดด้วยสตริงว่าง


1

@ คำตอบของ PeYoTIL โดยใช้ BeautifulSoup และกำจัดสไตล์และเนื้อหาของสคริปต์ไม่ได้ผลสำหรับฉัน ฉันลองใช้decomposeแทนextractแต่ก็ยังใช้งานไม่ได้ ดังนั้นฉันจึงสร้างของตัวเองซึ่งจัดรูปแบบข้อความโดยใช้<p>แท็กและแทนที่<a>แท็กด้วยลิงก์ href ยังเชื่อมโยงกับลิงก์ภายในข้อความด้วย มีอยู่ที่ส่วนสำคัญนี้ด้วยเอกสารทดสอบที่ฝัง

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
ขอบคุณคำตอบนี้มีการประเมินต่ำเกินไป สำหรับพวกเราที่ต้องการให้มีการแสดงข้อความที่สะอาดซึ่งมีลักษณะเหมือนเบราว์เซอร์มากขึ้น (ไม่สนใจบรรทัดใหม่และการพิจารณาย่อหน้าและการขึ้นบรรทัดใหม่เท่านั้น) BeautifulSoup get_textไม่ได้ตัดเลย
jrial

@jrial ดีใจที่คุณพบว่ามีประโยชน์ขอบคุณสำหรับ contrib สำหรับคนอื่น ๆ ส่วนสำคัญที่เชื่อมโยงได้รับการปรับปรุงให้ดีขึ้นเล็กน้อย สิ่งที่ OP ดูเหมือนจะหมายถึงคือเครื่องมือที่แสดงผล html เป็นข้อความเหมือนกับเบราว์เซอร์ที่ใช้ข้อความเช่น lynx นั่นคือสิ่งที่วิธีนี้พยายาม สิ่งที่คนส่วนใหญ่มีส่วนร่วมเป็นเพียงตัวแยกข้อความ
racitup

1

ใน Python 3.x คุณสามารถทำได้ง่ายๆด้วยการนำเข้าแพ็คเกจ 'imaplib' และ 'อีเมล' แม้ว่านี่จะเป็นโพสต์ที่เก่ากว่า แต่บางทีคำตอบของฉันสามารถช่วยผู้มาใหม่ในโพสต์นี้

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

ตอนนี้คุณสามารถพิมพ์ตัวแปรของร่างกายและมันจะอยู่ในรูปแบบข้อความธรรมดา :) ถ้ามันดีพอสำหรับคุณแล้วมันก็ดีที่จะเลือกมันเป็นคำตอบที่ได้รับการยอมรับ


มันไม่แปลงอะไรเลย
Antti Haapala

1
นี่แสดงให้คุณเห็นวิธีการแยกtext/plainบางส่วนจากอีเมลหากมีคนใส่ไว้ในนั้น มันไม่ได้ทำอะไรเลยในการแปลง HTML เป็นข้อความธรรมดาและไม่มีประโยชน์อะไรจากระยะไกลหากคุณพยายามแปลง HTML จากเว็บไซต์
tripleee

1

คุณสามารถแยกข้อความจาก HTML ด้วย 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)

1

ในขณะที่คนจำนวนมากพูดถึงการใช้ regex เพื่อตัดแท็ก html มีข้อเสียมากมาย

ตัวอย่างเช่น:

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

ควรแยกวิเคราะห์เป็น:

Hello world
I love you

นี่คือตัวอย่างข้อมูลที่ฉันคิดขึ้นมาคุณสามารถรวมเข้ากับความต้องการเฉพาะของคุณและทำงานได้อย่างมีเสน่ห์

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

1

อีกตัวอย่างหนึ่งที่ใช้ BeautifulSoup4 ใน Python 2.7.9+

รวมถึง:

import urllib2
from bs4 import BeautifulSoup

รหัส:

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'))

อธิบาย:

อ่านในข้อมูล url เป็น html (โดยใช้ BeautifulSoup) ลบองค์ประกอบสคริปต์และสไตล์ทั้งหมดและรับข้อความโดยใช้. get_text () เจาะเข้าไปในบรรทัดและลบช่องว่างนำหน้าและต่อท้ายแต่ละอันแล้วแยกหลายหัวข้อเป็นบรรทัดแต่ละชิ้น = (Phrasekstrip () สำหรับบรรทัดในบรรทัดสำหรับวลีใน line.split ("")) จากนั้นใช้ text = '\ n'.join, ปล่อยบรรทัดว่างเปล่า, ในที่สุดก็กลับมาตามทำนองคลองธรรม utf-8

หมายเหตุ:

  • บางระบบที่ใช้งานจะล้มเหลวด้วย https: // การเชื่อมต่อเนื่องจากปัญหา SSL คุณสามารถปิดการตรวจสอบเพื่อแก้ไขปัญหานั้น ตัวอย่างการแก้ไข: http://blog.pengyifan.com/how-to-fix-python-ssl-certificate_verify_failed/

  • Python <2.7.9 อาจมีปัญหาในการใช้งานบางอย่าง

  • text.encode ('utf-8') สามารถปล่อยการเข้ารหัสแปลก ๆ อาจต้องการคืนค่า str (text) แทน


0

นี่คือรหัสที่ฉันใช้เป็นประจำ

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

ฉันหวังว่าจะช่วย


0

ความคิดเห็นของผู้เขียน LibreOffice มีข้อดีเนื่องจากแอปพลิเคชันสามารถใช้ python macros ได้ ดูเหมือนว่าจะให้ประโยชน์หลายประการทั้งในการตอบคำถามนี้และเพิ่มฐานแมโครของ LibreOffice หากการแก้ปัญหานี้เป็นการใช้งานแบบครั้งเดียวแทนที่จะใช้เป็นส่วนหนึ่งของโปรแกรมการผลิตที่ยิ่งใหญ่ขึ้นให้เปิด HTML ในตัวเขียนและบันทึกหน้าเป็นข้อความดูเหมือนจะแก้ไขปัญหาที่กล่าวถึงที่นี่


0

วิธี Perl (แม่ขอโทษฉันจะไม่ทำในการผลิต)

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

นี่คือการปฏิบัติที่ไม่ดีด้วยเหตุผลมากมายเช่น&nbsp;
Uri Goren

ใช่ มันเป็นความจริง! อย่าทำมันเลย!
brunql
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.