杰瑞科技汇

Python HTMLParser如何实现网页下载?

html.parser 是 Python 标准库的一部分,所以您无需额外安装,可以直接导入使用,它不像 BeautifulSouplxml 那样功能强大和方便,但对于简单的解析任务,或者在不希望安装第三方依赖的场景下,它是一个非常轻量级的选择。

Python HTMLParser如何实现网页下载?-图1
(图片来源网络,侵删)

html.parser 的工作原理

html.parser 的工作方式是事件驱动的,它不会一次性将整个 HTML 文档解析成一个树状结构,而是当解析器遇到 HTML 的特定部分(如开始标签、结束标签、数据等)时,会触发相应的事件。

我们的核心任务就是创建一个自定义的类,继承自 html.parser.HTMLParser,然后重写这些特定的事件处理方法。


核心步骤

  1. 导入模块from html.parser import HTMLParser
  2. 创建子类:定义一个继承自 HTMLParser 的类。
  3. 重写方法:重写以下关键方法来处理不同的事件:
    • handle_starttag(tag, attrs): 当遇到一个开始标签时被调用(<div>)。
    • handle_endtag(tag): 当遇到一个结束标签时被调用(</div>)。
    • handle_data(data): 当遇到标签内的文本数据时被调用。
    • handle_startendtag(tag, attrs): 当遇到一个自闭合标签时被调用(<img />)。
  4. 实例化并使用:创建你自定义解析器的实例,然后用 feed() 方法喂给它 HTML 内容。
  5. 获取结果:在解析过程中,将提取的数据存储到你自定义类的属性中。

实战示例:从网页中提取所有链接

假设我们想从一个简单的 HTML 字符串中提取出所有 <a> 标签的 href 属性值。

示例代码

from html.parser import HTMLParser
import urllib.request # 用于从网络下载HTML内容
# 1. 创建一个自定义的解析器类
class MyHTMLParser(HTMLParser):
    def __init__(self):
        # 必须调用父类的初始化方法
        super().__init__()
        # 创建一个列表来存储找到的链接
        self.links = []
    # 2. 重写处理开始标签的方法
    def handle_starttag(self, tag, attrs):
        # 我们只关心 <a> 标签
        if tag == 'a':
            # attrs 是一个包含 (属性名, 属性值) 元组的列表
            # [('href', 'http://example.com'), ('class', 'link')]
            for attr in attrs:
                # 如果属性名是 'href'
                if attr[0] == 'href':
                    # 将链接添加到我们的列表中
                    self.links.append(attr[1])
                    print(f"找到链接: {attr[1]}")
    # (可选) 添加一个方法来获取最终结果
    def get_links(self):
        return self.links
# --- 主程序 ---
# 模拟一个HTML字符串
html_string = """
<html>
<head><title>一个测试页面</title></head>
<body>
    <h1>欢迎!</h1>
    <p>这是一个段落,里面有一个链接 <a href="https://www.python.org">Python官网</a>。</p>
    <p>这里是另一个链接,但可能没有 href:<a class="external-link">点击我</a></p>
    <div>
        <a href="/about.html">关于我们</a>
        <img src="logo.png" alt="Logo" />
    </div>
</body>
</html>
"""
# 3. 实例化我们的解析器
parser = MyHTMLParser()
# 4. 使用 feed() 方法将HTML内容喂给解析器
# 注意:feed() 方法可以处理不完整的HTML,多次调用也没问题
parser.feed(html_string)
# 5. 获取并打印结果
print("\n--- 所有提取的链接 ---")
all_links = parser.get_links()
for link in all_links:
    print(link)
# 清空解析器,以便下次使用(如果需要)
parser.close()

代码解释

  1. MyHTMLParser:我们定义了自己的解析器。
  2. __init__:调用了父类的 __init__,并初始化了一个 self.links 列表,用来存放我们找到的链接。
  3. handle_starttag(self, tag, attrs):这是核心。
    • 当解析器遇到任何开始标签(如 <html>, <p>, <a>),这个方法就会被调用。
    • tag 参数是标签名(如 'a')。
    • attrs 参数是一个元组列表,每个元组代表一个属性,格式如 ('属性名', '属性值')
    • 我们检查 tag 是否为 'a'
    • 如果是,我们就遍历 attrs 列表,查找属性名是否为 'href'
    • 如果找到了,就把它的值(URL)添加到 self.links 列表中,并打印出来。
  4. parser.feed(html_string):这行代码启动了整个解析过程,解析器会逐个字符地分析 html_string,并在遇到相应标签时调用我们重写的方法。
  5. parser.get_links():一个简单的辅助方法,用于获取最终收集到的所有链接。

如何从真实网站下载并解析 HTML?

上面的例子使用了字符串,我们来看如何从一个真实的 URL 下载 HTML 内容,然后用 html.parser 解析它,这里需要用到 urllib.request 模块。

Python HTMLParser如何实现网页下载?-图2
(图片来源网络,侵删)
from html.parser import HTMLParser
import urllib.request
import urllib.error
class NewsLinkParser(HTMLParser):
    def __init__(self):
        super().__init__()
        self.news_links = []
        # 我们可能只关心特定class的链接
        self.target_class = "news-title" 
    def handle_starttag(self, tag, attrs):
        # 假设新闻链接都在 <a> 标签里,并且有特定的 class
        if tag == 'a':
            # 检查是否有 'class' 属性
            for attr_name, attr_value in attrs:
                if attr_name == 'class' and self.target_class in attr_value:
                    # 还需要找到 href 属性
                    for href_name, href_value in attrs:
                        if href_name == 'href':
                            self.news_links.append(href_value)
                            print(f"找到新闻链接: {href_value}")
                            break # 找到href后就可以跳出内层循环了
    def get_news_links(self):
        return self.news_links
# --- 主程序:从网络获取数据并解析 ---
def fetch_and_parse(url):
    print(f"正在尝试从 {url} 下载内容...")
    try:
        # 创建一个请求对象,可以添加 User-Agent 等头部信息,模拟浏览器访问
        req = urllib.request.Request(url, headers={'User-Agent': 'Mozilla/5.0'})
        # 打开URL并读取内容
        with urllib.request.urlopen(req) as response:
            # 注意:一定要指定编码,否则可能会出错
            # response.info().get_param('charset', 'utf-8') 可以尝试从响应头获取编码
            html_content = response.read().decode('utf-8')
        print("内容下载成功,开始解析...")
        # 创建解析器实例
        parser = NewsLinkParser()
        # 喂给解析器
        parser.feed(html_content)
        # 获取结果
        links = parser.get_news_links()
        print("\n--- 所有提取的新闻链接 ---")
        for link in links:
            print(link)
        return links
    except urllib.error.URLError as e:
        print(f"无法打开 URL {url},错误: {e.reason}")
        return None
    except Exception as e:
        print(f"发生未知错误: {e}")
        return None
# 示例:抓取某个新闻网站首页的链接(请替换为真实且允许抓取的网站)
# 注意:直接抓取商业网站首页可能很复杂,这里仅为演示
# target_url = "https://www.some-news-site.com" 
# 为了演示,我们用一个更简单的、结构固定的页面
target_url = "https://www.w3schools.com/html/html_examples.asp" 
fetch_and_parse(target_url)

html.parser 的优缺点

优点

  1. 无需安装:作为 Python 标准库的一部分,开箱即用。
  2. 轻量级:没有外部依赖,适合简单的脚本或对依赖有严格限制的环境。
  3. 纯 Python 实现:不依赖 C 扩展,在所有平台上行为一致。

缺点

  1. 功能有限:不像 BeautifulSoup 那样提供便捷的 CSS 选择器(find, find_all)或强大的导航功能
Python HTMLParser如何实现网页下载?-图3
(图片来源网络,侵删)
分享:
扫描分享到社交APP
上一篇
下一篇