Strip HTML dari string dengan Python

271
from mechanize import Browser
br = Browser()
br.open('http://somewebpage')
html = br.response().readlines()
for line in html:
  print line

Saat mencetak baris dalam file HTML, saya mencoba menemukan cara untuk hanya menampilkan konten dari setiap elemen HTML dan bukan formatnya sendiri. Jika ditemukan '<a href="whatever.com">some text</a>', itu hanya akan mencetak 'beberapa teks', '<b>hello</b>'mencetak 'halo', dll. Bagaimana cara melakukannya?

diarahkan
sumber
16
Pertimbangan penting adalah bagaimana menangani entitas HTML (misalnya &amp;). Anda dapat 1) menghapusnya bersama dengan tag (sering tidak diinginkan, dan tidak perlu karena mereka setara dengan teks biasa), 2) membiarkannya tidak berubah (solusi yang sesuai jika teks yang dilucuti akan segera kembali ke konteks HTML) atau 3 ) decode mereka ke teks biasa (jika teks yang dilucuti pergi ke database atau konteks non-HTML lainnya, atau jika kerangka web Anda secara otomatis melakukan pelolosan HTML teks untuk Anda).
Søren Løvborg
2
untuk @ SørenLøvborg poin 2): stackoverflow.com/questions/753052/…
Robert
2
Jawaban teratas di sini, yang digunakan oleh proyek Django hingga Maret 2014, telah ditemukan tidak aman terhadap skrip lintas situs - lihat tautan untuk contoh yang membuatnya berhasil. Saya sarankan menggunakan Bleach.clean (), striptags Markupsafe, atau strip_tags TERAKHIR Django.
rescdsk

Jawaban:

419

Saya selalu menggunakan fungsi ini untuk menghapus tag HTML, karena hanya membutuhkan stdlib Python:

Untuk Python 3:

from io import StringIO
from html.parser import HTMLParser

class MLStripper(HTMLParser):
    def __init__(self):
        super().__init__()
        self.reset()
        self.strict = False
        self.convert_charrefs= True
        self.text = StringIO()
    def handle_data(self, d):
        self.text.write(d)
    def get_data(self):
        return self.text.getvalue()

def strip_tags(html):
    s = MLStripper()
    s.feed(html)
    return s.get_data()

Untuk Python 2:

from HTMLParser import HTMLParser
from StringIO import StringIO

class MLStripper(HTMLParser):
    def __init__(self):
        self.reset()
        self.text = StringIO()
    def handle_data(self, d):
        self.text.write(d)
    def get_data(self):
        return self.text.getvalue()

def strip_tags(html):
    s = MLStripper()
    s.feed(html)
    return s.get_data()
Olivier Le Floch
sumber
3
Dua tahun kemudian, menghadapi masalah yang sama, dan ini adalah solusi yang jauh lebih elegan. Satu-satunya perubahan yang saya lakukan adalah mengembalikan self.fed sebagai daftar, daripada bergabung, sehingga saya bisa menelusuri isi elemen.
directedition
47
Perhatikan bahwa ini menghapus entitas HTML (misalnya &amp;) dan juga tag.
Søren Løvborg
30
@ surya Saya yakin Anda pernah melihat ini
tkone
8
Terima kasih atas jawabannya. Satu hal yang perlu diperhatikan bagi Anda yang menggunakan versi Python (3.2+) yang lebih baru adalah Anda harus memanggil __init__fungsi kelas induk . Lihat di sini: stackoverflow.com/questions/11061058/… .
pseudoramble
10
Untuk menjaga entitas html (dikonversi menjadi unicode), saya menambahkan dua baris: parser = HTMLParser()dan html = parser.unescape(html)ke awal fungsi strip_tags.
James Doepp - pihentagyu
157

Saya belum berpikir banyak tentang kasus-kasus yang akan hilang, tetapi Anda dapat melakukan regex sederhana:

re.sub('<[^<]+?>', '', text)

Bagi mereka yang tidak mengerti regex, ini mencari string <...>, di mana konten dalam terbuat dari satu atau lebih ( +) karakter yang bukan a <. The ?berarti bahwa itu akan cocok dengan string terkecil dapat ditemukan. Misalnya diberikan <p>Hello</p>, itu akan cocok <'p>dan </p>secara terpisah dengan ?. Tanpanya, itu akan cocok dengan seluruh string <..Hello..>.

Jika non-tag <muncul dalam html (mis. 2 < 3), Itu harus ditulis sebagai urutan escape &...sehingga ^<mungkin tidak perlu.

mmmdreg
sumber
10
Ini hampir persis bagaimana strip_tags Django melakukannya.
Bluu
10
Perhatikan bahwa ini membuat entitas HTML (mis. &amp;) Tidak berubah dalam output.
Søren Løvborg
36
Seseorang masih bisa menipu metode ini dengan sesuatu seperti ini: <script <script>> alert ("Hai!") <</script> / script>
19
JANGAN MELAKUKANNYA CARA INI! Seperti @Julio Garcia katakan, BUKANLAH AMAN!
rescdsk
18
Orang-orang, jangan bingung pengupasan HTML dan sanitasi HTML. Ya, untuk input yang rusak atau berbahaya jawaban ini dapat menghasilkan output dengan tag HTML di dalamnya. Ini masih merupakan pendekatan yang sangat valid untuk menghapus tag HTML. Namun , menghapus tag HTML bukan pengganti yang tepat untuk sanitasi HTML yang tepat. Aturannya tidak sulit: Setiap kali Anda memasukkan string teks biasa ke dalam output HTML, Anda harus selalu menghindarinya dari HTML (menggunakan cgi.escape(s, True)), bahkan jika Anda "tahu" bahwa itu tidak mengandung HTML (misalnya karena Anda menghapus konten HTML) . Namun, ini bukan yang ditanyakan OP.
Søren Løvborg
77

Anda dapat menggunakan get_text()fitur BeautifulSoup .

from bs4 import BeautifulSoup

html_str = '''
<td><a href="http://www.fakewebsite.com">Please can you strip me?</a>
<br/><a href="http://www.fakewebsite.com">I am waiting....</a>
</td>
'''
soup = BeautifulSoup(html_str)

print(soup.get_text()) 
#or via attribute of Soup Object: print(soup.text)

Dianjurkan untuk secara eksplisit menentukan parser , misalnya sebagai BeautifulSoup(html_str, features="html.parser"), untuk output yang akan direproduksi.

Aminah Nuraini
sumber
32

Versi pendek!

import re, cgi
tag_re = re.compile(r'(<!--.*?-->|<[^>]*>)')

# Remove well-formed tags, fixing mistakes by legitimate users
no_tags = tag_re.sub('', user_input)

# Clean up anything else by escaping
ready_for_web = cgi.escape(no_tags)

Sumber regex: MarkupSafe . Versi mereka menangani entitas HTML juga, sedangkan yang cepat ini tidak.

Mengapa saya tidak bisa menghapus tag saja dan membiarkannya?

Adalah satu hal untuk menjauhkan orang dari <i>italicizing</i>berbagai hal, tanpa meninggalkan iyang mengambang. Tapi mengambil input sewenang-wenang dan membuatnya sama sekali tidak berbahaya. Sebagian besar teknik pada halaman ini akan membuat hal-hal seperti komentar tertutup ( <!--) dan kurung sudut yang bukan bagian dari tag ( blah <<<><blah) tetap utuh. Versi HTMLParser bahkan dapat meninggalkan tag lengkap, jika ada di dalam komentar tidak tertutup.

Bagaimana jika templat Anda {{ firstname }} {{ lastname }}? firstname = '<a'dan lastname = 'href="http://evil.com/">'akan diizinkan masuk oleh setiap stripper tag pada halaman ini (kecuali @Medeiros!), karena mereka tidak menyelesaikan tag sendiri. Menghapus tag HTML normal tidak cukup.

Django strip_tags, versi yang lebih baik (lihat tajuk berikutnya) dari jawaban atas pertanyaan ini, memberikan peringatan berikut:

Sama sekali TIDAK ADA jaminan yang diberikan tentang string yang dihasilkan menjadi HTML aman. Jadi, JANGAN PERNAH tandai hasil strip_tagspanggilan tanpa melarikan diri terlebih dahulu, misalnya dengan escape().

Ikuti saran mereka!

Untuk menghapus tag dengan HTMLParser, Anda harus menjalankannya beberapa kali.

Sangat mudah untuk mengelak dari jawaban teratas untuk pertanyaan ini.

Lihat string ini ( sumber dan diskusi ):

<img<!-- --> src=x onerror=alert(1);//><!-- -->

Pertama kali HTMLParser melihatnya, ia tidak bisa memastikan bahwa itu <img...>adalah sebuah tag. Itu terlihat rusak, jadi HTMLParser tidak menghilangkannya. Hanya mengeluarkan <!-- comments -->, meninggalkan Anda dengan

<img src=x onerror=alert(1);//>

Masalah ini diungkapkan kepada proyek Django pada bulan Maret 2014. Yang lama strip_tagspada dasarnya sama dengan jawaban teratas untuk pertanyaan ini. Versi baru mereka pada dasarnya menjalankannya dalam satu lingkaran sampai menjalankannya lagi tidak mengubah string:

# _strip_once runs HTMLParser once, pulling out just the text of all the nodes.

def strip_tags(value):
    """Returns the given HTML with all tags stripped."""
    # Note: in typical case this loop executes _strip_once once. Loop condition
    # is redundant, but helps to reduce number of executions of _strip_once.
    while '<' in value and '>' in value:
        new_value = _strip_once(value)
        if len(new_value) >= len(value):
            # _strip_once was not able to detect more tags
            break
        value = new_value
    return value

Tentu saja, semua ini bukan masalah jika Anda selalu luput dari hasilnya strip_tags().

Pembaruan 19 Maret 2015 : Ada bug dalam versi Django sebelum 1.4.20, 1.6.11, 1.7.7, dan 1.8c1. Versi ini dapat memasukkan infinite loop dalam fungsi strip_tags (). Versi tetap direproduksi di atas. Lebih detail di sini .

Hal-hal baik untuk disalin atau digunakan

Kode contoh saya tidak menangani entitas HTML - versi paket Django dan MarkupSafe lakukan.

Contoh kode saya diambil dari perpustakaan MarkupSafe yang sangat baik untuk pencegahan skrip lintas situs. Lebih mudah dan cepat (dengan speedup C ke versi Python aslinya). Ini termasuk dalam Google App Engine , dan digunakan oleh Jinja2 (2.7 dan lebih tinggi) , Mako, Pylons, dan banyak lagi. Ini bekerja dengan mudah dengan template Django dari Django 1.7.

Strip_tags Django dan utilitas html lainnya dari versi terbaru bagus, tapi saya merasa mereka kurang nyaman daripada MarkupSafe. Mereka cukup mandiri, Anda dapat menyalin apa yang Anda butuhkan dari file ini .

Jika Anda perlu menghapus hampir semua tag, perpustakaan Bleach bagus. Anda dapat menerapkannya seperti "pengguna saya dapat membuat huruf miring, tetapi mereka tidak bisa membuat iframe."

Pahami properti stripper tag Anda! Jalankan tes fuzz di atasnya! Berikut adalah kode yang saya gunakan untuk melakukan penelitian untuk jawaban ini.

catatan lamban - Pertanyaannya sendiri adalah tentang mencetak ke konsol, tapi ini adalah hasil Google teratas untuk "python strip html dari string", jadi itu sebabnya jawaban ini 99% tentang web.

rescdsk
sumber
Kode contoh "pengganti baris terakhir" saya tidak menangani entitas html - seberapa buruk itu?
rescdsk
Saya hanya mengurai sebagian kecil html tanpa tag khusus, dan versi singkat Anda melakukan pekerjaan dengan sangat baik. Terima kasih telah berbagi!
tbolender
31

Saya membutuhkan cara untuk menghapus tag dan mendekode entitas HTML menjadi teks biasa. Solusi berikut ini didasarkan pada jawaban Eloff (yang tidak bisa saya gunakan karena menghapus entitas).

from HTMLParser import HTMLParser
import htmlentitydefs

class HTMLTextExtractor(HTMLParser):
    def __init__(self):
        HTMLParser.__init__(self)
        self.result = [ ]

    def handle_data(self, d):
        self.result.append(d)

    def handle_charref(self, number):
        codepoint = int(number[1:], 16) if number[0] in (u'x', u'X') else int(number)
        self.result.append(unichr(codepoint))

    def handle_entityref(self, name):
        codepoint = htmlentitydefs.name2codepoint[name]
        self.result.append(unichr(codepoint))

    def get_text(self):
        return u''.join(self.result)

def html_to_text(html):
    s = HTMLTextExtractor()
    s.feed(html)
    return s.get_text()

Tes cepat:

html = u'<a href="#">Demo <em>(&not; \u0394&#x03b7;&#956;&#x03CE;)</em></a>'
print repr(html_to_text(html))

Hasil:

u'Demo (\xac \u0394\u03b7\u03bc\u03ce)'

Menangani kesalahan:

  • Struktur HTML yang tidak valid dapat menyebabkan HTMLParseError .
  • Entitas bernama HTML yang tidak valid (seperti &#apos;, yang valid dalam XML dan XHTML, tetapi tidak HTML biasa) akan menyebabkan ValueErrorpengecualian.
  • Entitas HTML numerik yang menetapkan titik kode di luar rentang Unicode yang dapat diterima oleh Python (seperti, pada beberapa sistem, karakter di luar Basic Multilingual Plane ) akan menyebabkan ValueErrorpengecualian.

Catatan keamanan: Jangan bingung stripping HTML (mengubah HTML menjadi teks biasa) dengan sanitasi HTML (mengubah teks biasa menjadi HTML). Jawaban ini akan menghapus HTML dan mendekode entitas ke dalam teks biasa - yang tidak membuat hasilnya aman untuk digunakan dalam konteks HTML.

Contoh: &lt;script&gt;alert("Hello");&lt;/script&gt;akan dikonversi menjadi <script>alert("Hello");</script>, yang merupakan perilaku 100% benar, tetapi jelas tidak cukup jika teks biasa yang dihasilkan dimasukkan apa adanya ke halaman HTML.

Aturannya tidak sulit: Setiap kali Anda memasukkan string teks biasa ke dalam output HTML, Anda harus selalu menghindarinya dari HTML (menggunakan cgi.escape(s, True)), bahkan jika Anda "tahu" bahwa itu tidak mengandung HTML (misalnya karena Anda menghapus konten HTML) .

(Namun, OP bertanya tentang mencetak hasilnya ke konsol, dalam hal ini tidak diperlukan pelolosan HTML.)

Versi Python 3.4+: (dengan doctest!)

import html.parser

class HTMLTextExtractor(html.parser.HTMLParser):
    def __init__(self):
        super(HTMLTextExtractor, self).__init__()
        self.result = [ ]

    def handle_data(self, d):
        self.result.append(d)

    def get_text(self):
        return ''.join(self.result)

def html_to_text(html):
    """Converts HTML to plain text (stripping tags and converting entities).
    >>> html_to_text('<a href="#">Demo<!--...--> <em>(&not; \u0394&#x03b7;&#956;&#x03CE;)</em></a>')
    'Demo (\xac \u0394\u03b7\u03bc\u03ce)'

    "Plain text" doesn't mean result can safely be used as-is in HTML.
    >>> html_to_text('&lt;script&gt;alert("Hello");&lt;/script&gt;')
    '<script>alert("Hello");</script>'

    Always use html.escape to sanitize text before using in an HTML context!

    HTMLParser will do its best to make sense of invalid HTML.
    >>> html_to_text('x < y &lt z <!--b')
    'x < y < z '

    Unrecognized named entities are included as-is. '&apos;' is recognized,
    despite being XML only.
    >>> html_to_text('&nosuchentity; &apos; ')
    "&nosuchentity; ' "
    """
    s = HTMLTextExtractor()
    s.feed(html)
    return s.get_text()

Perhatikan bahwa HTMLParser telah meningkat dalam Python 3 (artinya lebih sedikit kode dan penanganan kesalahan yang lebih baik).

Søren Løvborg
sumber
18

Ada cara sederhana untuk ini:

def remove_html_markup(s):
    tag = False
    quote = False
    out = ""

    for c in s:
            if c == '<' and not quote:
                tag = True
            elif c == '>' and not quote:
                tag = False
            elif (c == '"' or c == "'") and tag:
                quote = not quote
            elif not tag:
                out = out + c

    return out

Idenya dijelaskan di sini: http://youtu.be/2tu9LTDujbw

Anda dapat melihatnya berfungsi di sini: http://youtu.be/HPkNPcYed9M?t=35s

PS - Jika Anda tertarik pada kelas (tentang debugging pintar dengan python) saya beri Anda tautan: http://www.udacity.com/overview/Course/cs259/CourseRev/1 . Gratis!

Sama-sama! :)

Medeiros
sumber
2
Saya bertanya-tanya mengapa jawaban ini baru saja diturunkan. Ini adalah cara sederhana untuk menyelesaikan masalah tanpa lib. Hanya python murni dan berfungsi seperti yang ditunjukkan oleh tautan.
Medeiros
2
Mungkin orang lebih suka lib untuk memberi mereka keamanan. Saya menguji Anda kode dan lulus, dan saya selalu lebih suka kode kecil yang saya mengerti daripada menggunakan lib dan dengan asumsi bahwa itu tidak apa-apa sampai bug muncul. Bagi saya itulah yang saya cari dan sekali lagi terima kasih. Mengenai downvotes, jangan masuk ke pola pikir itu. Orang-orang di sini harus peduli dengan kualitas dan bukan suara. Akhir-akhir ini SO telah menjadi tempat di mana semua orang menginginkan poin dan bukan pengetahuan.
Jimmy Kane
2
Masalah dengan solusi ini adalah penanganan kesalahan. Misalnya jika Anda memberikan <b class="o'>x</b>output fungsi input x. Namun sebenarnya input ini tidak valid. Saya pikir itu sebabnya orang lebih suka lib.
laltin
1
Ia bekerja dengan input itu juga. Baru diuji. Sadarilah bahwa di dalam perpustakaan itu Anda akan menemukan kode yang sama. Itu tidak terlalu pythonic, saya tahu. Sepertinya kode C atau Java. Saya pikir ini efisien dan dapat dengan mudah dipindahkan ke bahasa lain.
Medeiros
1
Sederhana, Pythonic dan tampaknya berfungsi dengan baik atau lebih baik daripada metode lain yang dibahas. Mungkin saja itu tidak akan berfungsi untuk beberapa HTML yang tidak terbentuk dengan baik tetapi tidak ada cara mengatasinya.
denson
16

Jika Anda perlu mempertahankan entitas HTML (yaitu &amp;), saya menambahkan metode "handle_entityref" ke jawaban Eloff .

from HTMLParser import HTMLParser

class MLStripper(HTMLParser):
    def __init__(self):
        self.reset()
        self.fed = []
    def handle_data(self, d):
        self.fed.append(d)
    def handle_entityref(self, name):
        self.fed.append('&%s;' % name)
    def get_data(self):
        return ''.join(self.fed)

def html_to_text(html):
    s = MLStripper()
    s.feed(html)
    return s.get_data()
Robert
sumber
13

Jika Anda ingin menghapus semua tag HTML, cara termudah yang saya temukan adalah menggunakan BeautifulSoup:

from bs4 import BeautifulSoup  # Or from BeautifulSoup import BeautifulSoup

def stripHtmlTags(htmlTxt):
    if htmlTxt is None:
            return None
        else:
            return ''.join(BeautifulSoup(htmlTxt).findAll(text=True)) 

Saya mencoba kode jawaban yang diterima tetapi saya mendapatkan "RuntimeError: kedalaman rekursi maksimum terlampaui", yang tidak terjadi dengan blok kode di atas.

Vasilis
sumber
1
Saya baru saja mencoba metode Anda karena tampaknya lebih bersih, berhasil, semacam ... itu tidak menghapus tag input!
kustomrtr
Saya menemukan bahwa aplikasi sederhana dari BeautifulSoup memiliki masalah dengan spasi putih: ''.join(BeautifulSoup('<em>he</em>llo<br>world').find_all(text=True)). Di sini outputnya adalah "helloworld", sementara Anda mungkin menginginkannya menjadi "halo dunia". ' '.join(BeautifulSoup('<em>he</em>llo<br>world').find_all(text=True))tidak membantu karena menjadi "dia llo dunia".
Finn Årup Nielsen
@ customrtr, maaf ketidaktahuan saya, apa yang saya masukkan ke dalam argumen diri? NameError: name 'self' tidak didefinisikan
Ian_De_Oliveira
@Ian_De_Oliveira Anda dapat menghapusnya, saya berasumsi itu ada di dalam kelas tetapi tidak diperlukan. Saya juga mengedit jawaban untuk menghapusnya
Vasilis
@Ian_De_Oliveira Anda dapat menghapusnya, saya berasumsi itu ada di dalam kelas tetapi tidak diperlukan. Saya juga mengedit jawaban untuk menghapusnya
Vasilis
10

Berikut adalah solusi sederhana yang menghapus tag HTML dan menerjemahkan entitas HTML berdasarkan lxmlpustaka yang luar biasa cepat :

from lxml import html

def strip_html(s):
    return str(html.fromstring(s).text_content())

strip_html('Ein <a href="">sch&ouml;ner</a> Text.')  # Output: Ein schöner Text.
Robin Dinse
sumber
3
Pada tahun 2020, ini adalah cara tercepat dan terbaik untuk menghapus konten HTML. Ditambah bonus penanganan decoding. Bagus untuk deteksi bahasa!
dfabiano
text_content()kembali lxml.etree._ElementUnicodeResultsehingga Anda mungkin harus melemparkannya ke string terlebih dahulu
Suzana
1
@ Suzana Poin bagus. Tampaknya mendapatkan auto-casted to struntuk operasi string seperti +dan pengindeksan []. Menambahkan gips untuk ukuran yang baik bagaimanapun.
Robin Dinse
9

Sebuah lxml.html solusi berbasis (lxml adalah perpustakaan asli dan karena itu jauh lebih cepat daripada solusi python murni).

from lxml import html
from lxml.html.clean import clean_html

tree = html.fromstring("""<span class="item-summary">
                            Detailed answers to any questions you might have
                        </span>""")

print(clean_html(tree).strip())

# >>> Detailed answers to any questions you might have

Juga lihat http://lxml.de/lxmlhtml.html#cleaning-up-html untuk apa sebenarnya yang dilakukan lxml.cleaner.

Jika Anda memerlukan kontrol lebih besar atas apa sebenarnya yang disanitasi sebelum mengonversi ke teks maka Anda mungkin ingin menggunakan Pembersih lxml secara eksplisit dengan meneruskan opsi yang Anda inginkan dalam konstruktor, misalnya:

cleaner = Cleaner(page_structure=True,
                  meta=True,
                  embedded=True,
                  links=True,
                  style=True,
                  processing_instructions=True,
                  inline_style=True,
                  scripts=True,
                  javascript=True,
                  comments=True,
                  frames=True,
                  forms=True,
                  annoying_tags=True,
                  remove_unknown_tags=True,
                  safe_attrs_only=True,
                  safe_attrs=frozenset(['src','color', 'href', 'title', 'class', 'name', 'id']),
                  remove_tags=('span', 'font', 'div')
                  )
sanitized_html = cleaner.clean_html(unsafe_html)
ccpizza
sumber
1
Saya mendapat AttributeError: objek 'HtmlElement' tidak memiliki atribut 'strip'
aris
7

Paket Beautiful Soup segera melakukan ini untuk Anda.

from bs4 import BeautifulSoup

soup = BeautifulSoup(html)
text = soup.get_text()
print(text)
runawaykid
sumber
3
Dari antrian ulasan: Boleh saya minta Anda untuk menambahkan beberapa konteks di sekitar jawaban Anda. Jawaban khusus kode sulit dipahami. Ini akan membantu penanya dan pembaca di masa mendatang jika Anda dapat menambahkan lebih banyak informasi dalam posting Anda.
help-info.de
2

Inilah solusi saya untuk python 3.

import html
import re

def html_to_txt(html_text):
    ## unescape html
    txt = html.unescape(html_text)
    tags = re.findall("<[^>]+>",txt)
    print("found tags: ")
    print(tags)
    for tag in tags:
        txt=txt.replace(tag,'')
    return txt

Tidak yakin apakah itu sempurna, tetapi memecahkan kasus penggunaan saya dan tampaknya sederhana.

John Loutzenhiser
sumber
2

Anda dapat menggunakan parser HTML yang berbeda ( seperti lxml , atau Beautiful Soup ) - yang menawarkan fungsi untuk mengekstrak hanya teks. Atau, Anda dapat menjalankan regex pada string baris Anda yang menghapus tag. Lihat dokumen Python untuk lebih lanjut.

Jason Coon
sumber
1
tautan amk sudah mati. Punya alternatif?
2
Situs web Python memiliki cara kerja yang baik sekarang, di sini adalah regex cara-cara: docs.python.org/howto/regex
Jason Coon
5
Dalam lxml:lxml.html.fromstring(s).text_content()
Bluu
1
Contoh Bluu dengan lxml menerjemahkan entitas HTML (misalnya &amp;) ke teks.
Søren Løvborg
1

Saya telah menggunakan jawaban Eloff dengan sukses untuk Python 3.1 [terima kasih banyak!].

Saya memutakhirkan ke Python 3.2.3, dan mengalami kesalahan.

Solusinya, yang disediakan di sini berkat responden Thomas K, adalah dengan memasukkan super().__init__()kode berikut:

def __init__(self):
    self.reset()
    self.fed = []

... agar terlihat seperti ini:

def __init__(self):
    super().__init__()
    self.reset()
    self.fed = []

... dan itu akan berfungsi untuk Python 3.2.3.

Sekali lagi, terima kasih kepada Thomas K untuk perbaikan dan untuk kode asli Eloff yang disediakan di atas!

MilesNielsen
sumber
1

Anda dapat menulis fungsi Anda sendiri:

def StripTags(text):
     finished = 0
     while not finished:
         finished = 1
         start = text.find("<")
         if start >= 0:
             stop = text[start:].find(">")
             if stop >= 0:
                 text = text[:start] + text[start+stop+1:]
                 finished = 0
     return text
Yuda Prawira
sumber
1
Apakah menambahkan string membuat salinan string yang baru?
Jeremy L
1
@Nerdling - Ya, memang, yang dapat menyebabkan beberapa inefisiensi yang agak mengesankan dalam fungsi yang sering digunakan (atau, dalam hal ini, fungsi yang jarang digunakan yang bekerja pada gumpalan teks besar.) Lihat halaman ini untuk detailnya. : D
Jeremy Sandell
Apakah ini menguji terhadap string yang dikutip? Tidak.
Jimmy Kane
1

Solusi dengan HTML-Parser dapat dipecahkan, jika dijalankan hanya sekali:

html_to_text('<<b>script>alert("hacked")<</b>/script>

menghasilkan:

<script>alert("hacked")</script>

apa yang ingin Anda cegah. jika Anda menggunakan HTML-Parser, hitung Tag sampai nol diganti:

from HTMLParser import HTMLParser

class MLStripper(HTMLParser):
    def __init__(self):
        self.reset()
        self.fed = []
        self.containstags = False

    def handle_starttag(self, tag, attrs):
       self.containstags = True

    def handle_data(self, d):
        self.fed.append(d)

    def has_tags(self):
        return self.containstags

    def get_data(self):
        return ''.join(self.fed)

def strip_tags(html):
    must_filtered = True
    while ( must_filtered ):
        s = MLStripper()
        s.feed(html)
        html = s.get_data()
        must_filtered = s.has_tags()
    return html
Falk Nisius
sumber
1
Jika Anda memanggil fungsi yang dipanggil html_to_textdan Anda menanamkan teks yang menjadi output dari fungsi itu di dalam html tanpa melarikan diri teks itu, maka itu adalah kurangnya melarikan diri, yang merupakan kerentanan keamanan, bukan html_to_textfungsi. The html_to_textFungsi pernah menjanjikan Anda output akan teks. Dan memasukkan teks ke dalam html tanpa melarikan diri adalah potensi kerentanan keamanan terlepas dari apakah Anda mendapatkan teks dari html_to_text atau sumber lain.
kasperd
Anda benar dalam kasus ini, karena tidak dapat melarikan diri, tetapi pertanyaannya adalah untuk menghapus html dari string yang diberikan untuk tidak keluar dari string yang diberikan. Jika jawaban sebelumnya membangun html baru dengan solusi mereka sebagai hasil dari menghapus beberapa html, maka penggunaan solusi ini berbahaya.
Falk Nisius
1

Ini adalah perbaikan cepat dan bahkan dapat lebih dioptimalkan tetapi akan berfungsi dengan baik. Kode ini akan mengganti semua tag yang tidak kosong dengan "" dan menghapus semua tag html dari teks input yang diberikan. Anda dapat menjalankannya menggunakan ./file.py input output

    #!/usr/bin/python
import sys

def replace(strng,replaceText):
    rpl = 0
    while rpl > -1:
        rpl = strng.find(replaceText)
        if rpl != -1:
            strng = strng[0:rpl] + strng[rpl + len(replaceText):]
    return strng


lessThanPos = -1
count = 0
listOf = []

try:
    #write File
    writeto = open(sys.argv[2],'w')

    #read file and store it in list
    f = open(sys.argv[1],'r')
    for readLine in f.readlines():
        listOf.append(readLine)         
    f.close()

    #remove all tags  
    for line in listOf:
        count = 0;  
        lessThanPos = -1  
        lineTemp =  line

            for char in lineTemp:

            if char == "<":
                lessThanPos = count
            if char == ">":
                if lessThanPos > -1:
                    if line[lessThanPos:count + 1] != '<>':
                        lineTemp = replace(lineTemp,line[lessThanPos:count + 1])
                        lessThanPos = -1
            count = count + 1
        lineTemp = lineTemp.replace("&lt","<")
        lineTemp = lineTemp.replace("&gt",">")                  
        writeto.write(lineTemp)  
    writeto.close() 
    print "Write To --- >" , sys.argv[2]
except:
    print "Help: invalid arguments or exception"
    print "Usage : ",sys.argv[0]," inputfile outputfile"
kiran Mohan
sumber
1

A python 3 adaptasi dari jawaban søren-løvborg

from html.parser import HTMLParser
from html.entities import html5

class HTMLTextExtractor(HTMLParser):
    """ Adaption of http://stackoverflow.com/a/7778368/196732 """
    def __init__(self):
        super().__init__()
        self.result = []

    def handle_data(self, d):
        self.result.append(d)

    def handle_charref(self, number):
        codepoint = int(number[1:], 16) if number[0] in (u'x', u'X') else int(number)
        self.result.append(unichr(codepoint))

    def handle_entityref(self, name):
        if name in html5:
            self.result.append(unichr(html5[name]))

    def get_text(self):
        return u''.join(self.result)

def html_to_text(html):
    s = HTMLTextExtractor()
    s.feed(html)
    return s.get_text()
CpILL
sumber
1

Untuk satu proyek, saya perlu strip HTML, tetapi juga css dan js. Jadi, saya membuat variasi dari jawaban Eloff:

class MLStripper(HTMLParser):
    def __init__(self):
        self.reset()
        self.strict = False
        self.convert_charrefs= True
        self.fed = []
        self.css = False
    def handle_starttag(self, tag, attrs):
        if tag == "style" or tag=="script":
            self.css = True
    def handle_endtag(self, tag):
        if tag=="style" or tag=="script":
            self.css=False
    def handle_data(self, d):
        if not self.css:
            self.fed.append(d)
    def get_data(self):
        return ''.join(self.fed)

def strip_tags(html):
    s = MLStripper()
    s.feed(html)
    return s.get_data()
kail tikus
sumber
1

Berikut adalah solusi yang mirip dengan jawaban yang saat ini diterima ( https://stackoverflow.com/a/925630/95989 ), kecuali bahwa ia menggunakan HTMLParserkelas internal secara langsung (yaitu tidak ada subklasifikasi), sehingga membuatnya secara signifikan lebih singkat:

def strip_html (teks):
    bagian = []                                                                      
    parser = HTMLParser ()                                                           
    parser.handle_data = parts.append                                               
    parser.feed (teks)                                                               
    return '' .join (bagian)
Richard
sumber
0

Saya parsing readmes Github dan saya menemukan bahwa berikut ini benar-benar berfungsi dengan baik:

import re
import lxml.html

def strip_markdown(x):
    links_sub = re.sub(r'\[(.+)\]\([^\)]+\)', r'\1', x)
    bold_sub = re.sub(r'\*\*([^*]+)\*\*', r'\1', links_sub)
    emph_sub = re.sub(r'\*([^*]+)\*', r'\1', bold_sub)
    return emph_sub

def strip_html(x):
    return lxml.html.fromstring(x).text_content() if x else ''

Lalu

readme = """<img src="https://raw.githubusercontent.com/kootenpv/sky/master/resources/skylogo.png" />

            sky is a web scraping framework, implemented with the latest python versions in mind (3.4+). 
            It uses the asynchronous `asyncio` framework, as well as many popular modules 
            and extensions.

            Most importantly, it aims for **next generation** web crawling where machine intelligence 
            is used to speed up the development/maintainance/reliability of crawling.

            It mainly does this by considering the user to be interested in content 
            from *domains*, not just a collection of *single pages*
            ([templating approach](#templating-approach))."""

strip_markdown(strip_html(readme))

Hapus semua penurunan harga dan html dengan benar.

PascalVKooten
sumber
0

Menggunakan BeautifulSoup, html2text atau kode dari @ Eloff, sebagian besar waktu, itu tetap beberapa elemen html, kode javascript ...

Jadi, Anda dapat menggunakan kombinasi dari pustaka ini dan menghapus pemformatan penurunan harga (Python 3):

import re
import html2text
from bs4 import BeautifulSoup
def html2Text(html):
    def removeMarkdown(text):
        for current in ["^[ #*]{2,30}", "^[ ]{0,30}\d\\\.", "^[ ]{0,30}\d\."]:
            markdown = re.compile(current, flags=re.MULTILINE)
            text = markdown.sub(" ", text)
        return text
    def removeAngular(text):
        angular = re.compile("[{][|].{2,40}[|][}]|[{][*].{2,40}[*][}]|[{][{].{2,40}[}][}]|\[\[.{2,40}\]\]")
        text = angular.sub(" ", text)
        return text
    h = html2text.HTML2Text()
    h.images_to_alt = True
    h.ignore_links = True
    h.ignore_emphasis = False
    h.skip_internal_links = True
    text = h.handle(html)
    soup = BeautifulSoup(text, "html.parser")
    text = soup.text
    text = removeAngular(text)
    text = removeMarkdown(text)
    return text

Ini bekerja dengan baik untuk saya tetapi dapat ditingkatkan, tentu saja ...

hayj
sumber
0

Kode sederhana !. Ini akan menghapus semua jenis tag dan konten di dalamnya.

def rm(s):
    start=False
    end=False
    s=' '+s
    for i in range(len(s)-1):
        if i<len(s):
            if start!=False:
                if s[i]=='>':
                    end=i
                    s=s[:start]+s[end+1:]
                    start=end=False
            else:
                if s[i]=='<':
                    start=i
    if s.count('<')>0:
        self.rm(s)
    else:
        s=s.replace('&nbsp;', ' ')
        return s

Tapi itu tidak akan memberikan hasil penuh jika teks berisi <> simbol di dalamnya.

Vanjith
sumber
0
# This is a regex solution.
import re
def removeHtml(html):
  if not html: return html
  # Remove comments first
  innerText = re.compile('<!--[\s\S]*?-->').sub('',html)
  while innerText.find('>')>=0: # Loop through nested Tags
    text = re.compile('<[^<>]+?>').sub('',innerText)
    if text == innerText:
      break
    innerText = text

  return innerText.strip()
dabingsou
sumber
-2

Metode ini berfungsi dengan sempurna untuk saya dan tidak memerlukan instalasi tambahan:

import re
import htmlentitydefs

def convertentity(m):
    if m.group(1)=='#':
        try:
            return unichr(int(m.group(2)))
        except ValueError:
            return '&#%s;' % m.group(2)
        try:
            return htmlentitydefs.entitydefs[m.group(2)]
        except KeyError:
            return '&%s;' % m.group(2)

def converthtml(s):
    return re.sub(r'&(#?)(.+?);',convertentity,s)

html =  converthtml(html)
html.replace("&nbsp;", " ") ## Get rid of the remnants of certain formatting(subscript,superscript,etc).
John
sumber
3
Ini menerjemahkan entitas HTML ke teks biasa, tetapi jelas tidak benar-benar menghapus tag apa pun, yang merupakan pertanyaan asli. (Juga, blok coba-kecuali yang kedua perlu di-indentasi agar kode dapat melakukan hal yang sama).
Søren Løvborg