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

自学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'}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【黑马程序员】STL之stack与queue常用操作

stack容器 stack基本概念 stack是一种先进后出的数据结构&#xff0c;它只有一个出口 栈中只有顶端元素才可以被外界使用&#xff0c;因此栈不支持遍历操作 stack常用接口 stack代码示例 #include <iostream> #include <stack>using namespace std;void test()…

AI:138-开发一种能够自动化生成艺术品描述的人工智能系统

🚀点击这里跳转到本专栏,可查阅专栏顶置最新的指南宝典~ 🎉🎊🎉 你的技术旅程将在这里启航! 从基础到实践,深入学习。无论你是初学者还是经验丰富的老手,对于本专栏案例和项目实践都有参考学习意义。 ✨✨✨ 每一个案例都附带关键代码,详细讲解供大家学习,希望…

【软件工具】Typora 免费版下载安装Markdown编辑器-Win10快速安装

目录 下载安装激活配置图片路径管理高亮显示自动保存 使用教程 前言 Typora&#xff0c; 是一款好用的编辑器和阅读器。这里为大家找了一个可使用版本&#xff0c;安装过程十分简单&#xff0c;亲测有效&#xff0c;不浪费大家时间&#xff0c;现在将Typora分享给大家免费使用。…

C# OpenVino Yolov8 Pose 姿态识别

目录 效果 模型信息 项目 代码 下载 效果 模型信息 Model Properties ------------------------- date&#xff1a;2023-09-07T17:11:43.091306 description&#xff1a;Ultralytics YOLOv8n-pose model trained on /usr/src/app/ultralytics/datasets/coco-pose.yaml a…

visual stdio 使用ATL简单使用COM组件

先试用visual stdio创建ATL项目 选择第一个创建ATL简单对象 ProgId也需要添加一下&#xff0c;默认创建完之后添加方法 STDMETHODIMP AddNumber(LONG __num, LONG* result);添加定义 STDMETHODIMP_(HRESULT __stdcall) CATLSimpleObject::AddNumber(LONG __num, LONG* r…

智能充电桩案例分析——交流充电桩

随着电动汽车的发展&#xff0c;充电桩也成为当下的一个很热门的工业产品。我们初步接触充电桩&#xff0c;有了点滴的感受。 先简单说说容易一点的交流充电桩。就是通过市电&#xff08;220V,50赫兹&#xff09;给电动汽车提供充电的能源来源。很容易理解&#xff0c;交流…

高数考研 -- 公式总结(更新中)

1. 两个重要极限 (1) lim ⁡ x → 0 sin ⁡ x x 1 \lim _{x \rightarrow 0} \frac{\sin x}{x}1 limx→0​xsinx​1, 推广形式 lim ⁡ f ( x ) → 0 sin ⁡ f ( x ) f ( x ) 1 \lim _{f(x) \rightarrow 0} \frac{\sin f(x)}{f(x)}1 limf(x)→0​f(x)sinf(x)​1. (2) lim ⁡…

产品经理学习-产品运营《什么是SOP》

目录 什么是SOP 如何执行SOP 执行SOP的重点 什么是SOP SOP就是项目流程操作的说明书 日常工作中的例行操作&#xff1a; 例行操作是指&#xff0c;在每一天&#xff0c;针对每一个用户&#xff0c;在每个项目之中&#xff0c;都必须完成的操作&#xff0c;这些必须完成的操…

CleanMyMac2024永久免费mac电脑版本安装包下载

CleanMyMac 4 for Mac&#xff1a;细致入微的功能介绍&#xff0c;CleanMyMac 4 for Mac作为一款系统清理和优化工具&#xff0c;提供了丰富而细致的功能&#xff0c;旨在满足Mac用户在不同场景下的清理和优化需求。以下是对CleanMyMac 4功能的更加细化介绍&#xff1a; CleanM…

MySQL的SQL语句

1.MySQL连接 连接命令一般是这样写的 mysql -h$ip -P$port -u$user -p比如:mysql -h127.0.0.1 -P3306 -uroot -p -h 指定连接的主机地址&#xff1b;-P 指定连接端口号&#xff1b;-u 指定用户名 -p指定用户名密码 2.SQL分类 DDL(Data Definition Language) 数据定义语言&…

【数据结构】链表面试题

203.移除链表元素 206.反转链表 876.链表的中间结点 牛客.链表中倒数第k个结点 21.合并两个有序链表 牛客.链表分隔 牛客.链表的回文结构 160.相交链表 141.环形链表 142.环形链表2 1. 移除链表元素 题目描述 思路&#xff1a; 定义一个指针cur遍历整个链表&#xff0c;一个ta…

大开眼界的4款黑科技软件,功能强大,网友:越用越上瘾

作为一名热衷于探索软件的搞机爱好者&#xff0c;小蛙在各大软件论坛间游走&#xff0c;旨在帮助大家在纷繁复杂的Windows软件世界中&#xff0c;寻找到那些真正值得安装的神器。 在忙碌的现代生活中&#xff0c;我们的磁盘空间和时间都显得尤为宝贵&#xff0c;没必要下一些鸡…

解决内嵌帆软报表出现重定向问题

最近收到反馈&#xff0c;某些程序的前端通过iframe标签内嵌finebi帆软报表时&#xff0c;出现一系列问题。 问题1: 如下图所示&#xff0c;单点登录(单点登录地址schema是https)后service地址的schema协议是http, 浏览器内核的安全策略不允许http访问https。 解决方案&#xf…

java数据结构与算法刷题-----LeetCode530. 二叉搜索树的最小绝对差

java数据结构与算法刷题目录&#xff08;剑指Offer、LeetCode、ACM&#xff09;-----主目录-----持续更新(进不去说明我没写完)&#xff1a;https://blog.csdn.net/grd_java/article/details/123063846 解题思路&#xff1a;时间复杂度O(n)&#xff0c;空间复杂度O(n) 一个有序…

阻抗与电气模型(三)

阻抗的定义为电压与电流的比值&#xff0c;通常用大写字母Z表示。Z V/I&#xff0c;当信号沿互连传播时&#xff0c;将不断的探测互连的阻抗&#xff0c;并做出相应的反应。阻抗又称为交流&#xff08;AC&#xff09;电阻。 如果知道了互连的阻抗和传播时延&#xff0c;也就知…

如何改变.net托管的入口main函数

有小伙伴问: .NET托管入口Main函数可以修改成别的函数&#xff0c;用来作为程序的入口吗&#xff1f; 答案&#xff1a;当然是可以的。这也算是.NET里面非常简单的骚操了。本篇来用最新的.NET8演示下&#xff0c;如何修改Main入口。 1.简单控制台例子&#xff1a; namespace…

金融行业数字化人事管理:组织管理、风险管控、职级晋升一体化

目前&#xff0c;金融行业正在全面推进数字化转型&#xff0c;推动行业高质量发展。人力资源是组织发展的核心竞争力&#xff0c;数字化的人事管理能够为金融组织降本增效。 行业痛点 1、金融行业分支机构多、人员规模大&#xff0c;随着组织的快速发展&#xff0c;集团内组织…

Postman Tests:简介与示例

Postman 不仅是一个强大的 API 开发工具&#xff0c;它还提供了创建自动化测试脚本的能力&#xff0c;这些脚本可以用于检验API请求得到的响应是否符合预期。这些测试脚本被称为 “Tests”&#xff0c;支持使用 JavaScript 编程语言进行编写&#xff0c;并且 Postman 提供了一系…

图片录入设备、方式与质量对图片转Excel的影响

随着数字化时代的到来&#xff0c;图片已经成为人们日常生活中不可或缺的一部分。在各行各业中&#xff0c;图片的应用越发广泛&#xff0c;从而促使了图片处理技术的快速发展。然而&#xff0c;图片的质量对于后续数据处理和分析的准确性和可靠性有着至关重要的影响。本文将从…

IDEA 创建Spring Boot 项目整合jdbc详细步骤

IDEA 创建Spring Boot 项目&整合jdbc详细步骤 1、打开 IntelliJ IDEA 软件2、使用 "Spring Initializr" 作为项目类型&#xff0c;新建项目工程3、选择对应的SpringBoot版本和依赖4、Spring Boot 项目的结构5、创建一个TestController&#xff0c;并运行6、整合j…