8.3. 从 HTML 文档中提取数据

要从 HTML 文档中提取数据,请继承 SGMLParser 类,并为要捕获的每个标签或实体定义方法。

HTML 文档中提取数据的第一步是获取一些 HTML 代码。如果您的硬盘驱动器上有一些 HTML 文件,则可以使用 文件函数 读取它,但真正的乐趣在于从实时网页获取 HTML 代码。

示例 8.5. 介绍 urllib

>>> import urllib                                       1
>>> sock = urllib.urlopen("https://diveintopythonbook.pythonlang.cn/") 2
>>> htmlSource = sock.read()                            3
>>> sock.close()                                        4
>>> print htmlSource                                    5
<!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:[email protected]'>
<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>

[...snip...]
1 urllib 模块是标准 Python 库的一部分。它包含用于获取有关基于 Internet 的 URL(主要是网页)的信息以及实际检索数据的函数。
2 urllib 最简单的用法是使用 urlopen 函数检索网页的全部文本。打开 URL 类似于 打开文件urlopen 的返回值是一个类似文件的对象,它具有一些与文件对象相同的方法。
3 urlopen 返回的类似文件的对象,最简单的操作是 read,它将网页的整个 HTML 代码读入单个字符串中。该对象还支持 readlines,它将文本逐行读入列表中。
4 使用完该对象后,请确保像普通文件对象一样对其进行 close 操作。
5 现在,您已经在字符串中获得了 https://diveintopythonbook.pythonlang.cn/ 主页的完整 HTML 代码,可以开始解析它了。

示例 8.6. 介绍 urllister.py

如果您尚未下载,则可以 下载本书中使用的此示例和其他示例


from sgmllib import SGMLParser

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

    def start_a(self, attrs):                     2
        href = [v for k, v in attrs if k=='href'] 3 4
        if href:
            self.urls.extend(href)
1 resetSGMLParser__init__ 方法调用,并且一旦创建了解析器实例,也可以手动调用它。因此,如果您需要进行任何初始化,请在 reset 中进行,而不是在 __init__ 中进行,以便在有人重复使用解析器实例时可以正确地重新初始化它。
2 每当 SGMLParser 找到 <a> 标签时,都会调用 start_a。该标签可能包含 href 属性,以及/或者其他属性,例如 nametitleattrs 参数是一个元组列表,[(attribute, value), (attribute, value), ...]。或者它可能只是一个 <a>,一个有效的(即使无用)HTML 标签,在这种情况下,attrs 将是一个空列表。
3 您可以使用简单的 多变量 列表推导式 找出此 <a> 标签是否具有 href 属性。
4 k=='href' 这样的字符串比较始终区分大小写,但在这种情况下是安全的,因为 SGMLParser 在构建 attrs 时会将属性名称转换为小写。

示例 8.7. 使用 urllister.py

>>> import urllib, urllister
>>> usock = urllib.urlopen("https://diveintopythonbook.pythonlang.cn/")
>>> parser = urllister.URLLister()
>>> parser.feed(usock.read())         1
>>> usock.close()                     2
>>> parser.close()                    3
>>> for url in parser.urls: print url 4
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


... rest of output omitted for brevity ...
1 调用 SGMLParser 中定义的 feed 方法,将 HTML 代码输入解析器。[1] 它接受一个字符串,这是 usock.read() 返回的内容。
2 与文件一样,您应该在使用完 URL 对象后立即对其进行 close 操作。
3 您也应该对解析器对象执行 close 操作,但原因不同。您已经读取了所有数据并将其提供给了解析器,但是不能保证 feed 方法实际上已经处理了您提供给它的所有 HTML 代码;它可能会对其进行缓冲,等待更多数据。请确保调用 close 来刷新缓冲区并强制完全解析所有内容。
4 解析器 close 后,解析完成,parser.urls 包含 HTML 文档中所有链接的 URL 的列表。(如果在您阅读本文时下载链接已更新,则您的输出可能会有所不同。)

脚注

[1] SGMLParser 这样的解析器的技术术语是 消费者:它消耗 HTML 并将其分解。据推测,选择名称 feed 是为了符合整个“消费者”主题。就我个人而言,这让我想起了动物园里的一个展览,那里只有一个黑暗的笼子,里面没有树木、植物或任何生命的迹象,但如果你一动不动地站在那里,仔细观察,你会发现两只圆圆的眼睛从最左边的角落里盯着你,但你确信那只是你的幻觉,你能分辨出整个东西不仅仅是一个空笼子的唯一方法是栏杆上的一个小小的、无伤大雅的标志,上面写着:“不要给解析器喂食。”但这也许只是我个人的想法。无论如何,这是一个有趣的画面。