Python的HTMLParser库的用法

HTMLParser库的用法

如果我们要编写一个搜索引擎,第一步是用爬虫把目标网站的页面抓下来,第二步就是解析该HTML页面,看看里面的内容到底是新闻、图片还是视频。
假设第一步已经完成了,第二步应该如何解析HTML呢?
HTML本质上是XML的子集,但是HTML的语法没有XML那么严格,所以不能用标准的DOM或SAX来解析HTML。
好在Python提供了HTMLParser来非常方便地解析HTML,只需简单几行代码:

from HTMLParser import HTMLParser
from htmlentitydefs import name2codepoint
		
class MyHTMLParser(HTMLParser):

    def handle_starttag(self, tag, attrs):
        print('<%s>' % tag)

    def handle_endtag(self, tag):
        print('</%s>' % tag)

    def handle_startendtag(self, tag, attrs):
        print('<%s/>' % tag)

    def handle_data(self, data):
        print('data')

    def handle_comment(self, data):
        print('<!-- -->')

    def handle_entityref(self, name):
        print('&%s;' % name)

    def handle_charref(self, name):
        print('&#%s;' % name)

parser = MyHTMLParser()
parser.feed('<html><head></head><body><p>Some <a href=\"#\">html</a> tutorial...<br>END</p></body></html>')

feed()方法可以多次调用,也就是不一定一次把整个HTML字符串都塞进去,可以一部分一部分塞进去。
特殊字符有两种,一种是英文表示的 ,一种是数字表示的Ӓ,这两种字符都可以通过Parser解析出来。
小结
找一个网页,例如https://www.python.org/events/python-events/,用浏览器查看源码并复制,然后尝试解析一下HTML,输出Python官网发布的会议时间、名称和地点。

html.parser — 简单的HTML和XHTML解析器
源代码: Lib/html/parser.py


该模块定义了一个类HTMLParser,它用作解析HTML(超文本标记语言)和XHTML格式文本文件的基础。
class html.parser.HTMLParser(*, convert_charrefs=True)
创建一个能够解析无效标记的解析器实例。
如果convert_charrefs为True(默认值),则所有字符引用(script/style元素中的字符引用除外)将自动转换为相应的Unicode字符。
当遇到开始标记、结束标记、文本、注释和其它标记元素时,HTMLParser实例将被提供HTML数据并调用处理程序方法。 用户应该子类化HTMLParser并覆盖其方法以实现所需的行为。
此解析器不检查结束标记是否与开始标记匹配,或者通过关闭外部元素来隐式关闭的元素调用结束标记处理程序。
Python版本3.4中的修改:已添加convert_charrefs关键字参数。
Python版本3.5中的修改:参数convert_charrefs的值现在默认为True。

  1. 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>')

输出将是:
Encountered a start tag: html
Encountered a start tag: head
Encountered a start tag: title
Encountered some data : Test
Encountered an end tag : title
Encountered an end tag : head
Encountered a start tag: body
Encountered a start tag: h1
Encountered some data : Parse me!
Encountered an end tag : h1
Encountered an end tag : body
Encountered an end tag : html
2. HTMLParser方法
HTMLParser实例具有以下方法:
HTMLParser.feed(data)
将一些文本提供给解析器。 只要它由完整的元素组成,它就被处理; 缓存不完整的数据,直到输入更多数据或调用close()。 数据必须是str。
HTMLParser.close()
强制处理所有缓冲数据,就好像它后跟一个文件结束标记一样。 此方法可以由派生类重新定义,以在输入的末尾定义附加处理,但重新定义的版本应始终调用HTMLParser基类方法close()。
HTMLParser.reset()
重置实例。 丢失所有未处理的数据。 这在实例化时隐式调用。
HTMLParser.getpos()
返回当前行号和偏移量。
HTMLParser.get_starttag_text()
返回最近打开的开始标记的文本。 结构化处理通常不需要这样做,但在处理HTML“部署”或重新生成具有最小变化的输入(可以保留属性之间的空白等)时可能是有用的。
遇到数据或标记元素时会调用以下方法,并且要在子类中重写它们。 基类实现什么都不做(handle_startendtag()除外):
HTMLParser.handle_starttag(tag, attrs)
调用此方法来处理标记的开始(例如

)。
tag参数是转换为小写的标记的名称。 attrs参数是(名称,值)对的列表,包含在标记的<>括号内找到的属性。 该名称将被转换为小写,并且已删除该值中的引号,并且已替换字符和实体引用。
例如,对于标签 ,此方法将被调用为handle_starttag(‘a’, [(‘href’, ‘ https://www.cwi.nl/’)]).。
来自html.entities的所有实体引用都将替换为属性值。
HTMLParser.handle_endtag(tag)
调用此方法来处理元素的结束标记(例如
)。
tag参数是转换为小写的标记的名称。
HTMLParser.handle_startendtag(tag, attrs)
与handle_starttag()类似,但在解析器遇到XHTML样式的空标记(<img … />)时调用。 这个方法可能被需要这个特定词汇信息的子类所覆盖; 默认实现只调用handle_starttag()和handle_endtag()。
HTMLParser.handle_data(data)
调用此方法来处理任意数据(例如,文本节点和和的内容)。
HTMLParser.handle_entityref(name)
调用此方法来处理形式为&name;的命名字符引用(例如>),其中name是一般实体引用(例如’gt’)。 如果convert_charrefs为True,则永远不会调用此方法。
HTMLParser.handle_charref(name)
调用此方法来处理形式为&#NNN;和&#xNNN;的十进制和十六进制数字字符引用。例如,>的十进制等效值是>,而十六进制是>; 在这种情况下,该方法将接收’62’或’x3E’。 如果convert_charrefs为True,则永远不会调用此方法。
HTMLParser.handle_comment(data)
遇到注释时会调用此方法(例如 )。
例如,注释 将导致使用参数’ comment ‘调用此方法。
The content of Internet Explorer conditional comments (condcoms) will also be sent to this method, so, for , this method will receive ‘[if IE 9]>IE9-specific content<![endif]’.
Internet Explorer条件注释(condcoms)的内容也将发送到此方法,因此,对于 ,此方法将收到’[if IE 9]>IE9-特定内容<![endif]’。
HTMLParser.handle_decl(decl)
调用此方法来处理HTML doctype声明(例如)。
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参数将是<![…]>标记内声明的全部内容。由派生类重写有时很有用。基类实现什么都不做。
3. 示例
以下类实现了一个解析器,用于说明更多示例:

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!</strong>");</script>')
Start tag: script
     attr: ('type', 'text/javascript')
Data     : alert("<strong>hello!</strong>");
End tag  : script

解析注释:

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

解析命名和数字字符引用并将它们转换为正确的char(注意:这3个引用都等同于’>’):

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

将不完整的块提供给feed()可以工作,但handle_data()可能会被多次调用(除非convert_charrefs设置为True):

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

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值