这是从用Python开发开始到现在第二次使用HTMLParser模块进行html解析了,第一次用的时候,由于是刚刚接触Python,对其中的一些用法不是很理解,因为赶进度,虽然照着参考资料也写出来了,但是其中的原理还是不怎么了解。第二次用的时候,有一定的经验了,对Python的理解也更加深刻了,所以第二次用的时候,对HTMLParser模块的一些用法不像第一次用时那么茫然。结合鄙人第二次用该模块的经验,来讲讲HTMLParser模块的基本使用方法,希望对你有帮助。

    HTMLParser是python用来解析html的模块。它可以分析出html里面的标签、数据等等,是一种处理html的简便途径。HTMLParser采用的是一种事件驱动的模式,当HTMLParser找到一个特定的标记时,它会去调用一个用户定义的函数,以此来通知程序处理。它主要的用户回调函数的命名都是以handler_开头的,都是HTMLParser的成员函数。当我们使用时,就从HTMLParser派生出新的类,然后重新定义这几个以handler_开头的函数即可。这几个函数包括:

handle_startendtag(tag, attrs)  处理开始标签和结束标签

handle_starttag(tag, attrs)     处理开始标签,比如<xx>

handle_endtag(tag)       处理结束标签,比如</xx>

handle_charref(name)      处理特殊字符串,就是以&#开头的,一般是内码表示的字符

handle_entityref(name)    处理一些特殊字符,以&开头的,比如 &nbsp;

handle_data(data)         处理数据,就是<xx>data</xx>中间的那些数据

handle_comment(data)      处理注释

handle_decl(decl)         处理<!开头的,比如<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"

handle_pi(data)           处理形如<?instruction>的东西


tag是的html标签,attrs是 (属性,值)元组(tuple)的列表(list). 

HTMLParser自动将tag和attrs都转为小写。

下面以从中国银行官网上解析html文件以获取实时汇率(这就是我第二此用该模块时的解析任务)为例,来定义一个HTMLParser派生类。

在构造这个派生类之前,肯定要分析网页的源码的,找出你要获取的那一个值,在此不做介绍。直接上代码了,要仔细理解

#coding=utf-8
import HTMLParser
import requests
class MyHTMLParser(HTMLParser.HTMLParser):
    """

    """
    def __init__(self):
        HTMLParser.HTMLParser.__init__(self)
        self.result = []#记录结果
        self.flag = False#标记


    def handle_starttag(self,tag,attrs):
        if tag == "td" and self.flag == False and len(attrs) == 0:
        #attrs(属性,值)元组(tuple)的列表(list),由于我要获取的值的html标签的没有属性
        #所以要求它长度0以排除其它有属性的标签,缩小获取结果的范围。
        #如果有属性的话,attrs不会为空,它是元组列表可以用条件语句来判断
            self.flag = True

    def handle_end(self,tag):
        if tag == "td" and self.flag == True:
            self.flag = False

    def handle_data(self, data):
        if self.flag == True:
            self.result.append(data)

上面代码不难理解,都是重载HTMLParser类里的方法。

再接着就是爬取网页了,获取数据,代码如下:

def _filter_string(string):
    """
    除去字符串中的空格,换行等等
    由于网页源码中空格、换行符等会使返回的结果中带有换行符(\t)等,所以在此除去,
    其实也可以得到结果(返回的结果是个列表)后用列表的内建方法除去这些带有换行符的元素,
    其实这样用‘+’拼接字符串的效率很低,这个问题我也是刚刚发现,过段时间在研究一下,
    找到了好的方法,再跟大家分享下。
    """
    s = ''
    for one in string:
        if one != '\n' and one != '\r' and one != '\t' and one != ' ':
            s = s + one
    return s
    
def get_rate():
    """
    获取汇率
    Returns:

    """
    url = "http://srh.bankofchina.com/search/whpj/search.jsp"
    headers = {'user-agent':'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:46.0) Gecko/20100101 Firefox/46.0'}
    usd_data = {
                 'pjname':1316,#美元在中国银行官网的代码
                }
    hkd_data = {
                 'pjname':1315,#港币在中国银行官网的代码
                 }
    usd_result = requests.post(url, data=usd_data, headers=headers)
    hkd_result = requests.post(url, data=hkd_data, headers=headers)
    usd_parser = MyHTMLParser()
    hkd_parser = MyHTMLParser()
    usd_text = usd_result.text
    hkd_text = hkd_result.text
    # text = _filter_string(text)
    usd_text = _filter_string(usd_text)
    hkd_text = _filter_string(hkd_text)

    usd_parser.feed(usd_text)
    usd_parser.close()
    # print usd_result.text
    hkd_parser.feed(hkd_text)
    hkd_parser.close()
    usd_start = usd_parser.result.index(u"美元")  # 美元第一个出现的位置是最新值
    usd_rate = usd_parser.result[usd_start + 3]
    usd_rate = float(usd_rate) / 100  # 最新美元汇率
    usd_uptime = usd_parser.result[usd_start + 7]  # 发布时间
    hkd_start = hkd_parser.result.index(u"港币")
    hkd_rate = hkd_parser.result[hkd_start + 3]
    hkd_rate = float(hkd_rate) / 100
    hkd_uptime = hkd_parser.result[hkd_start + 7]  # 发布时间
    return usd_rate, usd_uptime, hkd_rate, hkd_uptime

HTMLParser类的其它一些方法:

HTMLParser.reset()

重置该实例。失去所有未处理的数据。这个在实例化对象时被隐含地调用。

HTMLParser.feed(data)

提供一些文本给解析器。在由完整元素组成的限度内进行处理,不完整的数据被缓冲直到更多的数据提供或者close()被调用。

HTMLParser.close()

强制将所有的缓冲数据按跟在结束标记的数据一样进行处理。该方法可以通过派生类定义对输入结尾的额外处理来进行重定义,但是重定义的版本应该总是调用HTMLParser基类方法close()

HTMLParser.getpos()

返回当前行数和位移值。

HTMLParser.get_starttag_text()

返回当前位置最近的开始标签的内容

这是给公司内部财务人员记账用的,所以做成了API供调用。隐约感觉上面代码最后几行获取结果的方法不是最佳方法,如果你有更好的方法,欢迎您与我分享,如果我找到了更好的我会及时分享给大家。

当然,如果是个简单的任务,用HTMLParser模块抓取一些想要的数据还是绰绰有余,如果是复杂的任务,有一定要求(比如性能要求)的任务,那就要用第三方库,比如Beautiful Soup,不过这个库我没用过,有时间研究一下,然后再和大家分享一下我的学习习得。


转载于:https://blog.51cto.com/11026142/1853831