在Python中高效使用HTMLParser
针对Python 正则表达式的问题,我尝试用HTMLParser
来实现一个HTML解析器:
import HTMLParser
class ExtractHeadings(HTMLParser.HTMLParser):
def __init__(self):
HTMLParser.HTMLParser.__init__(self)
self.text = None
self.headings = []
def is_relevant(self, tagname):
return tagname == 'h1' or tagname == 'h2'
def handle_starttag(self, tag, attrs):
if self.is_relevant(tag):
self.in_heading = True
self.text = ''
def handle_endtag(self, tag):
if self.is_relevant(tag):
self.headings += [self.text]
self.text = None
def handle_data(self, data):
if self.text != None:
self.text += data
def handle_charref(self, name):
if self.text != None:
if name[0] == 'x':
self.text += chr(int(name[1:], 16))
else:
self.text += chr(int(name))
def handle_entityref(self, name):
if self.text != None:
print 'TODO: entity %s' % name
def extract_headings(text):
parser = ExtractHeadings()
parser.feed(text)
return parser.headings
print extract_headings('abdk3<h1>The content we need</h1>aaaaabbb<h2>The content we need2</h2>')
print extract_headings('before<h1>Hello</h1>after')
在这个过程中,我在想这个模块的接口是不是不好,或者我是不是忽略了一些重要的东西。我的问题是:
- 为什么我实现的
handle_charref
要这么复杂?我本以为一个好的接口应该直接把代码点作为参数传递,而不是像x6c
或72
这样的字符串。 - 为什么
handle_charref
的默认实现不直接用合适的字符串来调用handle_data
? - 为什么没有一个可以直接调用的
handle_entityref
的实用实现?它可以叫做handle_entityref_HTML4
,然后查找HTML 4中定义的实体,再调用handle_data
。
如果有这样的接口,写自定义的HTML解析器会简单很多。那么我到底哪里理解错了呢?
2 个回答
0
你需要自己写一个解析器,还是可以用现成的呢?可以看看Beautiful Soup这个工具。
1
我同意,HTMLParser没有把HTML实体引用转换成普通的ASCII字符或其他字符,这确实是个大失误。我了解到在Python3中,这个问题是通过完全不同的方法解决的。
不过,我们似乎可以写一个相对简单的实体处理器,像这样:
import htmlentitydefs
def entity2char(x):
if x.startswith('&#x'):
# convert from hexadecimal
return chr(int(x[3:-1], 16))
elif x.startswith('&#'):
# convert from decimal
return chr(int(x[2:-1]))
elif x[1:-1] in htmlentitydefs.entitydefs:
return htmlentitydefs.entitydefs[x[1:-1]]
else:
return x
... 当然,我们还应该加上更多的输入验证,并且把整数转换的部分放在异常处理代码里。
这样的话,基本的功能大约只需要10行代码。加上异常处理后,可能会把行数翻倍。