8.3. 从 HTML 文档中提取数据
Description: Python from novice to pro
Keywords: Python, Dive Into Python, tutorial, object-oriented, programming, documentation, book, free

# 深入 Python :Dive Into Python 中文版 Python 从新手到专家 [Dip_5.4b_CPyUG_Release] | Find:

## 8.3. 从 HTML 文档中提取数据

为了从 HTML 文档中提取数据，将 `SGMLParser` 类进行子类化，然后对想要捕捉的标记或实体定义方法。

从 HTML 文档中提取数据的第一步是得到某个 HTML 文件。如果在您的硬盘里存放着 HTML 文件，您可以使用处理文件的函数将它读出来，但是真正有意思的是从实际的网页得到 HTML。

### 例 8.5. `urllib` 介绍

    >>> import urllib
    >>> sock = urllib.urlopen("http://diveintopython.org/")
    >>> htmlSource = sock.read()
    >>> sock.close()
    >>> print htmlSource
    <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"><html><head>
          <meta http-equiv='Content-Type' content='text/html; charset=ISO-8859-1'>
       <title>Dive Into Python</title>
    <link rel='stylesheet' href='diveintopython.css' type='text/css'>
    <link rev='made' href='mailto:mark@diveintopython.org'>
    <meta name='keywords' content='Python, Dive Into Python, tutorial, object-oriented, programming, documentation, book, free'>
    <meta name='description' content='a free Python tutorial for experienced programmers'>
    </head>
    <body bgcolor='white' text='black' link='#0000FF' vlink='#840084' alink='#0000FF'>
    <table cellpadding='0' cellspacing='0' border='0' width='100%'>
    <tr><td class='header' width='1%' valign='top'>diveintopython.org</td>
    <td width='99%' align='right'><hr size='1' noshade></td></tr>
    <tr><td class='tagline' colspan='2'>Python&nbsp;for&nbsp;experienced&nbsp;programmers</td></tr>

    [...略...]

| `urllib` 模块是标准 Python 库的一部分。它包含了一些函数，可以从基于互联网的 URL (主要指网页) 来获取信息并且真正取回数据。
| `urllib` 模块最简单的使用是提取用 `urlopen` 函数取回的网页的整个文本。打开一个 URL 同打开一个文件相似。`urlopen` 的返回值是像文件一样的对象，它具有一个文件对象一样的方法。
| 使用由 `urlopen` 所返回的类文件对象所能做的最简单的事情就是 `read`，它可以将网页的整个 HTML 读到一个字符串中。这个对象也支持 `readlines` 方法，这个方法可以将文本按行放入一个列表中。
| 当用完这个对象，要确保将它 `close`，就如同一个普通的文件对象。
| 现在我们将 `http://diveintopython.org/` 主页的完整的 HTML 保存在一个字符串中了，接着我们将分析它。

### 例 8.6. `urllister.py` 介绍

如果您还没有下载本书附带的样例程序, 可以 下载本程序和其他样例程序。

    from sgmllib import SGMLParser

    class URLLister(SGMLParser):
        def reset(self):
            SGMLParser.reset(self)
            self.urls = []

        def start_a(self, attrs):
            href = [v for k, v in attrs if k=='href']
            if href:
                self.urls.extend(href)

| `reset` 由 `SGMLParser` 的 `__init__` 方法来调用，也可以在创建一个分析器实例时手工来调用。所以如果您需要做初始化，在 `reset` 中去做，而不要在 `__init__` 中做。这样当某人重用一个分析器实例时，可以正确地重新初始化。
| 只要找到一个 `<a>` 标记，`start_a` 就会由 `SGMLParser` 进行调用。这个标记可以包含一个 `href` 属性，或者包含其它的属性，如 `name` 或 `title`。`attrs` 参数是一个 tuple 的 list，`[(attribute, value), (attribute, value), ...]`。或者它可以只是一个有效的 HTML 标记 `<a>` (尽管无用)，这时 `attrs` 将是个空 list。
| 我们可以通过一个简单的多变量 list 映射来查找这个 `<a>` 标记是否拥有一个 `href` 属性。
| 像 `k=='href'` 的字符串比较是区分大小写的，但是这里是安全的。因为 `SGMLParser` 会在创建 `attrs` 时将属性名转化为小写。

### 例 8.7. 使用 `urllister.py`

    >>> import urllib, urllister
    >>> usock = urllib.urlopen("http://diveintopython.org/")
    >>> parser = urllister.URLLister()
    >>> parser.feed(usock.read())
    >>> usock.close()
    >>> parser.close()
    >>> for url in parser.urls: print url
    toc/index.html
    #download
    #languages
    toc/index.html
    appendix/history.html
    download/diveintopython-html-5.0.zip
    download/diveintopython-pdf-5.0.zip
    download/diveintopython-word-5.0.zip
    download/diveintopython-text-5.0.zip
    download/diveintopython-html-flat-5.0.zip
    download/diveintopython-xml-5.0.zip
    download/diveintopython-common-5.0.zip

    ...略...

| 调用定义在 `SGMLParser` 中的 `feed` 方法，将 HTML 内容放入分析器中。 [4] 这个方法接收一个字符串，这个字符串就是 `usock.read()` 所返回的。
| 像处理文件一样，一旦处理完毕，您应该 `close` 您的 URL 对象。
| 您也应该 `close` 您的分析器对象，但出于不同的原因。`feed` 方法不保证对传给它的全部 HTML 进行处理，它可能会对其进行缓冲处理，等待接收更多的内容。只要没有更多的内容，就应调用 `close` 来刷新缓冲区，并且强制所有内容被完全处理。
| 一旦分析器被 `close`，分析过程也就结束了。`parser.urls` 中包含了在 HTML 文档中所有的链接 URL。(如果当您读到此处发现输出结果不一样，那是因为下载了本书的更新版本。)

### Footnotes

[4] 像 `SGMLParser` 这样的分析器，技术术语叫做消费者 (consumer)。它消费 HTML，并且拆分它。也许因此就选择了 `feed` 这个名字，以便同消费者 这个主题相适应。就个人来说，它让我想象在动物园看展览。里面有一个黑漆漆的兽穴，没有树，没有植物，没有任何生命的迹象。但只要您非常安静地站着，尽可能靠近着瞧，您会看到在远处的角落里有两只明眸在盯着您。但是您会安慰自已那不过是心理作用。唯一知道兽穴里并不是空无一物的方法，就是在栅栏上有一个不明显的标记，上面写着 “禁止给分析器喂食”。但也许只有我这么想，不管怎么样，这种心理想象很有意思。