Python'daki dizelerden HTML'yi çıkarın


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

Bir HTML dosyasında bir satır yazdırırken, biçimlendirmenin kendisini değil, yalnızca her HTML öğesinin içeriğini göstermenin bir yolunu bulmaya çalışıyorum. Bulursa '<a href="whatever.com">some text</a>', sadece 'bazı metinler' '<b>hello</b>'basar, 'merhaba' yazdırır, vb. Bunu nasıl yaparsınız?


16
Önemli bir husus HTML varlıklarının nasıl ele alınacağıdır (örn. &amp;). Bunları 1) etiketlerle birlikte kaldırabilirsiniz (genellikle istenmeyen ve düz metne eşdeğer oldukları için gereksiz), 2) bunları değiştirmeden bırakın (soyulmuş metin HTML içeriğine geri dönüyorsa uygun bir çözüm) veya 3 ) bunları düz metin olarak çözün (soyulmuş metin bir veritabanına veya HTML olmayan başka bir içeriğe gidiyorsa veya web çerçeveniz sizin için metinden HTML çıkışını otomatik olarak gerçekleştiriyorsa).
Søren Løvborg

2
@ SørenLøvborg noktası 2 için): stackoverflow.com/questions/753052/…
Robert

2
Django projesi tarafından Mart 2014'e kadar kullanılan en iyi cevabın siteler arası kodlamaya karşı güvensiz olduğu bulundu - bunu yapan bir örnek için bu bağlantıya bakın. Bleach.clean (), Markupsafe's striptags veya RECENT Django strip_tags kullanmanızı tavsiye ederim.
rescdsk

Yanıtlar:


419

Her zaman sadece Python stdlib gerektirdiği için HTML etiketlerini şeritlemek için bu işlevi kullandım:

Python 3 için:

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

Python 2 için:

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

3
İki yıl + sonra, aynı sorunla karşı karşıya ve bu çok daha zarif bir çözüm. Yaptığım tek şey self.fed'e katılmak yerine bir liste olarak geri dönmekti, bu yüzden öğe içeriğinden geçebildim.
yönetmenlik

47
Bunun HTML öğelerini (ör. &amp;) Ve etiketlerini içerdiğini unutmayın .
Søren Løvborg

30
@surya Eminim bunu gördünüz
tkone

8
Harika cevap için teşekkürler. Python'un (3.2+) daha yeni sürümlerini kullananlarınız için dikkat etmeniz gereken bir şey, ana sınıfın __init__işlevini çağırmanız gerektiğidir . Buraya bakın: stackoverflow.com/questions/11061058/… .
Ağustos'ta

10
Html varlıklarını (unicode'a dönüştürülmüş) tutmak için iki satır ekledim: parser = HTMLParser()ve html = parser.unescape(html)strip_tags işlevinin başına.
James Doepp - pihentagyu

156

Özleyeceği durumlar hakkında fazla düşünmedim, ancak basit bir normal ifade yapabilirsiniz:

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

Normal ifadeyi anlamayanlar için bu <...>, iç içeriğin a olmayan bir veya daha fazla ( +) karakterden oluştuğu bir dize arar <. ?Araçlar o bulabilirsiniz en küçük dize maç olacak. Örneğin verilen <p>Hello</p>, ile eşleşir <'p>ve </p>ayrı ayrı ?. Bu olmadan, tüm dizeyle eşleşir <..Hello..>.

Olmayan etiket Eğer <html içinde görünür (örn. 2 < 3), Bir çıkış sırası olarak yazılmalıdır &...zaten bu yüzden ^<gereksiz olabilir.


10
Neredeyse Django'nun strip_tag'ları bunu yapıyor.
Bluu

10
Bunun, HTML varlıklarını (örn. &amp;) Çıktıda değiştirmeden bıraktığını unutmayın .
Søren Løvborg

36
Bu yöntemi hala böyle bir şeyle kandırabilirsiniz: <script <script>> alert ("Hi!") <</script> / script>

19
BU ŞEKİLDE YAPMAYIN! @Julio Garcia'nın dediği gibi, bu GÜVENLİ DEĞİL!
rescdsk

18
İnsanlar, HTML sıyırma ve HTML sanitizing karıştırmayın. Evet, bozuk veya kötü amaçlı girdi için bu yanıt, içinde HTML etiketleri bulunan çıktılar üretebilir. HTML etiketlerini çıkartmak için hala geçerli bir yaklaşımdır. Bununla birlikte , HTML etiketlerini çıkarmak, uygun HTML temizliğinin geçerli bir yerine geçmez. Kural zor değildir: Her zaman HTML çıktısı içine bir düz metin dizesi eklemek size gereken her zaman HTML (kullanarak kaçmak cgi.escape(s, True)HTML içermediğini "bilmek" bile) (örn HTML içeriği elimden çünkü) . Ancak, OP bu konuda sordu.
Søren Løvborg

76

BeautifulSoup get_text()özelliğini kullanabilirsiniz .

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)

Ayrıştırıcının , örneğin BeautifulSoup(html_str, features="html.parser")çıktının tekrar üretilebilir olması için açıkça belirtilmesi tavsiye edilir .


32

Kısa versiyon!

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)

Normal ifade kaynağı: MarkupSafe . Sürümleri HTML varlıklarını da ele alırken, bu hızlı olanı kullanmaz.

Neden etiketleri soyup bırakamıyorum?

İnsanları etrafta yüzmeye <i>italicizing</i>bırakmadan şeylerden uzak tutmak bir şeydir i. Ancak keyfi girdi almak ve tamamen zararsız hale getirmek başka bir şeydir. Bu sayfadaki tekniklerin çoğu, kapatılmamış yorumlar ( <!--) ve etiketlerin ( blah <<<><blah) bir parçası olmayan açılı ayraçlar gibi şeyleri olduğu gibi bırakacaktır . HTMLParser sürümü, kapatılmamış bir yorumun içindeyse, etiketleri tam olarak bırakabilir.

Şablonunuz ne olacak {{ firstname }} {{ lastname }}? firstname = '<a've lastname = 'href="http://evil.com/">'bu sayfadaki her etiket çıkarıcı tarafından izin verilecektir (@Medeiros! hariç), çünkü kendi başlarına tam etiketler değildir. Normal HTML etiketlerini çıkarmak yeterli değildir.

strip_tagsBu sorunun en iyi cevabının geliştirilmiş (bir sonraki başlığa bakınız) versiyonu olan Django's , aşağıdaki uyarıyı verir:

Sonuçta elde edilen dizginin HTML açısından güvenli olduğu konusunda kesinlikle HİÇBİR garanti verilmez. Bu nedenle, ASLA bir strip_tagsçağrının sonucunu önce kaçmadan güvenli bir şekilde işaretlemeyin , örneğin escape().

Onların tavsiyelerine uyun!

Etiketleri HTMLParser ile şeritlemek için, etiketi birden çok kez çalıştırmanız gerekir.

Bu sorunun en iyi cevabını atlatmak kolaydır.

Şu dizeye bakın ( kaynak ve tartışma ):

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

HTMLParser bunu ilk gördüğünde, bunun <img...>bir etiket olduğunu söyleyemez . Bozuk görünüyor, bu yüzden HTMLParser ondan kurtulmuyor. Sadece <!-- comments -->sizi çıkarır,

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

Bu sorun Mart 2014'te Django projesine açıklandı. Eski strip_tagssoruları bu sorunun en büyük cevabı ile aynıydı. Yeni sürümleri temelde tekrar çalıştırıp dizeyi değiştirmeyene kadar bir döngüde çalıştırır:

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

Tabii ki, her zaman sonuçtan kaçarsanız bunların hiçbiri bir sorun değildir strip_tags().

Güncelleme 19 Mart 2015 : Django sürümlerinde 1.4.20, 1.6.11, 1.7.7 ve 1.8c1'den önce bir hata oluştu. Bu sürümler strip_tags () işlevine sonsuz bir döngü girebilir. Sabit sürüm yukarıda üretilmiştir. Daha fazla ayrıntı burada .

Kopyalamak veya kullanmak için iyi şeyler

Örnek kodum HTML varlıklarını işlemez - Django ve MarkupSafe paket sürümleri çalışır.

Örnek kodum, siteler arası komut dosyası önleme için mükemmel MarkupSafe kitaplığından alınmıştır. Kullanışlı ve hızlı (yerel Python sürümüne C hızları ile). Bu dahildir? Google App Engine ve kullandığı jinja2 (2.7 ve üstü) , Mako, elektrik direği ve daha fazlası. Django 1.7 Django şablonları ile kolayca çalışır.

Django'nun strip_tags ve son sürümdeki diğer html yardımcı programları iyidir, ancak MarkupSafe'den daha az uygun buluyorum. Oldukça bağımsızdırlar, ihtiyacınız olanı bu dosyadan kopyalayabilirsiniz .

Hemen hemen tüm etiketleri soymanız gerekiyorsa , Bleach kitaplığı iyidir. "Kullanıcılarım bir şeyleri italik yapabilir, ancak iframe yapamazlar" gibi kuralların uygulanmasını sağlayabilirsiniz.

Etiket soyucunuzun özelliklerini anlayın! Üzerinde fuzz testleri yap! İşte bu cevap için araştırma yapmak için kullandığım kod .

sheepish note - Sorunun kendisi konsola yazdırmakla ilgili, ancak bu "string'ten python strip html" için en iyi Google sonucudur, bu yüzden bu cevap web hakkında% 99'dur.


"Alternatif son satır" örnek kodum html varlıklarını işlemez - bu ne kadar kötü?
rescdsk

Ben sadece özel bir etiket ile html küçük bir yığın ayrıştırıyorum ve kısa sürümü çok iyi iş yapar. Paylaşım için teşekkürler!
tbolender

31

Etiketleri soymak ve HTML objelerini düz metne çözmek için bir yola ihtiyacım vardı . Aşağıdaki çözüm Eloff'un cevabına dayanmaktadır (ki bu varlıkları çıkardığı için kullanamadım).

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

Hızlı bir test:

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

Sonuç:

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

Hata yönetimi:

  • Geçersiz HTML yapısı bir HTMLParseError'a neden olabilir .
  • Geçersiz adlandırılmış HTML varlıkları ( &#apos;XML ve XHTML'de geçerli olan, ancak düz HTML'de olmayan) ValueErroristisnaya neden olur .
  • Python tarafından kabul edilebilir Unicode aralığının dışında kod noktaları belirten sayısal HTML varlıkları (bazı sistemlerde Temel Çok Dilli Düzlem dışındaki karakterler gibi ) bir ValueErroristisnaya neden olur .

Güvenlik notu: HTML sıyırma (HTML'yi düz metne dönüştürme) ile HTML sanitizing (düz metni HTML'ye dönüştürme) ile karıştırmayın. Bu yanıt, HTML'yi kaldırır ve varlıkları düz metne dönüştürür; bu, sonucun HTML bağlamında kullanılmasını güvenli hale getirmez.

Örnek: &lt;script&gt;alert("Hello");&lt;/script&gt;dönüştürülür <script>alert("Hello");</script>bir HTML sayfasına olduğu gibi elde edilen düz metin takılı ise, tabii ki, yeterli% 100 doğru davranıştır, fakat.

Kural zor değildir: Her zaman HTML çıktısı içine bir düz metin dizesi eklemek size gereken her zaman HTML (kullanarak kaçmak cgi.escape(s, True)HTML içermediğini "bilmek" bile) (örn HTML içeriği elimden çünkü) .

(Ancak, OP sonucu konsola yazdırmayı sordu, bu durumda HTML çıkışına gerek yoktur.)

Python 3.4+ sürümü: (doctest ile!)

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

HTMLParser'ın Python 3'te geliştiğini unutmayın (daha az kod ve daha iyi hata işleme anlamına gelir).


18

Bunun basit bir yolu var:

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

Fikir burada açıklanmaktadır: http://youtu.be/2tu9LTDujbw

Burada çalıştığını görebilirsiniz: http://youtu.be/HPkNPcYed9M?t=35s

Not: Sınıfla ilgileniyorsanız (python ile akıllı hata ayıklama hakkında) Size bir bağlantı veriyorum: http://www.udacity.com/overview/Course/cs259/CourseRev/1 . Bedava!

Rica ederim! :)


2
Bu cevabın neden azaldığını merak ediyorum. Bu, herhangi bir lib olmadan sorunu çözmenin basit bir yoludur. Sadece saf python ve bağlantılar tarafından gösterildiği gibi çalışır.
Medeiros

2
Muhtemelen insanlar güven vermek için kütüphaneleri tercih ederler. Size kod test ve geçti ve ben her zaman bir lib kullanarak ve bir hata çıkıncaya kadar tamam olduğunu varsayarak daha küçük kod tercih ederim. Benim için aradığım şey bu oldu ve tekrar teşekkürler. Aşağı oylarla ilgili olarak, bu zihniyete girmeyin. Buradaki insanlar oylara değil kaliteye önem vermelidir. Son zamanlarda SO, bilginin değil herkesin puan istediği bir yer haline geldi.
Jimmy Kane

2
Bu çözümün sorunu hata işlemedir. Örneğin <b class="o'>x</b>giriş işlevi çıkışları olarak verirseniz x. Ama aslında bu girdi geçersiz. Bence bu yüzden insanlar kütüphaneleri tercih ediyor.
laltin

1
Bu girişle de çalışır. Sadece test edildi. Sadece bu kütüphanelerin içinde benzer bir kod bulacağınızı unutmayın. Çok pitonik değil, biliyorum. C veya Java kodu gibi görünüyor. Bence etkili ve başka bir dile kolayca taşınabilir.
Medeiros

1
Basit, Pythonic ve tartışılan diğer yöntemlerden daha iyi veya daha iyi çalışıyor gibi görünüyor. Bazı kötü şekillendirilmiş HTML için işe yaramayabilir, ancak bunun üstesinden gelemez.
denson

16

HTML varlıklarını (yani &amp;) korumanız gerekiyorsa , Eloff'un cevabına "handle_entityref" yöntemini ekledim .

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

13

Tüm HTML etiketlerini çıkarmak istiyorsanız bulduğum en kolay yol BeautifulSoup kullanmaktır:

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

Kabul edilen yanıtın kodunu denedim ama yukarıdaki kod bloğunda olmadı "RuntimeError: maksimum özyineleme derinliği aşıldı" alıyordum.


1
Metodunu denedim çünkü daha temiz görünüyor, işe yaradı, iyi ... giriş etiketlerini çıkarmadı!
kustomrtr

Ben BeautifulSoup basit bir uygulama whitespaces ile ilgili bir sorun olduğunu fark: ''.join(BeautifulSoup('<em>he</em>llo<br>world').find_all(text=True)). Muhtemelen "merhaba dünya" olmasını istiyorsanız, burada çıktı "helloworld" dir. ' '.join(BeautifulSoup('<em>he</em>llo<br>world').find_all(text=True))"o llo dünyası" olduğu için yardımcı olmaz.
Finn Årup Nielsen

@kustomrtr, cehaletim için özür dilerim. NameError: 'self' adı tanımlanmadı
Ian_De_Oliveira

@Ian_De_Oliveira Onu kaldırabilirsiniz, bir sınıfın içinde olduğunu düşündüm ama gerekli değil. Cevabı kaldırmak için de düzenledim
Vasilis

@Ian_De_Oliveira Onu kaldırabilirsiniz, bir sınıfın içinde olduğunu düşündüm ama gerekli değil. Cevabı kaldırmak için de düzenledim
Vasilis

10

HTML etiketlerini ayıran ve HTML varlıklarını inanılmaz derecede hızlı lxmlkitaplığa dayalı olarak çözen basit bir çözüm :

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.

3
2020'den itibaren, bu, HTML'nin içeriğini soymanın en hızlı ve en iyi yoluydu. Ayrıca kod çözme işleminin bonusu. Dil algılama için harika!
dfabiano

text_content()geri döner, lxml.etree._ElementUnicodeResultbu yüzden önce dizeye
dökmeniz

1
@Suzana İyi bir nokta. Dizine ekleme ve dizine ekleme strişlemleri için otomatik olarak yayınlanıyor gibi görünüyor . Yine de iyi bir ölçü için bir oyuncu kadrosu eklendi. +[]
Robin Dinse

9

Bir lxml.html tabanlı çözüm (Lxml bir yerli kütüphane ve dolayısıyla çok daha hızlı herhangi saf piton çözümüne kıyasla).

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

Ayrıca , lxml.cleaner'ın tam olarak ne yaptığını görmek için http://lxml.de/lxmlhtml.html#cleaning-up-html adresine bakın .

Metne dönüştürmeden önce tam olarak neyin sterilize edildiği konusunda daha fazla kontrole ihtiyacınız varsa , yapıcıda istediğiniz seçenekleri geçirerek lxml Temizleyiciyi açıkça kullanmak isteyebilirsiniz , örneğin:

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)

1
AttributeError: 'HtmlElement' nesnesinin 'strip' özelliği yok
aris

7

Güzel Çorba paketi bunu hemen sizin için yapar.

from bs4 import BeautifulSoup

soup = BeautifulSoup(html)
text = soup.get_text()
print(text)

3
İnceleme kuyruğundan: Lütfen yanıtınıza daha fazla bağlam eklemenizi isteyebilir miyim? Yalnızca kod yanıtlarının anlaşılması zordur. Gönderinize daha fazla bilgi ekleyebilmeniz hem askerlere hem de gelecekteki okuyuculara yardımcı olacaktır.
help-info.de

2

İşte python 3 için çözümüm.

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

Mükemmel olup olmadığından emin değilim, ancak kullanım durumumu çözdüm ve basit görünüyor.


2

Yalnızca metni ayıklamak için işlevler sunan farklı bir HTML ayrıştırıcısı ( lxml veya Güzel Çorba gibi ) kullanabilirsiniz. Alternatif olarak, satır dizenizde etiketleri çıkaran bir normal ifade kullanabilirsiniz. Daha fazla bilgi için Python belgeleri bölümüne bakın .


1
amk bağlantısı öldü. Alternatifin var mı?

2
Python web sitesinde şimdi nasıl yapılır, regex nasıl yapılır: docs.python.org/howto/regex
Jason Coon

5
Lxml'de:lxml.html.fromstring(s).text_content()
Bluu

1
Bluu'nun lxml örneği, HTML varlıklarını (örn. &amp;) Metne çözer .
Søren Løvborg

1

Eloff'un cevabını Python 3.1 için başarıyla kullandım [çok teşekkürler!].

Python 3.2.3 sürümüne geçtim ve hatalarla karşılaştım.

Yanıtlayan Thomas K sayesinde burada sağlanan çözüm, super().__init__()aşağıdaki koda eklemektir:

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

... şöyle görünmesi için:

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

... ve Python 3.2.3 için çalışacak.

Yine, düzeltme ve Eloff'un yukarıda verilen orijinal kodu için Thomas K'a teşekkürler!


1

Kendi fonksiyonunuzu yazabilirsiniz:

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

1
Dizelere eklemek dizenin yeni bir kopyasını oluşturur mu?
Jeremy L

1
@Nerdling - Evet, sık kullanılan işlevlerde (veya bu nedenle, büyük metin yığınlarına etki eden nadiren kullanılan işlevlerde oldukça etkileyici bazı verimsizliklere yol açabilir). Ayrıntılar için bu sayfaya bakın . : D
Jeremy Sandell

Alıntılanan dizelere karşı test yapıyor mu? Hayır.
Jimmy Kane

1

HTML-Parser çözümlerinin tümü, yalnızca bir kez çalıştırılırsa kırılabilir:

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

sonuç:

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

ne önlemek istiyorsun. HTML-Ayrıştırıcı kullanıyorsanız sıfır değiştirilene kadar Etiketleri sayın:

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

1
Adlı bir işlevi çağırırsanız html_to_textve o metinden kaçmadan o işlevden çıkan metni html içine gömerseniz, html_to_textişlev değil bir güvenlik açığı olan kaçış eksikliğidir . html_to_textFonksiyon size çıkış metin olacağını söz vermedim. Ve html'ye kaçmadan metin eklemek, metni html_to_text veya başka bir kaynaktan aldığınızdan bağımsız olarak potansiyel bir güvenlik açığıdır .
kasperd

Doğru durumda, kaçmak için bir eksiklik, ama sorular belirli bir dize kaçmak için verilen bir dize html şerit oldu. Daha önceki cevaplar bazı html'lerin kaldırılması sonucunda çözümleri ile yeni html oluşturuyorsa, bu çözümlerin kullanımı tehlikelidir.
Falk Nisius

1

Bu hızlı bir düzeltmedir ve daha da optimize edilebilir, ancak iyi çalışır. Bu kod boş olmayan tüm etiketleri "" ile değiştirecek ve tüm html etiketlerini belirli bir giriş metninden çıkaracak. .Ffile.py girdi çıktısını kullanarak çalıştırabilirsiniz.

    #!/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"

1

Søren-løvborg'un cevabına bir python 3 uyarlaması

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

1

Bir proje için çok şerit HTML, ama aynı zamanda css ve js gerekiyordu. Böylece, Eloffs cevabının bir varyasyonunu yaptım:

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

1

Şu anda kabul edilen cevaba benzer bir çözüm ( https://stackoverflow.com/a/925630/95989 ), ancak dahiliHTMLParser sınıfı doğrudan kullanması (yani alt sınıflama olmaması), böylece daha da keskin hale getirir:

def strip_html (metin):
    parçalar = []                                                                      
    parser = HTMLParser ()                                                           
    parser.handle_data = parts.append                                               
    parser.feed (metin)                                                               
    dönüş ''. katıl (parçalar)

0

Github readmes'i ayrıştırıyorum ve aşağıdakilerin gerçekten iyi çalıştığını görüyorum:

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

Ve sonra

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

Tüm işaretleme ve html dosyalarını doğru şekilde kaldırır.


0

BeautifulSoup, html2text veya @Eloff kodunu kullanarak, çoğu zaman bazı html öğeleri, javascript kodu kalır ...

Böylece bu kitaplıkların bir kombinasyonunu kullanabilir ve etiketleme biçimlendirmesini silebilirsiniz (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

Benim için iyi çalışıyor ama elbette geliştirilebilir ...


0

Basit kod !. Bu, içindeki her türlü etiketi ve içeriği kaldıracaktır.

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

Ancak metnin içinde <> sembolleri varsa tam sonuç vermez .


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

-2

Bu yöntem benim için kusursuz çalışıyor ve ek kurulum gerektirmiyor:

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

3
Bu, HTML varlıklarını düz metne çözer, ancak asıl soru olan herhangi bir etiketi gerçekten kaldırmaz. (Ayrıca, kodun çok fazla yapabilmesi için ikinci deneme hariç bloğunun girintisinin kaldırılması gerekir).
Søren Løvborg
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.