Tách HTML khỏi chuỗi trong Python


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

Khi in một dòng trong tệp HTML, tôi đang cố gắng tìm cách chỉ hiển thị nội dung của từng thành phần HTML chứ không phải định dạng chính nó. Nếu tìm thấy '<a href="whatever.com">some text</a>', nó sẽ chỉ in 'một số văn bản', '<b>hello</b>'in 'xin chào', v.v ... Làm thế nào một người sẽ làm điều này?


16
Một xem xét quan trọng là làm thế nào để xử lý các thực thể HTML (ví dụ &amp;). Bạn có thể 1) xóa chúng cùng với các thẻ (thường không mong muốn và không cần thiết vì chúng tương đương với văn bản thuần túy), 2) không thay đổi chúng (một giải pháp phù hợp nếu văn bản bị tước sẽ quay lại ngữ cảnh HTML) hoặc 3 ) giải mã chúng thành văn bản thuần túy (nếu văn bản bị tước đi vào cơ sở dữ liệu hoặc một số bối cảnh không phải HTML khác hoặc nếu khung web của bạn tự động thực hiện thoát văn bản HTML cho bạn).
Søren Løvborg

2
cho @ SørenLøvborg điểm 2): stackoverflow.com/questions/753052/iêu
Robert

2
Câu trả lời hàng đầu ở đây, được sử dụng bởi dự án Django cho đến tháng 3 năm 2014, đã được phát hiện là không an toàn đối với kịch bản chéo trang - hãy xem liên kết đó để biết ví dụ. Tôi khuyên bạn nên sử dụng các trang thoát y của Bleach.clean (), Markupsafe hoặc RECENT Django.
rescdsk

Câu trả lời:


419

Tôi luôn sử dụng chức năng này để loại bỏ các thẻ HTML, vì nó chỉ yêu cầu stdlib của Python:

Đối với 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()

Đối với 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()

3
Hai năm sau, đối mặt với cùng một vấn đề, và đây là một giải pháp thanh lịch hơn nhiều. Chỉ thay đổi tôi đã thực hiện là trả về self.fed dưới dạng danh sách, thay vì tham gia nó, vì vậy tôi có thể chuyển qua nội dung phần tử.
chỉ thị

47
Lưu ý rằng điều này loại bỏ các thực thể HTML (ví dụ &amp;) cũng như các thẻ.
Søren Løvborg

30
@surya Tôi chắc chắn bạn đã nhìn thấy điều này
tkone

8
Cảm ơn câu trả lời tuyệt vời. Một điều cần lưu ý đối với những bạn sử dụng các phiên bản Python mới hơn (3.2+) là bạn sẽ cần gọi __init__hàm của lớp cha . Xem tại đây: stackoverflow.com/questions/11061058/ .
giả hành

10
Để giữ các thực thể html (được chuyển đổi thành unicode), tôi đã thêm hai dòng: parser = HTMLParser()html = parser.unescape(html)vào đầu hàm str_tags.
James Doepp - pieaveragyu

157

Tôi đã không nghĩ nhiều về các trường hợp nó sẽ bỏ lỡ, nhưng bạn có thể thực hiện một regex đơn giản:

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

Đối với những người không hiểu regex, điều này tìm kiếm một chuỗi <...>, trong đó nội dung bên trong được tạo từ một hoặc nhiều +ký tự ( ) không phải là một <. Có ?nghĩa là nó sẽ khớp với chuỗi nhỏ nhất mà nó có thể tìm thấy. Ví dụ đã cho <p>Hello</p>, nó sẽ khớp <'p></p>riêng với ?. Không có nó, nó sẽ khớp với toàn bộ chuỗi <..Hello..>.

Nếu thẻ không <xuất hiện trong html (ví dụ. 2 < 3), Dù sao thì nó cũng phải được viết dưới dạng một chuỗi thoát &...để ^<có thể không cần thiết.


10
Đây gần như chính xác là cách thoát khỏi dải băng của Django .
Bluu

10
Lưu ý rằng điều này làm cho các thực thể HTML (ví dụ &amp;) không thay đổi trong đầu ra.
Søren Løvborg

36
Người ta vẫn có thể lừa phương thức này bằng một cái gì đó như thế này: <script <script >> alert ("Hi!") << / script> / script>

19
ĐỪNG LÀM NÓ! Như @Julio Garcia nói, nó KHÔNG AN TOÀN!
rescdsk

18
Mọi người, đừng nhầm lẫn việc tước HTML và vệ sinh HTML. Có, đối với đầu vào bị hỏng hoặc độc hại, câu trả lời này có thể tạo ra đầu ra có thẻ HTML trong đó. Đây vẫn là một cách tiếp cận hoàn toàn hợp lệ để loại bỏ các thẻ HTML. Tuy nhiên , tước thẻ HTML không phải là sự thay thế hợp lệ cho việc vệ sinh HTML đúng cách. Quy tắc không khó: Bất cứ khi nào bạn chèn một chuỗi văn bản đơn giản vào đầu ra HTML, bạn phải luôn luôn thoát HTML (sử dụng cgi.escape(s, True)), ngay cả khi bạn "biết" rằng nó không chứa HTML (ví dụ: vì bạn đã tước nội dung HTML) . Tuy nhiên, đây không phải là những gì OP yêu cầu.
Søren Løvborg

76

Bạn có thể sử dụng get_text()tính năng 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)

Nên chỉ định rõ ràng trình phân tích cú pháp , ví dụ như BeautifulSoup(html_str, features="html.parser"), để đầu ra có thể tái tạo.


32

Phiên bản ngắn!

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)

Nguồn Regex: MarkupSafe . Phiên bản của họ cũng xử lý các thực thể HTML, trong khi phiên bản nhanh này thì không.

Tại sao tôi không thể tách các thẻ và để lại nó?

Đó là một điều để giữ mọi người khỏi <i>italicizing</i>mọi thứ, mà không để lại inổi xung quanh. Nhưng đó là một cách khác để có đầu vào tùy ý và làm cho nó hoàn toàn vô hại. Hầu hết các kỹ thuật trên trang này sẽ để lại những thứ như bình luận không được tiết lộ ( <!--) và dấu ngoặc nhọn không blah <<<><blahcòn nguyên vẹn của thẻ ( ). Phiên bản HTMLParser thậm chí có thể để lại các thẻ hoàn chỉnh, nếu chúng nằm trong một nhận xét không được tiết lộ.

Nếu mẫu của bạn là {{ firstname }} {{ lastname }}gì? firstname = '<a'lastname = 'href="http://evil.com/">'sẽ được thông qua bởi mọi trình gỡ thẻ trên trang này (ngoại trừ @Medeiros!), vì chúng không tự hoàn thành các thẻ. Tước các thẻ HTML bình thường là không đủ.

Django strip_tags, một phiên bản cải tiến (xem tiêu đề tiếp theo) của câu trả lời hàng đầu cho câu hỏi này, đưa ra cảnh báo sau:

Tuyệt đối KHÔNG đảm bảo được cung cấp về chuỗi kết quả là HTML an toàn. Vì vậy, KHÔNG BAO GIỜ đánh dấu an toàn kết quả của một strip_tagscuộc gọi mà không thoát khỏi cuộc gọi trước, ví dụ như với escape().

Làm theo lời khuyên của họ!

Để loại bỏ các thẻ bằng HTMLParser, bạn phải chạy nó nhiều lần.

Thật dễ dàng để vượt qua câu trả lời hàng đầu cho câu hỏi này.

Nhìn vào chuỗi này ( nguồn và thảo luận ):

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

Lần đầu tiên HTMLParser nhìn thấy nó, nó không thể nói rằng đó <img...>là một thẻ. Nó trông có vẻ bị hỏng, vì vậy HTMLParser không thoát khỏi nó. Nó chỉ lấy ra <!-- comments -->, để lại cho bạn

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

Vấn đề này đã được tiết lộ cho dự án Django vào tháng 3 năm 2014. Câu hỏi cũ của họ strip_tagsvề cơ bản giống như câu trả lời hàng đầu cho câu hỏi này. Phiên bản mới của họ về cơ bản chạy nó trong một vòng lặp cho đến khi chạy lại nó không thay đổi chuỗi:

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

Tất nhiên, không có gì trong số này là vấn đề nếu bạn luôn thoát khỏi kết quả strip_tags().

Cập nhật ngày 19 tháng 3 năm 2015 : Có lỗi trong các phiên bản Django trước 1.4.20, 1.6.11, 1.7.7 và 1.8c1. Các phiên bản này có thể nhập một vòng lặp vô hạn trong hàm dải_t_t (). Phiên bản cố định được sao chép ở trên. Thêm chi tiết tại đây .

Những điều tốt để sao chép hoặc sử dụng

Mã ví dụ của tôi không xử lý các thực thể HTML - phiên bản đóng gói Django và MarkupSafe.

Mã ví dụ của tôi được lấy từ thư viện MarkupSafe tuyệt vời để ngăn chặn kịch bản chéo trang. Thật tiện lợi và nhanh chóng (với việc tăng tốc độ C lên phiên bản Python gốc). Nó được bao gồm trong Google App Engine và được sử dụng bởi Jinja2 (2.7 trở lên) , Mako, Pylons, v.v. Nó hoạt động dễ dàng với các mẫu Django từ Django 1.7.

Strip_tags của Django và các tiện ích html khác từ phiên bản gần đây là tốt, nhưng tôi thấy chúng không tiện lợi hơn MarkupSafe. Chúng khá khép kín, bạn có thể sao chép những gì bạn cần từ tệp này .

Nếu bạn cần loại bỏ gần như tất cả các thẻ, thư viện Bleach là tốt. Bạn có thể yêu cầu nó thực thi các quy tắc như "người dùng của tôi có thể in nghiêng mọi thứ, nhưng họ không thể tạo ra iframe."

Hiểu các thuộc tính của vũ nữ thoát y thẻ của bạn! Chạy thử nghiệm fuzz trên đó! Đây là mã tôi đã sử dụng để thực hiện nghiên cứu cho câu trả lời này.

lưu ý ngớ ngẩn - Bản thân câu hỏi là về việc in lên bàn điều khiển, nhưng đây là kết quả hàng đầu của Google cho "chuỗi python html từ chuỗi", vì vậy đó là lý do tại sao câu trả lời này là 99% về web.


Mã ví dụ "dòng cuối cùng thay thế" của tôi không xử lý các thực thể html - điều đó tệ đến mức nào?
rescdsk

Tôi chỉ phân tích một đoạn html nhỏ không có thẻ đặc biệt và phiên bản ngắn của bạn thực hiện công việc rất tốt. Cám ơn vì đã chia sẻ!
tbolender

31

Tôi cần một cách để tách thẻ giải mã các thực thể HTML thành văn bản thuần túy. Giải pháp sau đây dựa trên câu trả lời của Eloff (mà tôi không thể sử dụng vì nó loại bỏ các thực thể).

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

Một bài kiểm tra nhanh:

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

Kết quả:

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

Xử lý lỗi:

  • Cấu trúc HTML không hợp lệ có thể gây ra HTMLPudeError .
  • Các thực thể HTML có tên không hợp lệ (chẳng hạn như &#apos;, hợp lệ trong XML và XHTML, nhưng không phải là HTML đơn giản) sẽ gây ra ValueErrorngoại lệ.
  • Các thực thể HTML số chỉ định các điểm mã nằm ngoài phạm vi Unicode được Python chấp nhận (chẳng hạn như trên một số hệ thống, các ký tự bên ngoài Mặt phẳng đa ngôn ngữ cơ bản ) sẽ gây ra ValueErrorngoại lệ.

Lưu ý bảo mật: Không nhầm lẫn giữa việc tước HTML (chuyển đổi HTML thành văn bản thuần túy) với việc khử trùng HTML (chuyển đổi văn bản thuần thành HTML). Câu trả lời này sẽ xóa HTML và giải mã các thực thể thành văn bản thuần túy - điều đó không làm cho kết quả an toàn khi sử dụng trong ngữ cảnh HTML.

Ví dụ: &lt;script&gt;alert("Hello");&lt;/script&gt;sẽ được chuyển đổi thành <script>alert("Hello");</script>hành vi đúng 100%, nhưng rõ ràng là không đủ nếu văn bản thuần túy kết quả được chèn vào nguyên trạng vào trang HTML.

Quy tắc không khó: Bất cứ khi nào bạn chèn một chuỗi văn bản đơn giản vào đầu ra HTML, bạn phải luôn luôn thoát HTML (sử dụng cgi.escape(s, True)), ngay cả khi bạn "biết" rằng nó không chứa HTML (ví dụ: vì bạn đã tước nội dung HTML) .

(Tuy nhiên, OP đã hỏi về việc in kết quả lên bàn điều khiển, trong trường hợp đó không cần thoát HTML.)

Phiên bản Python 3.4+: (với 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()

Lưu ý rằng HTMLParser đã được cải thiện trong Python 3 (có nghĩa là ít mã hơn và xử lý lỗi tốt hơn).


18

Có một cách đơn giản để làm điều này:

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

Ý tưởng được giải thích ở đây: http://youtu.be/2tu9LTDujbw

Bạn có thể thấy nó hoạt động ở đây: http://youtu.be/HPkNPcYed9M?t=35s

Tái bút - Nếu bạn quan tâm đến lớp học (về gỡ lỗi thông minh với python) Tôi cung cấp cho bạn một liên kết: http://www.udacity.com/overview/Cference/cs259/CferenceRev/1 . Nó miễn phí!

Không có gì! :)


2
Tôi tự hỏi tại sao câu trả lời này chỉ bị hạ cấp. Đó là một cách đơn giản để giải quyết vấn đề mà không cần bất kỳ lib. Chỉ cần trăn thuần túy và nó hoạt động như được hiển thị bởi các liên kết.
Medeiros

2
Có lẽ mọi người thích libs để cung cấp cho họ sự an toàn. Tôi đã kiểm tra mã của bạn và thông qua, và tôi luôn thích mã nhỏ mà tôi hiểu hơn là sử dụng lib và giả sử rằng nó ổn cho đến khi một lỗi xuất hiện. Đối với tôi đó là những gì tôi đang tìm kiếm và một lần nữa cảm ơn. Về các downvote, đừng có trong suy nghĩ đó. Mọi người ở đây nên quan tâm đến chất lượng chứ không phải phiếu bầu. Gần đây SO đã trở thành một nơi mà mọi người đều muốn điểm và không có kiến ​​thức.
Jimmy Kane

2
Vấn đề với giải pháp này là xử lý lỗi. Ví dụ nếu bạn đưa ra <b class="o'>x</b>như đầu ra chức năng đầu vào x. Nhưng thực sự đầu vào này không hợp lệ. Tôi nghĩ đó là lý do tại sao mọi người thích libs.
laltin

1
Nó hoạt động với đầu vào đó quá. Chỉ cần thử nghiệm. Chỉ cần nhận ra rằng bên trong những thư viện đó bạn sẽ tìm thấy mã tương tự. Nó không phải là rất pythonic, tôi biết. Trông giống như mã C hoặc Java. Tôi nghĩ rằng nó hiệu quả và có thể dễ dàng chuyển sang ngôn ngữ khác.
Medeiros

1
Đơn giản, Pythonic và dường như hoạt động tốt hoặc tốt hơn bất kỳ phương pháp nào khác được thảo luận. Có thể nó sẽ không hoạt động đối với một số HTML không được định dạng nhưng không có cách khắc phục nào.
denson

16

Nếu bạn cần bảo tồn các thực thể HTML (nghĩa là &amp;), tôi đã thêm phương thức "handle_entityref" vào câu trả lời của 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()

13

Nếu bạn muốn loại bỏ tất cả các thẻ HTML, cách dễ nhất tôi tìm thấy là sử dụng 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)) 

Tôi đã thử mã của câu trả lời được chấp nhận nhưng tôi nhận được "RuntimeError: vượt quá độ sâu đệ quy tối đa", điều này không xảy ra với khối mã trên.


1
Tôi vừa thử phương pháp của bạn vì nó có vẻ sạch hơn, nó hoạt động tốt, loại ... nó không tước thẻ đầu vào!
kustomrtr

Tôi thấy rằng một ứng dụng đơn giản của BeautifulSoup có vấn đề với khoảng trắng : ''.join(BeautifulSoup('<em>he</em>llo<br>world').find_all(text=True)). Ở đây, đầu ra là "hellowworld", trong khi bạn có thể muốn nó là "xin chào thế giới". ' '.join(BeautifulSoup('<em>he</em>llo<br>world').find_all(text=True))không giúp gì khi nó trở thành "thế giới llo".
Finn Årup Nielsen

@kustomrtr, xin lỗi sự thiếu hiểu biết của tôi, tôi phải làm gì để tranh luận? TênError: tên 'tự' không được xác định
Ian_De_Oliveira

@Ian_De_Oliveira Bạn có thể xóa nó, tôi giả sử nó nằm trong một lớp nhưng không cần thiết. Tôi cũng chỉnh sửa câu trả lời để xóa nó
Vasilis

@Ian_De_Oliveira Bạn có thể xóa nó, tôi giả sử nó nằm trong một lớp nhưng không cần thiết. Tôi cũng chỉnh sửa câu trả lời để xóa nó
Vasilis

10

Đây là một giải pháp đơn giản giúp loại bỏ các thẻ HTML và giải mã các thực thể HTML dựa trên lxmlthư viện nhanh đáng kinh ngạc :

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
Kể từ năm 2020, đây là cách nhanh nhất và tốt nhất để loại bỏ nội dung của HTML. Cộng với tiền thưởng của việc xử lý giải mã. Tuyệt vời để phát hiện ngôn ngữ!
dfabiano

text_content()trả về lxml.etree._ElementUnicodeResultđể bạn có thể phải chuyển nó thành chuỗi trước
Suzana

1
@Suzana Điểm tốt. Nó dường như được tự động chuyển sang strcác hoạt động chuỗi như +và lập chỉ mục []. Thêm một diễn viên cho biện pháp tốt dù sao đi nữa.
Robin Dinse

9

Một giải pháp dựa trên lxml.html (lxml là một thư viện riêng và do đó nhanh hơn nhiều so với bất kỳ giải pháp python thuần túy nào).

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

Đồng thời xem http://lxml.de/lxmlhtml.html#cleaning-up-html để biết chính xác lxml.cleaner làm gì.

Nếu bạn cần kiểm soát nhiều hơn chính xác những gì được khử trùng trước khi chuyển đổi thành văn bản thì bạn có thể muốn sử dụng lxml Cleaner một cách rõ ràng bằng cách chuyển các tùy chọn bạn muốn trong hàm tạo, ví dụ:

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
Tôi đã nhận được AttributionError: đối tượng 'HtmlEuity' không có thuộc tính 'dải'
aris

7

Gói Beautiful Soup làm điều này ngay lập tức cho bạn.

from bs4 import BeautifulSoup

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

3
Từ hàng đợi đánh giá: Tôi có thể yêu cầu bạn vui lòng thêm một số bối cảnh xung quanh câu trả lời của bạn. Câu trả lời chỉ có mã là khó hiểu. Nó sẽ giúp người hỏi và người đọc tương lai cả nếu bạn có thể thêm thông tin trong bài viết của mình.
help-info.de

2

Đây là giải pháp của tôi cho 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

Không chắc nó có hoàn hảo không, nhưng đã giải quyết trường hợp sử dụng của tôi và có vẻ đơn giản.


2

Bạn có thể sử dụng một trình phân tích cú pháp HTML khác nhau ( như lxml hoặc Beautiful Soup ) - một trình cung cấp các hàm để trích xuất chỉ văn bản. Hoặc, bạn có thể chạy regex trên chuỗi dòng của mình để loại bỏ các thẻ. Xem tài liệu Python để biết thêm.


1
liên kết amk đã chết. Có một sự thay thế?

2
Trang web Python hiện có các hướng dẫn tốt, đây là cách thức regex: docs.python.org/howto/regex
Jason Coon

5
Trong lxml:lxml.html.fromstring(s).text_content()
Bluu

1
Ví dụ của Bluu với lxml giải mã các thực thể HTML (ví dụ &amp;) thành văn bản.
Søren Løvborg

1

Tôi đã sử dụng câu trả lời của Eloff thành công cho Python 3.1 [cảm ơn rất nhiều!].

Tôi đã nâng cấp lên Python 3.2.3 và gặp lỗi.

Giải pháp, được cung cấp ở đây nhờ Thomas K trả lời, là chèn super().__init__()vào đoạn mã sau:

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

... để làm cho nó trông như thế này:

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

... và nó sẽ hoạt động với Python 3.2.3.

Một lần nữa, cảm ơn Thomas K về bản sửa lỗi và mã gốc của Eloff được cung cấp ở trên!


1

Bạn có thể viết chức năng của riêng bạn:

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
Việc nối vào chuỗi có tạo ra một bản sao mới của chuỗi không?
Jeremy L

1
@Nerdling - Có nó, mà có thể dẫn đến một số sự thiếu hiệu quả khá ấn tượng trong các chức năng thường được sử dụng (hoặc, cho rằng vấn đề, không thường xuyên sử dụng chức năng mà hành động dựa trên các đốm màu lớn các văn bản.) Xem này trang cho để xem chi tiết. : D
Jeremy Sandell

Nó kiểm tra chống lại chuỗi trích dẫn? Số
Jimmy Kane

1

Các giải pháp với HTML-Parser đều có thể phá vỡ, nếu chúng chỉ chạy một lần:

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

kết quả trong:

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

những gì bạn định ngăn chặn. nếu bạn sử dụng Trình phân tích cú pháp HTML, hãy đếm các Thẻ cho đến khi số 0 được thay thế:

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
Nếu bạn gọi một hàm được gọi html_to_textvà bạn nhúng văn bản là đầu ra từ hàm đó vào html mà không thoát văn bản đó, thì đó là thiếu thoát, đó là một lỗ hổng bảo mật, không phải là html_to_textchức năng. Các html_to_textchức năng không bao giờ hứa với bạn đầu ra sẽ là văn bản. Và chèn văn bản vào html mà không thoát là một lỗ hổng bảo mật tiềm ẩn bất kể bạn có nhận được văn bản từ html_to_text hoặc một số nguồn khác.
kasperd

Bạn đúng trong trường hợp thiếu thoát, nhưng câu hỏi là tách html khỏi một chuỗi đã cho để không thoát khỏi một chuỗi đã cho. Nếu các câu trả lời trước đó xây dựng html mới với các giải pháp của họ là kết quả của việc xóa một số html, thì việc sử dụng các giải pháp này có nguy hiểm không.
Falk Nisius ngày

1

Đây là một sửa chữa nhanh chóng và có thể được tối ưu hóa hơn nữa nhưng nó sẽ hoạt động tốt. Mã này sẽ thay thế tất cả các thẻ không trống bằng "" và loại bỏ tất cả các thẻ html tạo thành một văn bản đầu vào nhất định. Bạn có thể chạy nó bằng cách sử dụng ./file.py đầu ra đầu vào

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

Một con trăn 3 thích nghi với câu trả lời của 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()

1

Đối với một dự án, tôi cần loại bỏ HTML, nhưng cũng có css và js. Vì vậy, tôi đã thực hiện một biến thể của câu trả lời Eloffs:

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

Đây là một giải pháp tương tự như câu trả lời hiện được chấp nhận ( https://stackoverflow.com/a/925630/95989 ), ngoại trừ việc nó sử dụng HTMLParserlớp bên trong trực tiếp (nghĩa là không có phân lớp), do đó làm cho nó trở nên ngắn gọn hơn đáng kể:

def dải_html (văn bản):
    bộ phận = []                                                                      
    trình phân tích cú pháp = HTMLParser ()                                                           
    trình phân tích cú pháp.handle_data = phần.append                                               
    trình phân tích cú pháp.feed (văn bản)                                                               
    return '' .join (phần)

0

Tôi đang phân tích cú pháp Github readmes và tôi thấy rằng những điều sau đây thực sự hoạt động tốt:

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

Và sau đó

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

Loại bỏ tất cả markdown và html chính xác.


0

Sử dụng BeautifulSoup, html2text hoặc mã từ @Eloff, phần lớn thời gian, nó vẫn còn một số phần tử html, mã javascript ...

Vì vậy, bạn có thể sử dụng kết hợp các thư viện này và xóa định dạng đánh dấu (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

Nó hoạt động tốt với tôi nhưng nó có thể được tăng cường, tất nhiên ...


0

Mã đơn giản!. Điều này sẽ loại bỏ tất cả các loại thẻ và nội dung bên trong nó.

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

Nhưng nó sẽ không cho kết quả đầy đủ nếu văn bản chứa <> ký hiệu bên trong nó.


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

Phương pháp này hoạt động hoàn hảo đối với tôi và không yêu cầu cài đặt thêm:

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
Điều này giải mã các thực thể HTML thành văn bản thuần túy, nhưng rõ ràng không thực sự tước bất kỳ thẻ nào, đó là câu hỏi ban đầu. (Ngoài ra, khối thử ngoại trừ thứ hai cần được loại bỏ để mã có thể làm được nhiều như vậy).
Søren Løvborg
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.