自学Python第十五天-常用的HTML解析工具:bs4、xpath、re

news2024/10/6 20:28:14

自学Python第十五天-常用的HTML解析工具:bs4、xpath、re

  • BS4
    • 安装和引入
    • 开始使用
    • `find_all()` 方法获取标签
    • `find()` 方法获取标签
    • `select()` 方法获取标签,css 选择器
    • 从标签中获取数据
  • XPath
    • xpath 基础
    • xpath 语法规则
    • lxml 模块
      • `xpath()` 方法
  • RE
    • `match()` 方法
    • `search()` 方法
    • `findall()` 方法
    • `finditer()` 方法
    • `sub()` 方法
    • `subn()` 方法
    • `split()` 方法
    • `compile()` 方法
    • `flags` 参数
    • `match` 对象

之前应该写过关于 bs4、xpath、re 的python使用文章,但是找不到了。因为这3种工具在 html 解析中经常用到,所以重新写一遍。

在 python 学习中绕不过去的就是爬虫,学习爬虫绕不过去的就是HTML页面解析,而最常用的解析工具就是 BeautifulSoup4XPathRE 了。这三个工具的比较如下:

工具解析速度使用难度安装难度
bs最简单简单
lxml(xpath)简单一般
正则(re)最快困难无(内置)

BS4

BeautifulSoup 4 简称 BS4,是一个 HTML/XML 的解析器。它是基于 HTML DOM 文档的,会载入整个文档,解析整个 DOM 树,因此时间和内存开销会大很多,性能较低。但是其语法是基于 CSS Selector 的,所以学习和使用非常简单。

BS4中文文档

安装和引入

pip install beautifulsoup4
from bs4 import BeautifulSoup

开始使用

bs4 使用时,首先创建 Beautiful Soup 对象,然后使用该对象的对应方法来解析DOM获取需要的元素标签对象,最后使用该对象的对应方法获取需要的属性或文本数据。例如:

from bs4 import BeautifulSoup

html = """
<html><head><title>The Dormouse's story</title></head>
<body>
<p class="title" name="dromouse"><b>The Dormouse's story</b></p>
<p class="story">Once upon a time there were three little sisters; and their names were
<a href="http://example.com/elsie" class="sister" id="link1">Elsie</a>,
<a href="http://example.com/lacie" class="sister" id="link2">Lacie</a> and
<a href="http://example.com/tillie" class="sister" id="link3">Tillie</a>;
and they lived at the bottom of a well.</p>
<p class="story">...</p>
"""

# 创建 Beautiful Soup 对象
soup = BeautifulSoup(html, "lxml")

print(soup.prettify())

find_all() 方法获取标签

可以使用 find_all(self, name=None, attrs={}, recursive=True, string=None, limit=None, **kwargs) 方法来匹配相应的元素列表。该方法最常用的参数就是 nameattrsstring

  • name 参数可以传递标签名称字符串或列表,以及正则表达式匹配对象
# 根据标签名获取标签元素
ret_a = soup.find_all('a')
ret_img = soup.find_all('img')

# 根据标签名列表,返回匹配任一列表元素,即为或的关系
ret = soup.find_all(['a', 'img'])

# 根据正则表达式
ret_re = soup.find_all(re.compile('^b'))
  • attrs 参数可以根据标签的属性来匹配
# 匹配标签中 class 属性
ret_sister_1 = soup.find_all(attrs={'class': 'sister'})
# 简写
ret_sistet_2 = soup.find_all(class_='sister')	# 之所以使用 class_ 而不使用 class,是因为 class 是 python 关键字
ret_id = soup.find_all(id='link')
  • string 参数可以搜索文档中的文本字符串内容。与 name 一样,可以接受字符串、列表以及正则表达式
ret_1 = soup.find_all(string='Elsie')
ret_2 = soup.find_all(string=['Tillie', 'Elsie', 'Lacie'])
ret_3 = soup.find_all(string=re.compile('Dormouse'))

当然三个参数可以同时使用,以获取需要的匹配标签元素。

find() 方法获取标签

find 方法与 find_all() 方法一样,区别在于 find() 返回第一个匹配结果,而 find_all() 方法返回所有匹配结果列表。

select() 方法获取标签,css 选择器

bs4 可以直接使用 css 选择器语法作为 select() 方法的参数。需注意的是,返回值也是一个列表

# 选择 title 标签
soup.select('title')
# 选择 img 标签
soup.select('img')
# 类选择器
soup.select('.sister')
# id 选择器
soup.select('#link1')
# 层级选择器
soup.select('p #link1')
# 属性选择器
soup.select('a[class="sister"]')
soup.select('a[href="http://example.com/elsie"]')

从标签中获取数据

获取到标签对象后,可以使用一些方法获取具体需要的数据

  • get_text() 方法,可以获取文本内容
  • get() 方法,可以获取属性,参数为属性名
for attr in soup.select('a'):
	print(attr.get('href'))

XPath

XPath (XML Path Language)XML路径语言,最初时是作为在 XML 文档中查找需要的信息,现在也适用于 HTML 文档。

xpath 作为一种普遍使用的解析语法,有着广泛的作用。xpath 的解析速度不慢,学习和使用起来也算是简单,所以成为解析 html 文档最常用的方法之一。XPath可以很轻松的选择出想要的数据,提供了非常简单明了的路径选择表达式,几乎想要任何定位功能,XPath都可以很轻松的实现。

W3School官方文档

xpath 基础

在 xpath 中,每一个标签都称之为节点,最顶层的节点称为根节点
节点和根节点
学习 xpath 可以使用一些浏览器辅助工具:

  • Chrome浏览器插件: XPath Helper
  • Firefox浏览器插件:XPath Finder

注意: 这些工具是用来学习XPath语法的,可以在这些工具中测试和联系语法规则,当熟练掌握XPath的语法后就可以直接在代码中编写XPath而不一定非要用此工具。

xpath 语法规则

XPath使用路径表达式来选取文档中的节点或者节点集。

表达式描述
nodename选中该元素
/从根节点选取、或者是元素和元素间的过渡
//从匹配选择的当前节点选择文档中的节点,而不考虑它们的位置
.选取当前节点
..选取当前节点的父节点
@选取属性
text()选取文本
contains()测试是否包含特定字符

路径表达式

路径表达式结果
bookstore选择bookstore元素
/bookstore选取根元素 bookstore。注释:假如路径起始于正斜杠(/),则此路径始终代表到某元素的绝对路径!
bookstore/book选取属于 bookstore 的子元素的所有 book 元素
//book选取所有 book 子元素,而不管它们在文档中的位置
bookstore//book选择属于 bookstore 元素的后代的所有 book 元素,而不管它们位于 bookstore 之下的什么位置
//book/title/@lang选择所有的book下面的title中的lang属性的值
//book/title/text()选择所有的book下面的title的文本

查询特定节点

路径表达式结果
//title[@lang="eng"]选择lang属性值为eng的所有title元素
/bookstore/book[1]选取属于 bookstore 子元素的第1个 book 元素
/bookstore/book[last()]选取属于 bookstore 子元素的最后1个 book 元素
/bookstore/book[last()-1]选取属于 bookstore 子元素的倒数第2个 book 元素
/bookstore/book[position()>1]选择bookstore下面的book元素,从第2个开始选择
/bookstore/book[position()>1 and position()<4]选择bookstore下面的book元素,从第2个开始取到第4个元素
//book/title[text()='Harry Potter']选择所有book下的title元素,仅仅选择文本为Harry Potter的title元素
//book/title[contains(text(), 'arry')]选择所有book下的tiile元素中,文本包含 arry 的元素
//a[@href[contains(., 'about')]]选择所有 href 属性包含 ‘about’ 的 a 元素

注意点: 在XPath中,第一个元素的位置是1,最后一个元素的位置是last(),倒数第二个是last()-1

lxml 模块

python 中使用 xpath 最常用的模块就是 lxml 模块。

pip install lxml
from lxml import etree

使用此模块需要先将需要解析的文本转化为 Element 对象,Element 对象有 xpath 的方法

from lxml import etree

text = ''' <div> <ul> 
        <li class="item-1"><a href="link1.html">first item</a></li> 
        <li class="item-1"><a href="link2.html">second item</a></li> 
        <li class="item-inactive"><a href="link3.html">third item</a></li> 
        <li class="item-1"><a href="link4.html">fourth item</a></li> 
        <li class="item-0"><a href="link5.html">fifth item</a> 
        </ul> </div> '''

html = etree.HTML(text)

# 将Element对象转化为字符串
handled_html_str = etree.tostring(html).decode()
print(handled_html_str)

xpath() 方法

Element 对象的 xpath() 方法可以使用 xpath 语法来获取需要的对象或数据。注意返回的是列表,如果是元素对象,则是 Element 对象。所以也可以使用链式调用的方式来多层获取需要的数据。

# 获取数据列表,返回值为字符串列表
href_list = html.xpath("//li[@class='item-1']/a/@href")
title_list = html.xpath("//li[@class='item-1']/a/text()")
# 获取节点列表
li_list = html.xpath("//li[@class='item-1']")
# 从节点列表对象中继续使用 xpath 匹配查询
for li in li_list:
    item = dict()
    item["href"] = li.xpath("./a/@href")[0] if len(li.xpath("./a/@href")) > 0 else None
    item["title"] = li.xpath("./a/text()")[0] if len(li.xpath("./a/text()")) > 0 else None
    print(item)

RE

RE 模块是python中使用正则语法的模块,正则的语法比较复杂,另外起一篇文章学习。这里只有 re 模块的使用方法。re 模块是 python 的内置模块,所以可以直接引入

import re

re 模块的使用方式一般有两种:

  • 直接使用相应的匹配方法,将匹配字符串和待查找文本作为参数传入。
  • 将匹配字符串编译为一个Pattern对象,并用此对象的相关匹配方法来匹配目标待查找文本。

match 对象是 re 模块方法所返回的默认的匹配对象大部分匹配方法如果得到匹配结果,就会返回 match 对象。

match() 方法

match(pattern, string, flags=0) 方法可以从字符串开头开始检测是否于模式匹配。如果匹配成功,返回匹配对象,否则返回None

# 从开头检测字符串是否匹配
match = re.match(r'\d+', '123abc')
if match:
	print(match.group())		# 输出 123

search() 方法

search(pattern, string, flags=0) 方法可以在字符串中搜索并返回第一个匹配项。如果匹配成功,返回匹配对象,否则返回None

# 使用 search 方法在整个字符串中搜索匹配
search = re.search(r'\d+', 'abc123def')
if search:
    print(search.group())  # 输出: 123

findall() 方法

findall(pattern, string, flags=0) 方法会返回所有非重叠匹配项列表。如果匹配模式中有一个或多个捕获组(group),则会返回元组列表

# 使用 findall 方法找到所有匹配的数字
numbers = re.findall(r'\d+', 'abc123def456')
print(numbers)  # 输出: ['123', '456']

finditer() 方法

findall() 方法类似,不过返回值为一个迭代器,其中每一个元素都是一个匹配对象。

sub() 方法

sub(pattern, repl, string, count=0, flags=0) 方法可以将匹配项替换为 repl 参数的值,repl 可以是一个字符串或一个函数;如果是函数,每个匹配项都会作为参数传递给这个函数。count 用于指定最大替换次数;默认 0,替换所有匹配项。

# 使用 sub 方法替换所有的数字为 '#'
replaced = re.sub(r'\d+', '#', 'abc123def456')
print(replaced)  # 输出: abc#def#

subn() 方法

sub() 方法类似,不过返回值是一个包含新字符串和替换次数的元组

split() 方法

split(pattern, string, maxsplit=0, flags=0) 方法可以根据匹配项来分割字符串。maxsplit 用于指定最大分割次数;默认 0,表示分割所有匹配项。

# 使用 split 方法根据数字分割字符串
parts = re.split(r'\d+', 'abc123def456ghi')
print(parts)  # 输出: ['abc', 'def', 'ghi']

compile() 方法

compile(pattern, flags=0) 方法实际并不进行匹配,而是返回一个正则表达式匹配模式对象,这个对象可以使用 matchsearchfindall 等方法来进行匹配。常用于同一个正则表达式需要重复的与不同文本进行匹配的情况,避免重复编译相同的模式,提高效率。

p = re.compile(r'\d+')
search = p.search('abc123def')
if search:
    print(search.group())  # 输出: 123

flags 参数

几乎 re 模块的每种方法都有 flags 参数,该参数可以用于控制正则表达式的匹配方式:

简写说明
re.IGNORECASEre.I大小写不敏感。
re.MULTILINEre.M多行模式,改变 ^$ 的行为,使它们分别匹配每一行的开头和结尾,而不仅仅是整个字符串的开头和结尾。
re.DOTALLre.S使 . 特殊字符匹配任何字符,包括换行符。
re.UNICODEre.U根据 Unicode 字符集解析字符。这是 Python 3 中的默认行为。
re.ASCIIre.A使 \w, \W, \b, \B, \d, \D, \s\S 只匹配 ASCII 字符。
re.VERBOSEre.X允许在正则表达式中添加空白和注释。
import re

# 忽略大小写的匹配
case_insensitive = re.findall(r'abc', 'ABCabc', flags=re.IGNORECASE)
print(case_insensitive)  # 输出: ['ABC', 'abc']

# 多行模式的匹配
multiline = re.search(r'^abc', 'def\nabc', flags=re.MULTILINE)
if multiline:
    print(multiline.group())  # 输出: abc

# 让点号匹配换行符
dotall = re.search(r'a.b', 'a\nb', flags=re.DOTALL)
if dotall:
    print(dotall.group())  # 输出: a\nb

# 使用 ASCII 字符集
ascii_char = re.findall(r'\w+', 'café', flags=re.ASCII)
print(ascii_char)  # 输出: ['caf']

# 使用 VERBOSE 模式,允许正则表达式分行并添加注释
verbose = re.compile(r"""
    \b      # 单词边界
    \w+     # 一个或多个字母数字字符
    \b      # 单词边界
""", flags=re.VERBOSE)
print(verbose.findall('Hello, world!'))  # 输出: ['Hello', 'world']

match 对象

match 对象有一些常用的属性和方法,来获取需要的数据

属性说明
string返回传递给 matchsearch 等函数的原始字符串。
re返回用于匹配的正则表达式对象。
pos返回用于匹配的字符串的起始位置。
endpos返回用于匹配的字符串的结束位置。
lastindex返回最后一个被捕获的分组在 Match 对象中的索引。
lastgroup返回最后一个被捕获的分组的名称。
方法说明
group(num=0)返回整个匹配的字符串,或者指定编号的分组。
groups(default=None)返回一个包含所有捕获组的元组,如果没有匹配则为 default
groupdict(default=None)返回一个字典,包含所有命名的捕获组。
start([group])返回指定分组的起始位置。
end([group])返回指定分组的结束位置。
span([group])返回 (start(group), end(group))
import re

# 使用 search 方法查找数字
match = re.search(r'\d+', 'User ID: 12345')
if match:
    print(match.group())  # 输出匹配到的数字: 12345

# 使用捕获组
match = re.search(r'User ID: (\d+)', 'User ID: 12345')
if match:
    print(match.group(1))  # 输出第一个捕获组匹配到的内容: 12345

# 使用命名捕获组
match = re.search(r'User ID: (?P<id>\d+)', 'User ID: 12345')
if match:
    print(match.group('id'))  # 输出命名捕获组 'id' 匹配到的内容: 12345

# 获取匹配的起始和结束位置
match = re.search(r'ID', 'User ID: 12345')
if match:
    print(match.span())  # 输出匹配字符串 'ID' 的起始和结束位置: (5, 7)

# 获取所有捕获组
match = re.search(r'(\w+) (\w+)', 'Hello World')
if match:
    print(match.groups())  # 输出所有捕获组的内容: ('Hello', 'World')

# 获取所有命名捕获组
match = re.search(r'(?P<first>\w+) (?P<second>\w+)', 'Hello World')
if match:
    print(match.groupdict())  # 输出所有命名捕获组的内容: {'first': 'Hello', 'second': 'World'}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1476613.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

上拉电阻与下拉电阻、电容的作用

上拉电阻与下拉电阻 在单片机电路中&#xff0c;上拉电阻和下拉电阻都是常见的电路元件&#xff0c;它们在数字电路设计中扮演着重要的角色。它们的作用如下&#xff1a; 1. **上拉电阻**&#xff1a; - **作用**&#xff1a;当一个引脚没有外部信号时&#xff0c;上拉电阻…

本届挑战赛季军方案:基于图网络及LLM AGENT的微服务系统异常检测和根因定位方法

aiboco团队荣获本届挑战赛季军。该团队来自亿阳信通。 方案介绍 本届挑战赛采用开放式赛题&#xff0c;基于建行云龙舟运维平台的稳定性工具和多维监控系统&#xff0c;模拟大型的生活服务APP的生产环境&#xff0c;提供端到端的全链路的日志、指标和调用链数据。参赛队伍在组…

92. 递归实现指数型枚举 刷题笔记

思路 dfs 考虑选或者不选每个位置 用0表示未考虑 1表示选 2表示不选 用u表示搜索状态 u>n时 已经搜到底层了 需要输出当前方案 遍历 如果选了则输出 #include<iostream> using namespace std; int n; const int N16; int st[N]; void dfs(int u){ //u来记…

nginx------------缓存功能 (六)

一、http 协议反向代理 &#xff08;一&#xff09;反向代理示例:缓存功能 缓存功能可以加速访问&#xff0c;如果没有缓存关闭后端服务器后&#xff0c;图片将无法访问&#xff0c;缓存功能默认关闭&#xff0c;需要开启。 ​ proxy_cache zone_name | off; 默认off #指明调…

nginx之重写功能 模块指令 防盗链

一 重写功能 rewrite Nginx服务器利用 ngx_http_rewrite_module 模块解析和处理rewrite请求&#xff0c; 此功能依靠 PCRE(perl compatible regular expression)&#xff0c;因此编译之前要安装PCRE库&#xff0c;rewrite是 nginx服务器的重要功能之一&#xff0c;重写功…

【IO流】缓冲流

缓冲流 1. 概述2. 作用3. 字节缓冲流3.1 构造方法3.2 代码示例 4. 字符缓冲流4.1 构造方法4.2 特有方法4.3 代码示例4.3.1 readline()方法示例4.3.2 newline()方法示例 5. 字节缓冲流提高效率的原理6. 注意事项 文章中的部分照片来源于哔站黑马程序员阿伟老师处&#xff0c;仅用…

html2canvas + JsPDF.js 导出pdf分页时的问题

问题描述 前一段时间 实现了html2canvas jspdf.js 导出pdf的功能 项目当时没有测试做完就先搁置 最近项目要上线发现分页时问题 这篇文章记录一下之前的bug import html2canvas from html2canvas; import JsPDF from jspdf export function savePdf(el, title) {html2canva…

Keepalived双机热备——Haproxy搭建web群集

一、认识keepalived keepalived是一个开源的软件&#xff0c;用于实现高可用性和负载均衡。它主要用于在多个服务器之间提供故障转移和负载均衡的功能。keepalived可以监控服务器的状态&#xff0c;并在主服务器发生故障时自动将备份服务器切换为主服务器&#xff0c;以确保服…

2024.2.28 网络

思维导图 整理面试题 1、什么是回调函数 答&#xff1a;将函数作为参数传到另一个函数里面&#xff0c;当那个函数执行完之后&#xff0c;再执行传进去的这个函数。这个过程就叫做回调。 2、结构体和共用体的区别 答&#xff1a;结构体的每个成员都会分配内存&#xff0c;…

快讯|Tubi 更新内容库重新定义自己

在每月一期的 Tubi 快讯中&#xff0c;你将全面及时地获取 Tubi 最新发展动态&#xff0c;欢迎&#x1f31f;星标关注【比图科技】&#xff0c;一起成长变强&#xff01; Tubi 更新内容库&#xff0c;重新定义自己 Tubi 近日宣布为数千万用户免费提供备受观众喜爱、获奖无数的…

anaconda指定目录创建环境无效/环境无法创建到指定位置

已经设置目录到D盘 创建环境时还是分配到C盘 可能是指定位置没有开启读写权限&#xff0c;如我在这里安装到了anaconda文件夹&#xff0c;则打开该文件夹的属性->安全->编辑 allusers下的权限全都打勾

android开发电子书,android基础编程

内存泄漏是什么&#xff1f; 内存泄漏即 ML &#xff08;Memory Leak&#xff09; 指 程序在申请内存后&#xff0c;当该内存不需再使用 但 却无法被释放 & 归还给 程序的现象 内存泄漏有哪些情况&#xff0c;对应的解决方案&#xff1f; 内存泄漏的原因归根到底就是当需…

redis-RedisTemplate.opsForGeo 的geo地理位置及实现附近的人的功能

redis内部使用的是 zset 数据结构存储&#xff0c;如下 import cn.huawei.VideoApplication; import cn.huawei.domain.Jingqu; import cn.huawei.service.JingquService; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired…

【Java程序设计】【C00321】基于Springboot的在线租房和招聘平台(有论文)

基于Springboot的在线租房和招聘平台&#xff08;有论文&#xff09; 项目简介项目获取开发环境项目技术运行截图 项目简介 这是一个基于Springboot的在线租房和招聘平台&#xff0c;本系统有管理员、用户、房东以及公司四种角色&#xff1b; 管理员&#xff1a;首页、个人中心…

【DDD】学习笔记-领域驱动设计参考过程模型

通过领域驱动设计魔方&#xff0c;我们从业务、技术与管理三个维度引入了有助于领域驱动设计的方法和模式&#xff0c;同时梳理了影响领域驱动战略设计的架构因素&#xff0c;确定以“四个边界”为核心对领域逻辑进行控制&#xff0c;规定了领域驱动设计团队必须遵循的纪律&…

java常用环境docker安装

配置目录 rocketmqredismysql不配置binlog配置binlog Nacoszookeeper 本文为精简安装&#xff0c;部分不带容器卷映射&#xff0c;仅供以学习使用。 rocketmq nameservice docker run -d -p 9876:9876 --name rmqnamesrv rocketmqinc/rocketmq sh mqnamesrvbroker docker r…

小狐狸chat2.7.2免授权修复版可用版

小狐狸chat2.7.2免授权修复版可用版 在网络上面找了好几个版本不能使用&#xff0c;今天发布这个仔细测试正常使用 主要功能&#xff1a;独立版无限多开支持分销会员充值自己APP打包小程序万能创作MJ绘图多个国内接口 国外很火的ChatGPT&#xff0c;这是一种基于人工智能技术…

LNMP架构的源码编译环境下部署Discuz社区论坛与wordpress博客

目录 一、编译安装Nginx 1、关闭防火墙 2、安装依赖包 3、创建运行用户 4、解压软件包并编译安装 5、软链接路径优化 6、添加Nginx系统服务 二、编译安装Mysql服务 1、安装依赖环境以及源 2、创建运行用户 3、编译安装 4、修改配置文件 5、数据库目录进行权限调整…

Qt SQLite的创建和使用

重点&#xff1a; 1.SQLite创建数据库内容方法 链接&#xff1a;SQLite Expert Personal的简单使用-CSDN博客 2.和数据库进行链接方法 QSqlDatabase DB; //数据库连接bool MainWindow::openDatabase(QString aFile) {DBQSqlDatabase::addDatabase("QSQLITE"); /…

通过多进程并发方式(fork)实现服务器

以下内容为视频学习记录。 1、父进程accept后返回的文件描述符为cfd以及用于创建连接的lfd; 调用fork()创建子进程后&#xff0c;子进程继承cfd,lfd&#xff0c;通过该cfd与连接过来的客户端通信,lfd对子进程来说没用&#xff0c;可以直接close(lfd); 对于父进程来说&#x…