html.parser --- پارسر ساده‌ی HTML و XHTML

کد منبع: Lib/html/parser.py


این ماژول یک کلاس HTMLParser را تعریف می‌کند که مبنایی برای تجزیه‌ی پرونده‌های متنی قالب‌بندی‌شده با HTML (زبان نمادگذاری ابرمتن) و XHTML است.

class html.parser.HTMLParser(*, convert_charrefs=True, scripting=False)

یک نمونه از پارسر ایجاد کنید که قادر به تجزیه نمادگذاری نامعتبر باشد.

اگر convert_charrefs برابر true باشد (پیش‌فرض)، تمام ارجاع‌های نویسه‌ای (به‌جز آن‌هایی که در عنصرهایی مانند script و style قرار دارند) به‌طور خودکار به نویسه‌های یونیکد متناظر تبدیل می‌شوند.

اگر scripting نادرست باشد (پیش‌فرض)، محتوای عنصر noscript به‌طور معمول تجزیه می‌شود؛ اگر درست باشد، همان‌طور که هست بدون تجزیه بازگردانده می‌شود.

یک نمونه از HTMLParser داده‌های HTML را دریافت می‌کند و هنگامی که با برچسب‌های آغازین، برچسب‌های پایانی، متن، کامنت‌ها و سایر عناصر نمادگذاری مواجه می‌شود، متدهای هندلر را فراخوانی می‌کند. کاربر باید یک زیرکلاس از HTMLParser بسازد و متدهای آن را بازنویسی کند تا رفتار دلخواه پیاده‌سازی شود.

این پارسر مطابقت برچسب‌های پایانی با برچسب‌های آغازین را بررسی نمی‌کند و هندلر برچسب پایانی را برای المان‌هایی که به‌طور ضمنی با بسته‌شدن یک المان بیرونی بسته می‌شوند، فراخوانی نمی‌کند.

تغییر یافته در نسخه‌ی 3.4: آرگومان کلیدواژه‌ای convert_charrefs اضافه شد.

تغییر یافته در نسخه‌ی 3.5: مقدار پیش‌فرض آرگومان convert_charrefs اکنون True است.

تغییر یافته در نسخه‌ی 3.14.1: پارامتر scripting افزوده شد.

مثال برنامه پارسر HTML

به‌عنوان یک مثال پایه، در زیر یک پارسر ساده‌ی HTML آمده است که از کلاس HTMLParser برای چاپ برچسب‌های شروع، برچسب‌های پایان و داده‌ها به‌محض مواجهه با آن‌ها استفاده می‌کند:

from html.parser import HTMLParser

class MyHTMLParser(HTMLParser):
    def handle_starttag(self, tag, attrs):
        print("Encountered a start tag:", tag)

    def handle_endtag(self, tag):
        print("Encountered an end tag :", tag)

    def handle_data(self, data):
        print("Encountered some data  :", data)

parser = MyHTMLParser()
parser.feed('<html><head><title>Test</title></head>'
            '<body><h1>Parse me!</h1></body></html>')

سپس خروجی به این صورت خواهد بود:

با یک برچسب آغازین مواجه شد: html
با یک برچسب آغازین مواجه شد: head
با یک برچسب آغازین مواجه شد: title
با داده‌ای مواجه شد: Test
با یک برچسب پایانی مواجه شد: title
با یک برچسب پایانی مواجه شد: head
با یک برچسب آغازین مواجه شد: body
با یک برچسب آغازین مواجه شد: h1
با داده‌ای مواجه شد: Parse me!
با یک برچسب پایانی مواجه شد: h1
با یک برچسب پایانی مواجه شد: body
با یک برچسب پایانی مواجه شد: html

متدهای HTMLParser

نمونه‌های HTMLParser دارای متدهای زیر هستند:

HTMLParser.feed(data)

مقداری متن به پارسر بدهید. این متن تا جایی که از عناصر کامل تشکیل شده باشد، پردازش می‌شود؛ داده‌های ناقص در بافر نگهداری می‌شوند تا زمانی که داده بیشتری به آن داده شود یا close() فراخوانی شود. data باید str باشد.

HTMLParser.close()

پردازش تمام داده‌های بافرشده را به‌اجبار انجام می‌دهد، گویی که به دنبال آن‌ها یک نشانه پایان پرونده آمده باشد. این متد ممکن است توسط یک کلاس مشتق‌شده بازتعریف شود تا پردازش اضافی را در پایان ورودی تعریف کند، اما نسخه بازتعریف‌شده باید همیشه متد close() کلاس پایه HTMLParser را فراخوانی کند.

HTMLParser.reset()

نمونه را بازنشانی می‌کند. تمام داده‌های پردازش‌نشده را از دست می‌دهد. این متد به‌طور ضمنی در زمان نمونه‌سازی فراخوانی می‌شود.

HTMLParser.getpos()

شماره سطر و آفست فعلی را برمی‌گرداند.

HTMLParser.get_starttag_text()

متن آخرین برچسب شروع بازشده را برمی‌گرداند. این مورد معمولاً برای پردازش ساختارمند لازم نیست، اما ممکن است در کار با HTML «به‌صورت مستقرشده» یا برای تولید مجدد ورودی با کمترین تغییرات مفید باشد (فضای سفید بین ویژگی‌ها می‌تواند حفظ شود، و غیره).

متدهای زیر زمانی فراخوانی می‌شوند که داده یا عناصر نمادگذاری مشاهده شوند و قرار است در یک زیرکلاس بازنویسی شوند. پیاده‌سازی‌های کلاس پایه کاری انجام نمی‌دهند (به‌جز handle_startendtag()):

HTMLParser.handle_starttag(tag, attrs)

این متد برای مدیریت برچسب آغازین یک المان فراخوانی می‌شود (مثلاً <div id="main">).

آرگومان tag نام تگ است که به حروف کوچک تبدیل شده است. آرگومان attrs فهرستی از جفت‌های (name, value) است که شامل ویژگی‌های یافت‌شده درون براکت‌های <> تگ است. name به حروف کوچک تبدیل خواهد شد، علامت‌های نقل‌قول در value حذف شده‌اند، و ارجاع‌های نویسه و موجودیت جایگزین شده‌اند. برای ویژگی‌های خالی، value برابر None است.

برای مثال، برای برچسب <A HREF="https://www.cwi.nl/">، این متد به‌صورت handle_starttag('a', [('href', 'https://www.cwi.nl/')]) فراخوانی می‌شود.

تمام ارجاع‌های موجودیت (entity references) از html.entities در مقادیر ویژگی جایگزین می‌شوند.

HTMLParser.handle_endtag(tag)

این متد برای رسیدگی به برچسب پایانی یک المان فراخوانی می‌شود (مثلاً </div>).

آرگومان tag، نام برچسبی است که به حروف کوچک تبدیل‌شده است.

HTMLParser.handle_startendtag(tag, attrs)

مشابه handle_starttag()، اما زمانی فراخوانی می‌شود که پارسر با یک برچسب خالی به سبک XHTML (<img ... />) مواجه شود. این متد ممکن است توسط زیرکلاس‌هایی که به این اطلاعات واژگانی خاص نیاز دارند بازنویسی شود؛ پیاده‌سازی پیش‌فرض به‌سادگی handle_starttag() و handle_endtag() را فراخوانی می‌کند.

HTMLParser.handle_data(data)

این متد برای پردازش داده‌های دلخواه فراخوانی می‌شود (برای مثال، گره‌های متنی و محتوای المان‌هایی مانند script و style).

HTMLParser.handle_entityref(name)

این متد برای پردازش یک ارجاع نویسه‌ی نام‌دار به شکل &name; (مثلاً &gt;) فراخوانی می‌شود، که در آن name یک ارجاع به موجودیت عمومی (مثلاً 'gt') است. این متد فقط زمانی فراخوانی می‌شود که convert_charrefs نادرست باشد.

HTMLParser.handle_charref(name)

این متد برای پردازش ارجاع‌های عددی نویسه به‌صورت دهدهی و مبنای شانزده، در قالب &#NNN; و &#xNNN; فراخوانی می‌شود. برای مثال، معادل دهدهی &gt; برابر &#62; است، درحالی‌که معادل مبنای شانزده آن &#x3E; است؛ در این حالت متد '62' یا 'x3E' را دریافت می‌کند. این متد تنها در صورتی فراخوانی می‌شود که convert_charrefs نادرست باشد.

HTMLParser.handle_comment(data)

این متد هنگامی فراخوانی می‌شود که با یک کامنت مواجه شود (مثلاً <!--comment-->).

برای مثال، کامنت <!-- comment --> باعث می‌شود این متد با آرگومان ' comment ' فراخوانی شود.

محتوای کامنت‌های شرطی Internet Explorer (condcoms) نیز به این متد ارسال می‌شود، بنابراین، برای <!--[if IE 9]>IE9-specific content<![endif]-->، این متد '[if IE 9]>IE9-specific content<![endif]' را دریافت خواهد کرد.

HTMLParser.handle_decl(decl)

این متد برای پردازش اعلامیه‌ی نوع سند (doctype) در HTML فراخوانی می‌شود (مثلاً <!DOCTYPE html>).

پارامتر decl تمام محتوای اعلامیه درون نمادگذاری <!...> خواهد بود (برای مثال 'DOCTYPE html').

HTMLParser.handle_pi(data)

متدی که هنگام مواجهه با یک دستورالعمل پردازشی فراخوانی می‌شود. پارامتر data شامل کل دستورالعمل پردازشی خواهد بود. برای مثال، برای دستورالعمل پردازشی <?proc color='red'>، این متد به‌صورت handle_pi("proc color='red'") فراخوانی می‌شود. این متد در نظر گرفته شده است تا توسط یک کلاس مشتق‌شده بازنویسی شود؛ پیاده‌سازی کلاس پایه هیچ کاری انجام نمی‌دهد.

توجه

کلاس HTMLParser از قواعد سینتکسی SGML برای دستورالعمل‌های پردازشی استفاده می‌کند. یک دستورالعمل پردازشی XHTML که از '?' پایانی استفاده می‌کند، باعث می‌شود که '?' در data گنجانده شود.

HTMLParser.unknown_decl(data)

این متد زمانی فراخوانی می‌شود که یک اعلامیه ناشناخته توسط پارسر خوانده شود.

پارامتر data تمام محتوای اعلان داخل نمادگذاری <![...]> خواهد بود. گاهی بازنویسی آن توسط یک کلاس مشتق‌شده مفید است. پیاده‌سازی کلاس پایه هیچ کاری انجام نمی‌دهد.

مثال‌ها

کلاس زیر یک پارسر را پیاده‌سازی می‌کند که برای نمایش مثال‌های بیشتر استفاده خواهد شد:

from html.parser import HTMLParser
from html.entities import name2codepoint

class MyHTMLParser(HTMLParser):
    def handle_starttag(self, tag, attrs):
        print("Start tag:", tag)
        for attr in attrs:
            print("     attr:", attr)

    def handle_endtag(self, tag):
        print("End tag  :", tag)

    def handle_data(self, data):
        print("Data     :", data)

    def handle_comment(self, data):
        print("Comment  :", data)

    def handle_entityref(self, name):
        c = chr(name2codepoint[name])
        print("Named ent:", c)

    def handle_charref(self, name):
        if name.startswith('x'):
            c = chr(int(name[1:], 16))
        else:
            c = chr(int(name))
        print("Num ent  :", c)

    def handle_decl(self, data):
        print("Decl     :", data)

parser = MyHTMLParser()

تجزیه‌ی doctype:

>>> parser.feed('<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" '
...             '"http://www.w3.org/TR/html4/strict.dtd">')
Decl     : DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd"

تجزیه‌ی یک عنصر با چند ویژگی و یک عنوان:

>>> parser.feed('<img src="python-logo.png" alt="The Python logo">')
Start tag: img
     attr: ('src', 'python-logo.png')
     attr: ('alt', 'The Python logo')
>>>
>>> parser.feed('<h1>Python</h1>')
Start tag: h1
Data     : Python
End tag  : h1

محتوای المان‌هایی مانند script و style همان‌طور که هست، بدون تجزیه بیشتر بازگردانده می‌شود:

>>> parser.feed('<style type="text/css">#python { color: green }</style>')
Start tag: style
     attr: ('type', 'text/css')
Data     : #python { color: green }
End tag  : style

>>> parser.feed('<script type="text/javascript">'
...             'alert("<strong>hello! &#9786;</strong>");</script>')
Start tag: script
     attr: ('type', 'text/javascript')
Data     : alert("<strong>hello! &#9786;</strong>");
End tag  : script

نام ویژگی‌ها به حروف کوچک تبدیل می‌شود، علامت‌های نقل‌قول از مقادیر ویژگی‌ها حذف می‌شود، و None به‌عنوان مقدار برای ویژگی‌های خالی (مانند checked) برگردانده می‌شود:

>>> parser.feed("<input TYPE='checkbox' checked required='' disabled=disabled>")
Start tag: input
     attr: ('type', 'checkbox')
     attr: ('checked', None)
     attr: ('required', '')
     attr: ('disabled', 'disabled')

تجزیه کامنت‌ها:

>>> parser.feed('<!--a comment-->'
...             '<!--[if IE 9]>IE-specific content<![endif]-->')
Comment  : a comment
Comment  : [if IE 9]>IE-specific content<![endif]

تجزیه‌ی ارجاع‌های نویسه‌ی نام‌دار و عددی و تبدیل آن‌ها به نویسه صحیح (توجه: این ۳ ارجاع همگی معادل '>' هستند):

>>> parser = MyHTMLParser()
>>> parser.feed('&gt;&#62;&#x3E;')
Data     : >>>

>>> parser = MyHTMLParser(convert_charrefs=False)
>>> parser.feed('&gt;&#62;&#x3E;')
Named ent: >
Num ent  : >
Num ent  : >

ارسال تکه‌های ناقص به feed() کار می‌کند، اما اگر convert_charrefs نادرست باشد، ممکن است handle_data() بیش از یک بار فراخوانی شود:

>>> for chunk in ['<sp', 'an>buff', 'ered', ' text</s', 'pan>']:
...     parser.feed(chunk)
...
Start tag: span
Data     : buff
Data     : ered
Data     :  text
End tag  : span

تجزیه‌ی HTML نامعتبر (برای مثال، ویژگی‌های بدون علامت نقل‌قول) نیز کار می‌کند:

>>> parser.feed('<p><a class=link href=#main>tag soup</p ></a>')
Start tag: p
Start tag: a
     attr: ('class', 'link')
     attr: ('href', '#main')
Data     : tag soup
End tag  : p
End tag  : a