HtmlParser,顾名思义,是python自带的解析Html的一个工具。
HTMLParser采用的是一种事件驱动的模式,当HTMLParser找到一个特定的标记时,它会去调用一个用户定义的函数,以此来通知程序处理。它主要的用户回调函数的命名都是以handler_开头的,都是HTMLParser的成员函数。
一、常用属性和方法介绍
HtmlParser是一个类,在使用时一般继承它然后重载它的方法,来达到解析出需要的数据的目的。
1.常用属性:
lasttag,保存上一个解析的标签名,是字符串。
2.常用方法:
handle_startendtag 处理自己结束的标签,如<img />
handle_starttag 处理开始标签,比如<xx>
handle_endtag 处理结束标签,比如</xx>
handle_charref 处理特殊字符串,就是以&#开头的,一般是内码表示的字符
handle_entityref 处理一些特殊字符,以&开头的,比如
handle_data 处理数据,就是<xx>data</xx>中间的那些数据
handle_comment 处理注释
handle_decl 处理<!开头的,比如<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
handle_pi 处理形如<?instruction>的东西
3、解析html时,一行行解析。每一行解析时都会调用handle_starttag
等方法。所以我们只要写出一套统一的针对每一行的操作方法,就可
以顺利解析出所有行。
解析时如何获取标签属性:
tag是的html标签,attrs是 (属性,值)元组(tuple)的列表(list).
如一个标签为:
<input type="hidden" name="NXX" id="IDXX" value="VXX" />
那么它的attrs列表为
[(‘type’, ‘hidden’), (‘name’, ‘NXX’), (‘id’, ‘IDXX’), (‘value’, ‘VXX’)]
HTMLParser自动将tag和attrs都转为小写。
解析时如何获取标签内容:
解析时碰到<***>,自动调用handle_starttag();碰到</***>,自动调用handle_endtag()
每一个标签,无论<> 还是</>,均会调用handle_data()
html中第一行、第二行分别为<html>和<head>,后面无具体数据,只有回车换行,所用调用handle_data(),打印结果为换行;</html></head>同理。
找一个网页,例如https://www.python.org/events/python-events/,用浏览器查看源码并复制,然后尝试解析一下HTML,输出Python官网发布的会议时间、名称和地点。
from html.parser import HTMLParser
from urllib import request
import sys,io
class MyHTMLParser(HTMLParser):
#__init__()方法意义重大的原因有两个。第一个原因是在对象生命周期
中初始化是最重要的一步;每个对象必须正确初始化后才能正常工作。第二
个原因是__init__()参数值可以有多种形式。
def __init__(self):
#利用父类构造函数
HTMLParser.__init__(self)
#list,即将开展的活动
self.upcoming = []
#list,错过的活动
self.missed = []
#每一个会议的数据
self.item = []
#是否错过的标志
self.ismiss = False
#tuple,我们关心的信息
self.flag = {
'h3': False,
'time': False,
'span':False
}
def handle_starttag(self, tag, attrs):
#按网页元素顺序判断,这样可以减少判断次数
#通过starttag来判断这一行是不是我们想要获取的信息以及是我们想获取的哪一类信息。将相关属性,例如self.flag['h3'],置为True.然后在handle_data中通过判断这些相关属性的值,来知道我们要不要记录这些值和这些值要归属在哪个相关属性上。
#如果标签是h3,并且属性class的值是‘widget-title just-
missed’,就将相关属性置为True
if tag=='h3' and self._attr(attrs,'class')=='widget-title just-missed':
self.ismiss = True
if tag=='h3' and self._attr(attrs,'class')=='event-title':
self.flag['h3'] = True
if tag=='time':
self.flag['time'] = True
if tag=='span' and self._attr(attrs,'class')=='event-location':
self.flag['span'] = True
def handle_data(self, data):
#利用完相关信息后,还要将相关属性恢复为默认值,以便解析下一行
if self.flag['h3']:
self.item.append(data)
self.flag['h3'] = False
if self.flag['time']:
self.item.append(data)
self.flag['time'] = False
if self.flag['span']:
self.item.append(data)
if not self.ismiss:
self.upcoming.append(self.item)
else:
self.missed.append(self.item)
self.flag['span'] = False
#当遍历到span元素时,说明为一轮,到下一轮时要重置
self.item = []
#这个方法用来获取我们想要的attrname的值。无论这一行有多少attr,调用这个函数_attr(attrs,attrname),都可以获取到我们想要的属性的值
def _attr(self,attrlist, attrname):
for attr in attrlist:
if attr[0] == attrname:
return attr[1]
return None
def getHtml():
sys.stdout = io.TextIOWrapper(sys.stdout.buffer,encoding='utf8') #改变标准输出的默认编码
with request.urlopen('https://www.python.org/events/python-events/') as f:
data = f.read().decode('utf-8')
return data
parser = MyHTMLParser()
parser.feed(getHtml())
#可以写个函数进行纯数据改造
print(parser.upcoming,parser.missed)