میخواهیم یک فایل HTML را پردازش کرده و همچنین اطلاعاتی را از آن استخراج کنیم. برای این کار، باید کلاسی به نام `HTMLParser` طراحی کنید که ویژگیها و توابع زیر را دارا باشد:
فایل اولیه پروژه را از [اینجا](http://s9.picofile.com/file/8339068418/html_parser_initial.zip.html) دریافت کنید.
```python
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` را شرح میدهیم:
۱- `finding_args`: یک دیکشنری است که میتواند یک یا چند کلید از میان `name` ، `id` ، `string` و `class` بپذیرد. برای مثال برای تگی مانند مثال زیر:
```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` میباشد. **تضمین میشود** مشخصههای جستجوی ورودی تنها شامل مشخصههای فوق میباشد.
۲- `output_arg`: یک `str` است که مشخصهٔ مطلوب خروجی تابع را مشخص میکند و **تضمین میشود** مقادیری همانند بالا دارد. در صورتی که مقدار متناظر مشخصهٔ خروجی مقداردهی نشده بود، خروجی یک رشتهٔ خالی خواهد بود.
```python
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'
```
برای مثال فراخوانی یک تابع با مشخصههای فوق چنین است:
```python
parser.find_first("string", id="link", name='a')
```
ویژگیها و توابع این کلاس به شرح زیر هستند:
۱- ویژگی `html_doc`: کل کد `HTML` به فرمت یک رشته `str` است.
۲- متد `find_first`: مقدار مشخصهٔ خروجی (`output_arg`) اولین برچسب که با ویژگیهای `finding_args` مطابقت داشته باشد را خروجی میدهد.
۳- متد `find_all`: یک لیست به طول **حداکثر** `n` از مقدار مشخصههای خروجی برچسبهای مطابق با `finding_args` را برمیگرداند (`n`تای اول). اگر تعداد برچسبهای یافتهشده کمتر از `n` بود لیستی به اندازهٔ همان تعداد برمیگرداند.
مثال:
```python
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',""]
```
۴- متد `find_parent`: مقدار مشخصهٔ خروجی اولین والد برچسب مطابق با مشخصههای جستجو را خروجی میدهد. (در صورتی که بیش از یک برچسب یافت شد، مقدار مطلوب را برای اولین برچسب یافتهشده خروجی میدهد.) **تضمین میشود** برچسبهای یافتهشده حداقل یک والد دارند.
مثال:
```python
parser.set_html_doc("<p><b>example</b></p>")
>>>parser.find_parent("name", name='b', string='example')
>'p'
```
۵- متد `find_grandparent`: مقدار مشخصهٔ خروجی `n`اُمین جد (اولین جد همان والد است.) اولین برچسب مطابق با مشخصههای جستجو را خروجی میدهد. در صورتی که `n` از تعداد جدهای برچسب یافته شده بیشتر باشد، استثنایی با پیام `No Such Parent` پرتاب شود. **تضمین میشود** برچسبهای یافتهشده حداقل یک والد دارند.
۶- متد `remove_comment`: اولین برچسب منطبق با مشخصهٔ ورودی را در صورتی که **فقط** محتوی کامنت باشد به طور کلی از `html_doc`حذف میکند. کامنتهای `HTML` به صورت زیر هستند:
```html
<!--Some Comments-->
```
```python
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` پرتاب شود.
۷- متد `remove_all_comments`: تمامی برچسبهای محتوی کامنت را از `html_doc` حذف میکند.
۸- متد `remove_tag`: اولین برچسب مطابق با مشخصات ورودی را به طور کلی از `html_doc` حذف میکند.
### تذکر
در تمامی متدهایی که میبایست برچسبی مطابق با مشخصات ورودی یافت شود، اگر برچسبی یافت نشد باید استثنایی با پیام `No Such Tag` پرتاب شود.
## پاسخ ارسالی
در پاسخ ارسالی خود میتوانید از هر کتابخانهای که لازم میدانید استفاده کنید. اما در فایل ارسالی خود، علاوه بر فایل `HTMLParser.py` که حاوی راهحل شما است، باید فایلی با نام `python_requirements.txt` نیز موجود باشد که در آن نام کتابخانههای مورد نیاز و شمارهٔ نسخهٔ آنها به فرمت زیر در آن موجود باشد:
```
firstlib==1.2.3
secondlib==4.5.6
...
```
## ساختار فایل ارسالی
ساختار فایل `.zip` ارسالی شما باید به صورت زیر باشد:
```
<your_zip_file_name.zip>
├── HTMLParser.py
└── python_requirements.txt
```