میخواهیم یک فایل HTML را پردازش کرده و همچنین اطلاعاتی را از آن استخراج کنیم. برای این کار، باید کلاسی به نام `HTMLParser` طراحی کنید که ویژگیها و توابع زیر را دارا باشد:
```python HTMLParser.py
class HTMLParser:
def __init__(self, html_doc):
self.html_doc = html_doc
pass
def set_html_doc(self, html_doc):
self.html_doc = html_doc
def find_first(self, output_arg, **finding_args):
pass
def find_all(self, n, output_arg, **finding_args):
pass
def find_parent(self, output_arg, **finding_args):
pass
def find_grandparent(self, n, output_arg, **finding_args):
pass
def remove_comment(self, **finding_args):
pass
def remove_all_comments(self):
pass
def remove_tag(self, **finding_args):
pass
if __name__ == '__main__':
pass
```
پیش از شرح توابع و ویژگیها، مشخصههای جستجو `finding_args` و مشخصهٔ خروجی `output_arg` را شرح میدهیم:
<details class="red">
<summary>**مشخصههای جستجو** (`finding_args`)</summary>
یک دیکشنری است که میتواند یک یا چند کلید از میان `name` ، `id` ، `string` و `class` بپذیرد. برای مثال برای تگی مانند مثال زیر:
```html first.html
<p class="important" id="some-ID">you can <i>access</i> Quera <a href="http://www.quera.ir">here</a>!</p>
```
مشخصهٔ `name` برابر `p`، مشخصهٔ `id` برابر `some-ID`، مشخصهٔ `string` برابر `you can access Quera here!` و مشخصهٔ `class` برابر `important` میباشد. **تضمین میشود** مشخصههای جستجوی ورودی تنها شامل مشخصههای فوق میباشد.
</details>
<details class="red">
<summary>**مشخصهٔ خروجی** (`output_arg`)</summary>
یک `str` است که مشخصهٔ مطلوب خروجی تابع را مشخص میکند و **تضمین میشود** مقادیری همانند بالا دارد. در صورتی که مقدار متناظر مشخصهٔ خروجی مقداردهی نشده بود، خروجی یک رشتهٔ خالی خواهد بود.
```python terminal terminal
parser = HTMLParser("<b>some text.</b>")
>>> parser.find_first("class", name='b')
""
>>> parser.find_first("string", name='b')
"some text."
>>> parser.find_first("name", string="some text.")
'b'
```
</details>
## نمونه
برای مثال فراخوانی یک تابع با مشخصههای فوق، چنین است:
```python
parser.find_first("string", id="link", name='a')
```
ویژگیها و توابع این کلاس به شرح زیر هستند:
<details class="green">
<summary> **ویژگی** `html_doc`</summary>
کل کد `HTML` به فرمت یک رشته `str` است.
</details>
<details class="green">
<summary> **متد** `find_first`</summary>
مقدار مشخصهٔ خروجی (`output_arg`) اولین برچسب که با ویژگیهای `finding_args` مطابقت داشته باشد را خروجی میدهد.
</details>
<details class="green">
<summary> **متد** `find_all`</summary>
یک لیست به طول **حداکثر** `n` از مقدار مشخصههای خروجی برچسبهای مطابق با `finding_args` را برمیگرداند (`n`تای اول). اگر تعداد برچسبهای یافتهشده کمتر از `n` بود لیستی به اندازهٔ همان تعداد برمیگرداند.
مثال:
```python terminal terminal
doc = '<b id="1">first</b> <b id="2">second</b> <b>third</b>'
parser.set_html_doc(doc)
>>> parser.find_all(4, "id", name='b')
['1','2',""]
```
</details>
<details class="green">
<summary> **متد** `find_parent`</summary>
مقدار مشخصهٔ خروجی اولین والد برچسب مطابق با مشخصههای جستجو را خروجی میدهد. (در صورتی که بیش از یک برچسب یافت شد، مقدار مطلوب را برای اولین برچسب یافتهشده خروجی میدهد.) **تضمین میشود** برچسبهای یافتهشده حداقل یک والد دارند.
مثال:
```python terminal terminal
parser.set_html_doc("<p><b>example</b></p>")
>>>parser.find_parent("name", name='b', string='example')
'p'
```
</details>
<details class="green">
<summary> **متد** `find_grandparent`</summary>
مقدار مشخصهٔ خروجی `n`اُمین جد (اولین جد همان والد است.) اولین برچسب مطابق با مشخصههای جستجو را خروجی میدهد. در صورتی که `n` از تعداد جدهای برچسب یافته شده بیشتر باشد، استثنایی با پیام `No Such Parent` پرتاب شود. **تضمین میشود** برچسبهای یافتهشده حداقل یک والد دارند.
</details>
<details class="green">
<summary> **متد** `remove_comment`</summary>
اولین برچسب منطبق با مشخصهٔ ورودی را در صورتی که **فقط** محتوی کامنت باشد به طور کلی از `html_doc`حذف میکند. کامنتهای `HTML` به صورت زیر هستند:
```html comment.html
<!--Some Comments-->
```
```python terminal terminal
parser.set_html_doc('<b id="comment"><!--comment--></b>')
>>> parser.remove_comment(name='b')
>>> parser.html_doc
""
>>> parser.set_html_doc("<b>not a <!--comment--></b>")
>>> parser.remove_comment(name='b')
>>>parser.html_doc
"<b>not a <!--comment--></b>"
```
در صورتی که محتویات اولین برچسب یافتهشده عاری از کامنت بود، استثایی با پیام `No Comments Found` پرتاب شود.
</details>
<details class="green">
<summary> **متد** `remove_all_comments`</summary>
تمامی برچسبهای محتوی کامنت را از `html_doc` حذف میکند.
</details>
<details class="green">
<summary> **متد** `remove_tag`</summary>
اولین برچسب مطابق با مشخصات ورودی را به طور کلی از `html_doc` حذف میکند.
</details>
**توجه**: در تمامی متدهایی که میبایست برچسبی مطابق با مشخصات ورودی یافت شود، اگر برچسبی یافت نشد باید استثنایی با پیام `No Such Tag` پرتاب شود.
## نکات
+ میتوانید فایل اولیه این سوال را از [این لینک](/problemset/assignments/4367/download_problem_initial_project/21209/) دریافت کنید.
+ دقت کنید که در هیچکدام از توابع نباید مقداری را چاپ کنید؛ تنها در توابعی که گفته شده مقداری را بازگردانید، باید مقدار مورد نظر را `return` کنید.
## نحوه ارسال
فایل کامل شده `HTMLParser.py` را ارسال کنید؛ همچنین برای استفاده از کتابخانههای مختلف میتوانید همراه فایل ارسالی، فایلی به نام `python_requirements.txt` در *ZIP* خود بگذارید که در آن نام کتابخانههای مورد نیاز و شماره نسخهی آنها به فرمت زیر در آن موجود باشد: (اگر شماره نسخه را ننویسید آخرین نسخهی آن کتابخانه نصب میشود)
```
firstlib==1.2.3
secondlib==4.5.6
...
```
در نهایت یک فایل *ZIP* حاوی دو فایل خواسته شده را آپلود کنید.