目录
- 一、正则表达式
- 1、常用元字符
- 2、量词
- 3、Re模块
- 4、爬取豆瓣电影
- 二、Xpath
- 1、Xpath解析
- Ⅰ、节点选择
- Ⅱ、路径表达式
- Ⅲ、常用函数
- 2、爬取豆瓣电影
解析数据,除了前面的BeautifulSoup库,还有正则表达式和Xpath两种方法。
一、正则表达式
正则表达式(简称RE)是一种用来描述和匹配字符串模式的工具。
它广泛应用于文本处理、数据验证、文本搜索和替换等场景。正则表达式使用一种特殊的语法,可以对字符串进行复杂的模式匹配。
正则表达式测试:在线正则表达式测试
1、常用元字符
元字符:具有固定含义的特殊符号。每个元字符,默认只匹配一个字符串,并且不能匹配换行符。
元字符 | 描述 | 示例 |
---|---|---|
. | 匹配除换行符以外的任意字符 | a.b 可以匹配 a1b 、acb |
\w | 匹配字母、数字或下划线 | \w+ 匹配 hello 、world_123 |
\s | 匹配任意的空白字符 | \s+ 匹配空格、制表符等 |
\d | 匹配数字 | \d+ 匹配 123 、456 |
\n | 匹配一个换行符 | hello\nworld 匹配换行符 |
\t | 匹配一个制表符 | hello\tworld 匹配制表符 |
^ | 匹配字符串的开始 | ^Hello 匹配 Hello 开头的字符串 |
$ | 匹配字符串的结束 | World$ 匹配 World 结尾的字符串 |
\W | 匹配非字母、非数字、非下划线的字符 | \W+ 匹配 !@# 、$%^ |
\D | 匹配非数字字符 | \D+ 匹配 abc 、XYZ |
\S | 匹配非空白字符 | \S+ 匹配 hello 、world123 |
`a | b` | 匹配字符 a 或字符 b |
(...) | 捕获括号内的表达式,表示一个组 | (abc) 捕获 abc |
[...] | 匹配方括号中的任意字符 | [abc] 匹配 a 、b 或 c |
[^...] | 匹配不在方括号中的任意字符 | [^abc] 匹配除 a 、b 、c 之外的任意字符 |
2、量词
量词:控制前面的元字符出现的次数
量词 | 描述 |
---|---|
* | 重复零次或更多次 |
+ | 重复一次或更多次 |
? | 重复零次或一次 |
{n} | 重复n次 |
{n,} | 重复n次或更多次 |
{n,m} | 重复n到m次 |
惰性匹配.*?
:尽可能少地匹配字符。在重复元字符后加 ?
实现惰性匹配。
贪婪匹配.*
:尽可能多地匹配字符。默认的重复元字符都是贪婪的。
3、Re模块
在Python中使用处理正则表达式,可以使用 re
模块,这个模块提供了一系列用于搜索、匹配和操作字符串的函数。
函数 | 描述 |
---|---|
re.search(pattern, string, flags=0) | 搜索字符串,返回第一个匹配的对象;若无匹配返回 None |
re.match(pattern, string, flags=0) | 从字符串起始位置匹配模式;若匹配成功返回匹配对象,否则 None |
re.fullmatch(pattern, string, flags=0) | 整个字符串完全匹配模式返回匹配对象,否则返回 None |
re.findall(pattern, string, flags=0) | 返回字符串中所有非重叠匹配的列表 |
re.finditer(pattern, string, flags=0) | 返回字符串中所有非重叠匹配的迭代器 |
re.sub(pattern, repl, string, count=0, flags=0) | 用替换字符串替换匹配模式的所有部分,返回替换后的字符串 |
re.split(pattern, string, maxsplit=0, flags=0) | 根据模式匹配分割字符串,返回分割后的列表 |
import re
# 示例文本
text = "在2024年,Python是最受欢迎的编程语言之一。Python 3.9版本在2020年发布。"
# 1. re.search() 搜索字符串,返回第一个匹配的对象
# 查找第一个数字序列
search_result = re.search(r'\d+', text)
if search_result:
print(f"re.search: 找到的第一个数字是 '{search_result.group()}',位置在 {search_result.start()}")
# 2. re.match() 从字符串起始位置匹配模式
# 匹配字符串开头是否为 '在'
match_result = re.match(r'在', text)
if match_result:
print(f"re.match: 匹配的字符串是 '{match_result.group()}',位于字符串的开始")
# 3. re.fullmatch() 整个字符串完全匹配模式
# 检查整个字符串是否只包含中文字符
fullmatch_result = re.fullmatch(r'[\u4e00-\u9fff]+', '在编程')
if fullmatch_result:
print(f"re.fullmatch: 整个字符串完全匹配,匹配到的内容是 '{fullmatch_result.group()}'")
# 4. re.findall() 返回字符串中所有非重叠匹配的列表
# 查找所有的数字序列
findall_result = re.findall(r'\d+', text)
print(f"re.findall: 找到的所有数字序列是 {findall_result}")
# 5. re.finditer() 返回字符串中所有非重叠匹配的迭代器
# 查找所有的数字序列,并逐一输出
finditer_result = re.finditer(r'\d+', text)
for match in finditer_result:
print(f"re.finditer: 找到的数字是 '{match.group()}',位置在 {match.start()}")
# 6. re.sub() 用替换字符串替换匹配模式的所有部分
# 将所有数字替换为 '#'
sub_result = re.sub(r'\d+', '#', text)
print(f"re.sub: 替换后的字符串是: {sub_result}")
# 7. re.split() 根据模式匹配分割字符串
# 按照空白字符或标点分割字符串
split_result = re.split(r'[,。 ]+', text)
print(f"re.split: 分割后的字符串列表是: {split_result}")
4、爬取豆瓣电影
从<li>
标签开始,逐步匹配到包含电影名的<span class="title">
标签,使用非贪婪模式(.*?
)匹配中间可能存在的任意字符,直到找到下一个明确的标记,用命名捕获组(?P<name>)
提取出电影名部分。
Re表达式写法:
<li>.*?<div class="item">.*?<span class="title">(?P<name>.*?)</span>
爬虫代码:
import requests
import re
from bs4 import BeautifulSoup
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36 Edg/125.0.0.0"
}
for start_num in range(0, 250, 25):
response = requests.get(f"https://movie.douban.com/top250?start={start_num}", headers=headers)
# 拿到页面源代码
html = response.text
# 使用re解析数据
obj = re.compile(r'<li>.*?<div class="item">.*?<span class="title">(?P<name>.*?)</span>',re.S)
# 开始匹配
result = obj.finditer(html)
# 打印结果
for it in result:
print(it.group('name'))
二、Xpath
Xpath是在XML文档中搜索的一门语言,它可以通过路径表达式来选择节点或节点集,HTML是XML的一个子集。
安装lxml模块: pip install lxml
1、Xpath解析
Ⅰ、节点选择
符号 | 解释 |
---|---|
/ | 从根节点选择。 |
// | 从匹配选择的当前节点选择文档中的节点,而不考虑它们的位置。 |
. | 选择当前节点。 |
.. | 选择当前节点的父节点。 |
@ | 选择属性。 |
Ⅱ、路径表达式
表达式 | 解释 |
---|---|
/bookstore/book | 选择bookstore节点下的所有book子节点。 |
//book | 选择文档中的所有book节点,不考虑它们的位置。 |
bookstore/book[1] | 选择bookstore节点下的第一个book子节点。 |
//title[@lang] | 选择所有具有lang属性的title节点。 |
//title[@lang='en'] | 选择所有lang属性为’en’的title节点。 |
Ⅲ、常用函数
text()
: 选择元素的文本。@attr
: 选择元素的属性。contains()
: 判断包含关系。starts-with()
: 判断开始部分。
from lxml import etree
html_content = '''
<html>
<body>
<div class="movie">
<span class="title">肖申克的救赎</span>
<span class="title">The Shawshank Redemption</span>
</div>
<div class="movie">
<span class="title">霸王别姬</span>
<span class="title">Farewell My Concubine</span>
</div>
</body>
</html>
'''
# 解析HTML
tree = etree.HTML(html_content)
# 提取电影标题
titles_cn = tree.xpath('//div[@class="movie"]/span[@class="title"][1]/text()')
titles_en = tree.xpath('//div[@class="movie"]/span[@class="title"][2]/text()')
# 打印结果
for cn, en in zip(titles_cn, titles_en):
print(f'中文标题: {cn}, 英文标题: {en}')
//div[@class="movie"]/span[@class="title"][1]/text()
//div[@class="movie"]
:选择所有class为movie
的div元素。
/span[@class="title"][1]
:选择每个div中class为title
的第一个span元素。
/text()
:获取span元素的文本内容。
//div[@class="movie"]/span[@class="title"][2]/text()
类似上述表达式,但选择的是每个div中class为title
的第二个span元素。
2、爬取豆瓣电影
import requests
from lxml import etree
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36 Edg/125.0.0.0"
}
for start_num in range(0, 250, 25):
response = requests.get(f"https://movie.douban.com/top250?start={start_num}", headers=headers)
# 拿到页面源代码
html = response.text
# 使用lxml解析页面
html = etree.HTML(html)
# 提取电影名字
titles = html.xpath('//*[@id="content"]/div/div[1]/ol/li/div/div[2]/div[1]/a/span[1]/text()')
# 提取评分
ratings = html.xpath('//*[@id="content"]/div/div[1]/ol/li/div/div[2]/div[2]/div/span[2]/text()')
# 打印结果
for title, rating in zip(titles, ratings):
print(f"电影: {title} 评分: {rating}")