Python网络编程 HTML解析
Html中的实体表示正规的字符。例如:有个Html实体:&,表示"&"。当为了工作而显示HTML代码的时候,通常要把这些HTML实体转换成纯文本格式。当出现实体时,HTMLParser会调用handle_entityref()方法,如果不定义这个方法,实体就会被忽略掉。当出现一个实体的时候,代码检查实体是否可识别。如果可以,则转换为相应的值。否则,就使用输入流中的文字值。
HTML中还包含字符参考。他们包含字符的十进制数值,类似®。用来嵌入那些不能打印的字符,如:非英文的文档会包含某些特定字符的字符参考如注册商标。
作为第三方插件,Tidy库的接口如:mxTidy or uTidylib同样可以被使用,可以把html转换到xml,同时还可以纠正html中编码错误,如标签遗漏对应结束
解析一个页面中的title:
#!/usr/bin/env python
# -*- coding:gb2312 -*-
from htmlentitydefs import entitydefs
from HTMLParser import HTMLParser
import sys
#定义一个HTMLParser.HTMLParser子类,并添加用来处理不同标签的函数
class TitleParser(HTMLParser):
def __init__(self):
self.title = ''
self.readingtitle = 0
HTMLParser.__init__(self)
def handle_starttag(self, tag, attrs):
if tag == 'title':
self.readingtitle = 1
def handle_data(self,data):
if self.readingtitle: #检查是否取得数据
#通常这是一个慢又差的方法
self.title += data
def handle_endtag(self, tag):
if tag == 'title':
self.readingtitle = 0
#处理html中的实体方法
def handle_entityref(self, name):
if entitydefs.has_key(name):
self.handle_data(entitydefs[name])
else:
self.handle_data('&' + name + ';')
#convert the character reference like ®商标符号
def handle_charref(self, name):
#Validate the name.
try:
charnum = int(name)
except ValueError:
return
if charnum < 1 or charnum > 255:
return
self.handle_data(chr(charnum))
def gettitle(self):
return self.title
#HTMLParser的feed()方法会适当地调用handle_starttag,handle_data,handle_endtag方法。
fd = open(sys.argv[1])
tp = TitleParser()
tp.feed(fd.read())
print "Title is:", tp.gettitle()
处理有结束标签缺失的HTML:
#!/usr/bin/env python
# -*- coding:gb2312 -*-
from htmlentitydefs import entitydefs
from HTMLParser import HTMLParser
import sys, re
#定义一个HTMLParser.HTMLParser子类,并添加用来处理不同标签的函数
class TitleParser(HTMLParser):
def __init__(self):
#the taglevels list keeps track of where we are in the tag hierarchy
self.taglevels = []
self.handledtags = ['title', 'ul', 'li']
self.processing = None
HTMLParser.__init__(self)
def handle_starttag(self, tag, attrs):
"""Called whenever a start tag is encountered."""
if len(self.taglevels) and self.taglevels[-1]==tag:
#Processing a previous version of this tag. Close it out
#and then start a new on this one.
self.handle_endtag(tag)
#Note that we're now processing this tag,只要是一个开始标签就添加
self.taglevels.append(tag)
#如果是一个我们要处理的标签
if tag in self.handledtags:
#Only bother saving off the data if it's a tag we handle.
self.data = ''
self.processing = tag
if tag == 'ul':
print "List started."
def handle_data(self,data):
"""This function simply records incoming data if we are presently inside a handled tag."""
if self.processing: #检查是否处理这个标签,如果是就记录其中的数据
#通常这是一个慢又差的方法
self.data += data
def handle_endtag(self, tag):
if not tag in self.taglevels:
#we didn't have a start tag for this anyway. Just ignore.
return
while len(self.taglevels):
#get the last tag on the list and remove it
starttag = self.taglevels.pop()
#finish processing it.
if starttag in self.handledtags:
self.finishprocessing(starttag)
#if it's our tag, stop now
if starttag == tag:
break
def cleanse(self):
"""Remove extra whitespace from the document."""
self.data = re.sub('\s+',' ',self.data)
def finishprocessing(self, tag):
self.cleanse()
if tag == 'title' and tag == self.processing:
print "Document title:", self.data
elif tag == 'ul':
print "List ended."
elif tag == 'li' and tag == self.processing:
print "List item:", self.data
self.processing = None
#处理html中的实体方法
def handle_entityref(self, name):
if entitydefs.has_key(name): //entitydefs中定义了一些实体的映射
self.handle_data(entitydefs[name])
else: //如果是没有定义的,就直接输出
self.handle_data('&' + name + ';')
#convert the character reference like ®商标符号
def handle_charref(self, name):
#Validate the name.
try:
charnum = int(name)
except ValueError:
return
if charnum < 1 or charnum > 255:
return
self.handle_data(chr(charnum))
def gettitle(self):
return self.title
#HTMLParser的feed()方法会适当地调用handle_starttag,handle_data,handle_endtag方法。
fd = open(sys.argv[1])
tp = TitleParser()
tp.feed(fd.read())
#print "Title is:", tp.gettitle()测试HTML:
<html>
<head>
<title>google & Com®
</head>
<body>
this is the text.
<UL>
<LI>first list Item.
<LI>Second List Item.</LI>
<LI>Third List Item.
</body>
</html>
从图中可以看出HTMLParser的执行过程:
依次读入数据文件,然后如果遇到一个开始标签就会自动执行handle_starttag,并获得标签名字存入参数tag中,如果遇到的是一个结束标签就自动执行handle_endtag(tag),他并不检测这个结束标签是否与开始标签相对应,如果是普通的数据就自动调用handle_data(data),所有这些方法在基类中都没有具体的功能,需要用户的派生类中的对方法进行重载,实现自己需要的功能。整个过程类似于事件处理机制,遇到一个标签就会回调相关的方法。注意图中的空行是因为标签后面虽然没有数据但换行符会调用handle_data(data)方法,当遇到实体&name;或字符引用&#ref;,也会调用相关的方法如:handle_entityref(self, name),如果不处理就会被忽略掉。
HTML中还包含字符参考。他们包含字符的十进制数值,类似®。用来嵌入那些不能打印的字符,如:非英文的文档会包含某些特定字符的字符参考如注册商标。
作为第三方插件,Tidy库的接口如:mxTidy or uTidylib同样可以被使用,可以把html转换到xml,同时还可以纠正html中编码错误,如标签遗漏对应结束
解析一个页面中的title:
#!/usr/bin/env python
# -*- coding:gb2312 -*-
from htmlentitydefs import entitydefs
from HTMLParser import HTMLParser
import sys
#定义一个HTMLParser.HTMLParser子类,并添加用来处理不同标签的函数
class TitleParser(HTMLParser):
def __init__(self):
self.title = ''
self.readingtitle = 0
HTMLParser.__init__(self)
def handle_starttag(self, tag, attrs):
if tag == 'title':
self.readingtitle = 1
def handle_data(self,data):
if self.readingtitle: #检查是否取得数据
#通常这是一个慢又差的方法
self.title += data
def handle_endtag(self, tag):
if tag == 'title':
self.readingtitle = 0
#处理html中的实体方法
def handle_entityref(self, name):
if entitydefs.has_key(name):
self.handle_data(entitydefs[name])
else:
self.handle_data('&' + name + ';')
#convert the character reference like ®商标符号
def handle_charref(self, name):
#Validate the name.
try:
charnum = int(name)
except ValueError:
return
if charnum < 1 or charnum > 255:
return
self.handle_data(chr(charnum))
def gettitle(self):
return self.title
#HTMLParser的feed()方法会适当地调用handle_starttag,handle_data,handle_endtag方法。
fd = open(sys.argv[1])
tp = TitleParser()
tp.feed(fd.read())
print "Title is:", tp.gettitle()
处理有结束标签缺失的HTML:
#!/usr/bin/env python
# -*- coding:gb2312 -*-
from htmlentitydefs import entitydefs
from HTMLParser import HTMLParser
import sys, re
#定义一个HTMLParser.HTMLParser子类,并添加用来处理不同标签的函数
class TitleParser(HTMLParser):
def __init__(self):
#the taglevels list keeps track of where we are in the tag hierarchy
self.taglevels = []
self.handledtags = ['title', 'ul', 'li']
self.processing = None
HTMLParser.__init__(self)
def handle_starttag(self, tag, attrs):
"""Called whenever a start tag is encountered."""
if len(self.taglevels) and self.taglevels[-1]==tag:
#Processing a previous version of this tag. Close it out
#and then start a new on this one.
self.handle_endtag(tag)
#Note that we're now processing this tag,只要是一个开始标签就添加
self.taglevels.append(tag)
#如果是一个我们要处理的标签
if tag in self.handledtags:
#Only bother saving off the data if it's a tag we handle.
self.data = ''
self.processing = tag
if tag == 'ul':
print "List started."
def handle_data(self,data):
"""This function simply records incoming data if we are presently inside a handled tag."""
if self.processing: #检查是否处理这个标签,如果是就记录其中的数据
#通常这是一个慢又差的方法
self.data += data
def handle_endtag(self, tag):
if not tag in self.taglevels:
#we didn't have a start tag for this anyway. Just ignore.
return
while len(self.taglevels):
#get the last tag on the list and remove it
starttag = self.taglevels.pop()
#finish processing it.
if starttag in self.handledtags:
self.finishprocessing(starttag)
#if it's our tag, stop now
if starttag == tag:
break
def cleanse(self):
"""Remove extra whitespace from the document."""
self.data = re.sub('\s+',' ',self.data)
def finishprocessing(self, tag):
self.cleanse()
if tag == 'title' and tag == self.processing:
print "Document title:", self.data
elif tag == 'ul':
print "List ended."
elif tag == 'li' and tag == self.processing:
print "List item:", self.data
self.processing = None
#处理html中的实体方法
def handle_entityref(self, name):
if entitydefs.has_key(name): //entitydefs中定义了一些实体的映射
self.handle_data(entitydefs[name])
else: //如果是没有定义的,就直接输出
self.handle_data('&' + name + ';')
#convert the character reference like ®商标符号
def handle_charref(self, name):
#Validate the name.
try:
charnum = int(name)
except ValueError:
return
if charnum < 1 or charnum > 255:
return
self.handle_data(chr(charnum))
def gettitle(self):
return self.title
#HTMLParser的feed()方法会适当地调用handle_starttag,handle_data,handle_endtag方法。
fd = open(sys.argv[1])
tp = TitleParser()
tp.feed(fd.read())
#print "Title is:", tp.gettitle()测试HTML:
<html>
<head>
<title>google & Com®
</head>
<body>
this is the text.
<UL>
<LI>first list Item.
<LI>Second List Item.</LI>
<LI>Third List Item.
</body>
</html>
从图中可以看出HTMLParser的执行过程:
依次读入数据文件,然后如果遇到一个开始标签就会自动执行handle_starttag,并获得标签名字存入参数tag中,如果遇到的是一个结束标签就自动执行handle_endtag(tag),他并不检测这个结束标签是否与开始标签相对应,如果是普通的数据就自动调用handle_data(data),所有这些方法在基类中都没有具体的功能,需要用户的派生类中的对方法进行重载,实现自己需要的功能。整个过程类似于事件处理机制,遇到一个标签就会回调相关的方法。注意图中的空行是因为标签后面虽然没有数据但换行符会调用handle_data(data)方法,当遇到实体&name;或字符引用&#ref;,也会调用相关的方法如:handle_entityref(self, name),如果不处理就会被忽略掉。