当前位置:首页>python>每天学习一点Python——用三种方法提取网页标题

每天学习一点Python——用三种方法提取网页标题

  • 2026-09-07 19:58:31
每天学习一点Python——用三种方法提取网页标题

每天学习一点Python——用三种方法提取网页标题

大家好,今天我们来做一个非常实用的练习:如何从一个网页中提取出标题。这看起来简单,但里面却藏着不少容易踩的坑。让我们通过实际代码一步步来探索。


准备工作:获取网页HTML

首先,我们需要从网上下载一个页面的HTML代码。这里我们用Python内置的urllib.request模块来完成任务。

from urllib.request import urlopen

url = "http://olympus.realpython.org/profiles/aphrodite"
page = urlopen(url)
html_bytes = page.read()
html = html_bytes.decode("utf-8")

print
(html)

代码讲解:

  • • from urllib.request import urlopen:从Python标准库中导入urlopen函数,这个函数可以打开一个网络地址。
  • • url = "...":定义要访问的网址,这里是一个专门用来练习的测试页面。
  • • page = urlopen(url):调用urlopen()打开网页,返回一个HTTPResponse对象,它包含了服务器的响应。
  • • html_bytes = page.read():使用.read()方法读取响应内容。注意,返回的是字节序列(bytes类型),而不是字符串。
  • • html = html_bytes.decode("utf-8"):将字节序列解码成字符串。"utf-8"是最常用的字符编码,绝大多数网页都使用它。
  • • print(html):打印出整个HTML源码。

执行结果:

<html>
<head>
<title>Profile: Aphrodite</title>
</head>
<body bgcolor="yellow">
<center>
<br><br>
<img src="/static/aphrodite.gif" />
<h2>Name: Aphrodite</h2>
<br><br>
Favorite animal: Dove
<br><br>
Favorite color: Red
<br><br>
Hometown: Mount Olympus
</center>
</body>
</html>

可以看到,我们成功获取了完整的HTML代码。现在,我们的目标是从中提取出<title>标签里的内容"Profile: Aphrodite"。


方法一:字符串查找(最容易踩坑)

第一种思路是直接用字符串的.find()方法定位标签位置,然后用切片提取。

步骤1:查找<title>标签的位置

title_index = html.find("<title>")
print
(title_index)

执行结果:

14

.find()方法返回子字符串第一次出现的起始索引。这里返回14,意思是字符串"<title>"从索引14开始。

💡 小Tip:初学者最容易困惑的地方

为什么是14?因为HTML中<html>和<head>后面各有一个换行符\n,它们也是字符串的一部分,占用索引位置。

具体索引分布:

  • • 0-5:<html>(6个字符)
  • • 6:\n(换行符)
  • • 7-12:<head>(6个字符)
  • • 13:\n(又一个换行符)
  • • 14:<(这才是<title>的开始)

字符串中的每个字符(包括空格、换行)都计入索引!

步骤2:计算标题开始位置

start_index = title_index + len("<title>")
print
(start_index)

执行结果:

21

len("<title>")返回7(因为<title>有7个字符)。14 + 7 = 21,所以标题内容从索引21开始。

步骤3:查找</title>标签的位置

end_index = html.find("</title>")
print
(end_index)

执行结果:

39

</title>的起始位置是39。

步骤4:提取标题

title = html[start_index:end_index]
print
(title)

执行结果:

Profile: Aphrodite

切片html[21:39]成功提取出了标题!

💡 小Tip:切片规则

切片[start:end]包含start索引的字符,但不包含end索引的字符。所以索引39的<没有被包含在内,这正是我们想要的。


方法二:正则表达式(更灵活)

字符串查找的方法虽然可行,但太脆弱了——如果HTML格式稍有变化(比如<title>中间多了个空格),代码就会出错。这时候,正则表达式就派上用场了。

先来熟悉正则表达式基础

import re

print
(re.findall("ab*c", "ac"))
print
(re.findall("ab*c", "abcd"))
print
(re.findall("ab*c", "acc"))
print
(re.findall("ab*c", "abcac"))
print
(re.findall("ab*c", "abdc"))

执行结果:

['ac']
['abc']
['ac']
['abc', 'ac']
[]

代码讲解:

  • • import re:导入Python的正则表达式模块。
  • • re.findall(模式, 字符串):在字符串中查找所有匹配模式的子串,返回列表。
  • • "ab*c":这是一个正则表达式模式,它的含义是:
    • • a:匹配字符a
    • • b*:*表示前面的字符(b)出现0次或多次
    • • c:匹配字符c
  • • 所以"ab*c"匹配的是:以a开头、以c结尾、中间有0个或多个b的字符串。

逐行解析:

  • • "ac":a + 0个b + c ✅ → ['ac']
  • • "abcd":a + 1个b + c → "abc" ✅ → ['abc']
  • • "acc":a + 0个b + c ✅ → ['ac']
  • • "abcac":找到两个匹配:"abc"和"ac" ✅ → ['abc', 'ac']
  • • "abdc":a后面是b,但再后面是d(不是c),所以不匹配 → []

忽略大小写匹配

print(re.findall("ab*c", "ABC"))
print
(re.findall("ab*c", "ABC", re.IGNORECASE))

执行结果:

[]
['ABC']

代码讲解:

  • • 第一个re.findall()没有指定标志位,默认区分大小写,所以找不到匹配。
  • • 第二个re.findall()增加了第三个参数re.IGNORECASE,这个标志让匹配忽略大小写,所以能找到"ABC"。

点号匹配任意字符

print(re.findall("a.c", "abc"))
print
(re.findall("a.c", "abbc"))

执行结果:

['abc']
[]

"a.c"中的.代表任意单个字符,所以:

  • • "abc":a + b(任意字符) + c ✅
  • • "abbc":a + b,但下一个字符是b不是c(模式只匹配三个字符)❌

.*匹配任意字符任意次数

print(re.findall("a.*c", "abc"))
print
(re.findall("a.*c", "abbc"))
print
(re.findall("a.*c", "ac"))

执行结果:

['abc']
['abbc']
['ac']

"a.*c"中的.*代表任意字符出现任意次数,所以:

  • • "abc":a + b + c ✅
  • • "abbc":a + bb + c ✅
  • • "ac":a + 0个字符 + c ✅

使用re.search()获取匹配对象

match_results = re.search("ab*c", "ABC", re.IGNORECASE)
print
(match_results.group())

执行结果:

ABC

代码讲解:

  • • re.search()和re.findall()不同,它返回第一个匹配的Match对象,而不是列表。
  • • match_results.group()从Match对象中提取出匹配的字符串。

re.sub()替换文本——小心贪婪匹配

string = "Everything is <replaced> if it's in <tags>."
string = re.sub("<.*>", "ELEPHANTS", string)
print
(string)

执行结果:

Everything is ELEPHANTS.

代码讲解:

  • • re.sub(模式, 替换文本, 原字符串):将字符串中匹配模式的部分替换成指定文本。
  • • "<.*>":这个模式匹配从第一个<到最后一个>之间的所有内容。
  • • 问题在于.*是贪婪匹配,它会尽可能匹配最长的字符串。所以它匹配了整个"<replaced> if it's in <tags>",而不是分别匹配两个标签。

使用非贪婪匹配.*?

string = "Everything is <replaced> if it's in <tags>."
string = re.sub("<.*?>", "ELEPHANTS", string)
print
(string)

执行结果:

Everything is ELEPHANTS if it's in ELEPHANTS.

代码讲解:

  • • "<.*?>":.*?是非贪婪匹配,它会尽可能匹配最短的字符串。
  • • 现在它分别匹配了<replaced>和<tags>两个标签,分别替换成"ELEPHANTS"。

💡 小Tip:贪婪 vs 非贪婪

  • • *、+等量词默认是贪婪的,会匹配尽可能多的字符。
  • • 在量词后面加?变成非贪婪(如*?、+?),会匹配尽可能少的字符。
  • • 在处理HTML标签时,通常需要用非贪婪匹配,避免把多个标签一起匹配掉。

用正则表达式提取标题

import re
from
 urllib.request import urlopen

url = "http://olympus.realpython.org/profiles/aphrodite"
page = urlopen(url)
html = page.read().decode("utf-8")

pattern = "<title.*?>.*?</title.*?>"
match_results = re.search(pattern, html, re.IGNORECASE)
title = match_results.group()
title = re.sub("<.*?>", "", title)

print
(title)

执行结果:

Profile: Aphrodite

代码讲解:

  • • pattern = "<title.*?>.*?</title.*?>":这个模式分三部分理解:
    1. 1. <title.*?>:匹配<title开头,后面可能有任意字符(如空格、属性),非贪婪匹配到第一个>结束。
    2. 2. .*?:非贪婪匹配标题内容。
    3. 3. </title.*?>:匹配</title开头,后面可能有任意字符,非贪婪匹配到第一个>结束。
  • • re.search(pattern, html, re.IGNORECASE):在html中搜索匹配pattern的内容,忽略大小写(这样即使HTML中是<TITLE>也能匹配)。
  • • match_results.group():从Match对象中提取出匹配的完整字符串,比如"<title>Profile: Aphrodite</title>"。
  • • re.sub("<.*?>", "", title):用空字符串替换所有HTML标签,只保留文本内容。
  • • print(title):打印最终提取出的标题。

方法三:BeautifulSoup HTML解析器(最专业)

正则表达式虽然强大,但处理复杂的HTML时容易出错。专业的HTML解析器BeautifulSoup是更好的选择。

首先需要安装:

pip install beautifulsoup4

创建BeautifulSoup对象

from bs4 import BeautifulSoup
from
 urllib.request import urlopen

url = "http://olympus.realpython.org/profiles/dionysus"
page = urlopen(url)
html = page.read().decode("utf-8")

soup = BeautifulSoup(html, "html.parser")

print
(soup)

代码讲解:

  • • from bs4 import BeautifulSoup:从bs4模块导入BeautifulSoup类。
  • • soup = BeautifulSoup(html, "html.parser"):创建BeautifulSoup对象。第一个参数是要解析的HTML字符串,第二个参数"html.parser"指定使用Python内置的HTML解析器。
  • • print(soup):打印解析后的对象,会显示格式化后的HTML。

执行结果:

<html>
<head>
<title>Profile: Dionysus</title>
</head>
<body bgcolor="yellow">
<center>
<br/><br/>
<img src="/static/dionysus.jpg"/>
<h2>Name: Dionysus</h2>
<img src="/static/grapes.png"/><br/><br/>
Hometown: Mount Olympus
<br/><br/>
Favorite animal: Leopard <br/>
<br/>
Favorite Color: Wine
</center>
</body>
</html>

提取纯文本

print(soup.get_text())

代码讲解:

  • • .get_text():提取HTML文档中的所有文本内容,自动去除HTML标签。注意方法名是小写的get_text,不是get_Text。

执行结果:


Profile: Dionysus



Name: Dionysus

Hometown: Mount Olympus

Favorite animal: Leopard 

Favorite Color: Wine


💡 小Tip:常见拼写错误

很多初学者会写成soup.get_Text(),但Python是区分大小写的,正确的写法是soup.get_text()(小写t)。

如果写成soup.get_Text(),会得到错误:

TypeError: 'NoneType' object is not callable

这是因为get_Text不存在,Python找到了一个叫get_Text的属性(值是None),然后试图调用它,当然会失败。

查找所有图片标签

img_tags = soup.find_all("img")
print
(img_tags)

执行结果:

[<img src="/static/dionysus.jpg"/>, <img src="/static/grapes.png"/>]

代码讲解:

  • • soup.find_all("img"):查找文档中所有<img>标签,返回一个列表。列表中的每个元素都是Tag对象,而不是字符串。

解包Tag对象并查看标签名

image1, image2 = soup.find_all("img")
print
(image1.name)

执行结果:

img

代码讲解:

  • • image1, image2 = soup.find_all("img"):将返回的列表解包赋值给两个变量。
  • • image1.name:每个Tag对象都有.name属性,返回标签名(这里是"img")。

访问标签属性

print(image1["src"])
print
(image2["src"])

执行结果:

/static/dionysus.jpg
/static/grapes.png

代码讲解:

  • • image1["src"]:通过字典风格的键访问,获取<img>标签的src属性值。
  • • 这比正则表达式简单多了!

直接访问特定标签

print(soup.title)
print
(soup.title.string)

执行结果:

<title>Profile: Dionysus</title>
Profile: Dionysus

代码讲解:

  • • soup.title:直接获取<title>标签,返回Tag对象。
  • • soup.title.string:获取标签内的文本内容。

带条件的查找

specific_img = soup.find_all("img", src="/static/dionysus.jpg")
print
(specific_img)

执行结果:

[<img src="/static/dionysus.jpg"/>]

代码讲解:

  • • soup.find_all("img", src="/static/dionysus.jpg"):查找所有<img>标签中,src属性等于指定值的标签。
  • • 这是BeautifulSoup的强大功能,可以根据标签属性精确定位。

三种方法对比

方法
优点
缺点
适用场景
字符串方法
无需安装库,简单直接
对格式变化敏感,易出错
非常规整的HTML,快速测试
正则表达式
灵活强大,可处理复杂模式
写起来复杂,可读性差
需要精确模式匹配的场景
BeautifulSoup
专业稳定,容错性强,代码简洁
需要安装第三方库
绝大多数网页解析任务

推荐:日常工作中,除非是非常简单的任务,否则建议直接用BeautifulSoup,省时省力又稳定。


总结

今天我们通过一个简单的“提取网页标题”的任务,学习了:

  1. 1. 字符串方法的基本操作:.find()查找位置、切片提取内容,以及要注意的索引计数问题
  2. 2. 正则表达式的基础知识:元字符含义、贪婪与非贪婪、常用函数findall()、search()、sub()
  3. 3. BeautifulSoup的基本使用:创建对象、提取文本、查找标签、访问属性

这三种方法各有千秋,掌握它们,你就拥有了处理各种HTML解析任务的工具箱。


📦 资源获取提示

关注「码农自习室」,后台回复关键词 Python学习,即可获取本文完整代码,一起动手掌握高效编程的核心技巧!


❤️ 支持我们

如果觉得本文对你有帮助,欢迎点赞 + 关注,您的支持是我们持续创作优质内容的最大动力!


📚 学习资源说明

本文内容整理自《Python基础教程(第3版)》第16章,所有命令均已实测。

最新文章

随机文章