html.parser.HTMLParser

1. 题目考点

这道题主要考:

  1. HTMLParser 的使用
  2. 类的继承
  3. 重写方法
  4. 字符串判断
  5. 多行输入拼接
  6. 按题目格式输出

这道题不是让我们自己手写 HTML 解析器,而是让我们使用 Python 已经提供好的工具:html.parser.HTMLParser


2. 审题

题目给你一段 HTML 代码,一共有 N 行。

你需要从这段 HTML 中找出三类内容:

内容类型示例输出标题
单行注释<!-- comment -->>>> Single-line Comment
多行注释<!-- line1\nline2 -->>>> Multi-line Comment
普通数据<div>Hello</div> 里面的 Hello>>> Data

注意题目要求:

Do not print data if data == '\n'

意思是:

如果 HTMLParser 读到的数据只是一个换行符 '\n',不要输出它。


3. 思路提示

这道题最关键的点是:

你不需要自己去判断哪里是标签、哪里是注释、哪里是数据。

Python 的 HTMLParser 会帮你解析 HTML。你只需要写一个类,继承它,然后重写对应的方法。

HTMLParser 在解析时会自动调用这些方法:

handle_comment(self, data)

遇到注释时自动调用。

handle_data(self, data)

遇到普通文本数据时自动调用。

所以整体方向是:

第一步,导入 HTMLParser

第二步,定义一个自己的解析器类,继承 HTMLParser

第三步,重写 handle_comment(),判断注释是单行还是多行。

第四步,重写 handle_data(),输出普通数据。

第五步,把输入的 N 行 HTML 拼成一个完整字符串,然后交给解析器处理。


4. 完整设计思路

第一步:导入工具

这道题要用:

from html.parser import HTMLParser

HTMLParser 是 Python 内置的 HTML 解析工具。


第二步:定义自己的解析器类

我们可以写:

class MyHTMLParser(HTMLParser):
    ...

意思是:

我创建一个自己的 HTML 解析器,它继承自 Python 已经写好的 HTMLParser


第三步:处理注释

HTML 注释长这样:

<!-- comment -->

HTMLParser 读到注释时,会自动调用:

handle_comment(self, data)

其中 data 就是注释里面的内容。

例如:

<!-- hello -->

传进来的 data 是:

hello

如果注释里面有换行符 \n,说明是多行注释。

所以可以这样判断:

if '\n' in data:
    print(">>> Multi-line Comment")
else:
    print(">>> Single-line Comment")

然后再打印注释内容:

print(data)

第四步:处理普通数据

普通数据就是标签之间的文字,例如:

<div>Welcome to HackerRank</div>

其中:

Welcome to HackerRank

就是 data。

HTMLParser 读到普通文本时,会自动调用:

handle_data(self, data)

但是题目说,如果 data == '\n',不要打印。

所以写成:

if data != '\n':
    print(">>> Data")
    print(data)

第五步:读取输入并拼接 HTML

输入第一行是整数 N

后面 N 行是 HTML 代码。

注意这里有一个很容易错的地方:

我们不能直接把每一行连起来,否则多行注释中的换行会丢失。

例如多行注释原本是:

<!-- line1
line2 -->

如果不加 '\n',就会变成:

<!-- line1line2 -->

这样程序就无法判断它是多行注释了。

所以每读取一行,都要加上换行符:

html += input() + '\n'

5. 代码实现

from html.parser import HTMLParser


class MyHTMLParser(HTMLParser):

    def handle_comment(self, data):
        if '\n' in data:
            print(">>> Multi-line Comment")
        else:
            print(">>> Single-line Comment")
        print(data)

    def handle_data(self, data):
        if data != '\n':
            print(">>> Data")
            print(data)


n = int(input())

html = ""
for _ in range(n):
    html += input() + '\n'

parser = MyHTMLParser()
parser.feed(html)

6. 运行演示

假设输入是:

4
<!--[if IE 9]>IE9-specific content
<![endif]-->
<div>Welcome to HackerRank</div>
<!--[if IE 9]>IE9-specific content<![endif]-->

程序先把这 4 行拼成一个完整 HTML 字符串。

遇到:

<!--[if IE 9]>IE9-specific content
<![endif]-->

里面有换行符,所以输出:

>>> Multi-line Comment
[if IE 9]>IE9-specific content
<![endif]

遇到:

<div>Welcome to HackerRank</div>

其中 Welcome to HackerRank 是普通数据,所以输出:

>>> Data
Welcome to HackerRank

遇到:

<!--[if IE 9]>IE9-specific content<![endif]-->

里面没有换行符,所以输出:

>>> Single-line Comment
[if IE 9]>IE9-specific content<![endif]

7. 方法总结

这一类题的关键不是自己从头解析字符串,而是识别题目想让你用哪个工具。

看到题目要求处理 HTML,并且要分别处理 comment、data、tag 这些内容时,应该马上想到:

HTMLParser

然后根据题目需要,重写对应的方法:

题目要处理的内容应该重写的方法
开始标签handle_starttag()
结束标签handle_endtag()
空标签handle_startendtag()
注释handle_comment()
普通数据handle_data()

本题只需要处理:

handle_comment()
handle_data()

所以代码并不长,难点主要在审题和理解 HTMLParser 的工作方式。


8. 小练习

请你尝试完成一个轻微变形题:

输入若干行 HTML 代码,要求打印所有普通数据 Data,但不打印注释。

示例输入:

3
<div>Hello</div>
<!-- hidden comment -->
<p>Python</p>

期望输出:

>>> Data
Hello
>>> Data
Python

提示:

只需要重写 handle_data(),不需要写 handle_comment()

我们引入了到底哪些工具,我们是怎么使用的?,我看这个代码,他好像并没有使用到?

这个疑问非常关键:代码里确实没有直接写“提取注释”或“提取 data”的逻辑,因为这件事不是我们手动做的,而是交给了 HTMLParser 这个工具去做。

我们引入的工具只有一个:

from html.parser import HTMLParser

这里的意思是:

从 Python 内置模块 html.parser 里面,导入一个叫 HTMLParser 的类。

它的作用是:帮我们解析 HTML 代码,并且在遇到不同内容时,自动调用对应的方法。


1. 我们到底引入了什么?

这句代码:

from html.parser import HTMLParser

可以拆开理解:

部分含义
html.parserPython 内置的 HTML 解析模块
HTMLParser模块里面提供的 HTML 解析器类
from ... import ...从某个模块中导入某个工具

所以我们引入的是:

HTMLParser

它不是一个普通函数,而是一个

你可以把它理解成 Python 官方已经写好的一个“HTML 分析机器”。


2. 为什么看起来好像没有用到?

其实用到了,在这里:

class MyHTMLParser(HTMLParser):

这一行就是在使用 HTMLParser

它的意思是:

我们自己定义一个类 MyHTMLParser,它继承 Python 官方提供的 HTMLParser

换句话说:

class MyHTMLParser(HTMLParser):

可以理解成:

我基于官方的 HTML 解析器,做一个自己的解析器。

官方的 HTMLParser 已经有“读取 HTML、识别标签、识别注释、识别文本”的能力。

我们自己写的 MyHTMLParser 只负责告诉它:

遇到注释时,我要怎么输出;

遇到普通数据时,我要怎么输出。


3. 真正启动解析的是哪一行?

真正让解析器开始工作的,是最后这一行:

parser.feed(html)

完整过程是:

parser = MyHTMLParser()
parser.feed(html)

第一行:

parser = MyHTMLParser()

意思是创建一个我们自己的 HTML 解析器对象。

第二行:

parser.feed(html)

意思是把 HTML 字符串喂给解析器,让它开始分析。

这里的 feed 可以理解为“喂入数据”。


4. 为什么 handle_comment()handle_data() 没有被我们手动调用?

这是这道题最容易困惑的地方。

代码里没有写:

parser.handle_comment(...)
parser.handle_data(...)

因为这两个方法不是我们主动调用的,而是 HTMLParser 在解析过程中自动调用的。

例如 HTML 是:

<div>Hello</div>
<!-- test -->

当执行:

parser.feed(html)

时,HTMLParser 会自动分析这段内容。

它看到:

<div>

知道这是开始标签。

它看到:

Hello

知道这是普通数据,于是自动调用:

handle_data("Hello")

它看到:

<!-- test -->

知道这是注释,于是自动调用:

handle_comment("test")

所以我们写的这两个方法,本质上是“回调方法”。

也就是说:

我们提前定义好规则,等 HTMLParser 遇到对应内容时,它会自动帮我们调用。


5. 这段代码每一部分到底在干什么?

我们把代码拆开看:

from html.parser import HTMLParser

导入官方 HTML 解析器。


class MyHTMLParser(HTMLParser):

定义自己的解析器,并继承官方解析器的能力。


def handle_comment(self, data):

告诉解析器:

当你遇到 HTML 注释时,请执行这里的代码。

比如遇到:

<!-- hello -->

这里的 data 就是:

hello

if '\n' in data:
    print(">>> Multi-line Comment")
else:
    print(">>> Single-line Comment")

判断这个注释里面有没有换行符。

有换行符,就是多行注释。

没有换行符,就是单行注释。


def handle_data(self, data):

告诉解析器:

当你遇到普通文本数据时,请执行这里的代码。

比如遇到:

<div>Hello</div>

这里的 data 就是:

Hello

if data != '\n':

题目要求如果数据只是换行符,就不要打印。

所以我们要过滤掉单独的 '\n'


parser = MyHTMLParser()
parser.feed(html)

创建解析器,并把 HTML 内容交给它处理。

从这一刻开始,HTMLParser 就会自动扫描 HTML,并自动调用我们写好的方法。


6. 用一个生活类比理解

你可以把 HTMLParser 想象成一个快递分拣机器。

HTML 内容里面有很多东西:

标签、注释、普通文字、换行

HTMLParser 负责识别它们属于哪一类。

我们写的代码相当于提前告诉机器:

如果你发现“注释”,就执行 handle_comment()

如果你发现“普通文字”,就执行 handle_data()

所以我们不需要自己写复杂的字符串查找规则。


7. 最核心的一句话

这道题的关键是:

HTMLParser 负责“识别 HTML 结构”,我们负责“定义遇到注释和数据时怎么输出”。

所以工具确实用到了,只是它不是像普通函数那样直接调用,而是通过:

class MyHTMLParser(HTMLParser)

和:

parser.feed(html)

间接发挥作用。


8. 小练习

请你判断下面代码中,哪一行真正开始让解析器工作?

from html.parser import HTMLParser

class MyHTMLParser(HTMLParser):
    def handle_data(self, data):
        print(data)

parser = MyHTMLParser()
parser.feed("<p>Hello</p>")

提示:不是 class 那一行,也不是 def handle_data 那一行。

文末附加内容
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇