舟山市建设工程造价管理协会网站,西安免费平台网站建设,seo网站营销,太原建站模板自学Python第十五天-常用的HTML解析工具#xff1a;bs4、xpath、re BS4安装和引入开始使用find_all() 方法获取标签find() 方法获取标签select() 方法获取标签#xff0c;css 选择器从标签中获取数据 XPathxpath 基础xpath 语法规则lxml 模块xpath() 方法 REmatch() 方法sear… 自学Python第十五天-常用的HTML解析工具bs4、xpath、re BS4安装和引入开始使用find_all() 方法获取标签find() 方法获取标签select() 方法获取标签css 选择器从标签中获取数据 XPathxpath 基础xpath 语法规则lxml 模块xpath() 方法 REmatch() 方法search() 方法findall() 方法finditer() 方法sub() 方法subn() 方法split() 方法compile() 方法flags 参数match 对象 之前应该写过关于 bs4、xpath、re 的python使用文章但是找不到了。因为这3种工具在 html 解析中经常用到所以重新写一遍。 在 python 学习中绕不过去的就是爬虫学习爬虫绕不过去的就是HTML页面解析而最常用的解析工具就是 BeautifulSoup4、XPath 和 RE 了。这三个工具的比较如下
工具解析速度使用难度安装难度bs慢最简单简单lxml(xpath)快简单一般正则(re)最快困难无(内置)
BS4
BeautifulSoup 4 简称 BS4是一个 HTML/XML 的解析器。它是基于 HTML DOM 文档的会载入整个文档解析整个 DOM 树因此时间和内存开销会大很多性能较低。但是其语法是基于 CSS Selector 的所以学习和使用非常简单。 BS4中文文档 安装和引入
pip install beautifulsoup4from bs4 import BeautifulSoup开始使用
bs4 使用时首先创建 Beautiful Soup 对象然后使用该对象的对应方法来解析DOM获取需要的元素标签对象最后使用该对象的对应方法获取需要的属性或文本数据。例如
from bs4 import BeautifulSouphtml
htmlheadtitleThe Dormouses story/title/head
body
p classtitle namedromousebThe Dormouses story/b/p
p classstoryOnce upon a time there were three little sisters; and their names were
a hrefhttp://example.com/elsie classsister idlink1Elsie/a,
a hrefhttp://example.com/lacie classsister idlink2Lacie/a and
a hrefhttp://example.com/tillie classsister idlink3Tillie/a;
and they lived at the bottom of a well./p
p classstory.../p
# 创建 Beautiful Soup 对象
soup BeautifulSoup(html, lxml)print(soup.prettify())find_all() 方法获取标签
可以使用 find_all(self, nameNone, attrs{}, recursiveTrue, stringNone, limitNone, **kwargs) 方法来匹配相应的元素列表。该方法最常用的参数就是 nameattrs 和 string。
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(idlink)string 参数可以搜索文档中的文本字符串内容。与 name 一样可以接受字符串、列表以及正则表达式
ret_1 soup.find_all(stringElsie)
ret_2 soup.find_all(string[Tillie, Elsie, Lacie])
ret_3 soup.find_all(stringre.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[classsister])
soup.select(a[hrefhttp://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 HelperFirefox浏览器插件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[langeng]选择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 lxmlfrom lxml import etree使用此模块需要先将需要解析的文本转化为 Element 对象Element 对象有 xpath 的方法
from lxml import etreetext div ul li classitem-1a hreflink1.htmlfirst item/a/li li classitem-1a hreflink2.htmlsecond item/a/li li classitem-inactivea hreflink3.htmlthird item/a/li li classitem-1a hreflink4.htmlfourth item/a/li li classitem-0a hreflink5.htmlfifth 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[classitem-1]/a/href)
title_list html.xpath(//li[classitem-1]/a/text())
# 获取节点列表
li_list html.xpath(//li[classitem-1])
# 从节点列表对象中继续使用 xpath 匹配查询
for li in li_list:item dict()item[href] li.xpath(./a/href)[0] if len(li.xpath(./a/href)) 0 else Noneitem[title] li.xpath(./a/text())[0] if len(li.xpath(./a/text())) 0 else Noneprint(item)RE
RE 模块是python中使用正则语法的模块正则的语法比较复杂另外起一篇文章学习。这里只有 re 模块的使用方法。re 模块是 python 的内置模块所以可以直接引入
import rere 模块的使用方式一般有两种
直接使用相应的匹配方法将匹配字符串和待查找文本作为参数传入。将匹配字符串编译为一个Pattern对象并用此对象的相关匹配方法来匹配目标待查找文本。
match 对象是 re 模块方法所返回的默认的匹配对象大部分匹配方法如果得到匹配结果就会返回 match 对象。
match() 方法
match(pattern, string, flags0) 方法可以从字符串开头开始检测是否于模式匹配。如果匹配成功返回匹配对象否则返回None。
# 从开头检测字符串是否匹配
match re.match(r\d, 123abc)
if match:print(match.group()) # 输出 123search() 方法
search(pattern, string, flags0) 方法可以在字符串中搜索并返回第一个匹配项。如果匹配成功返回匹配对象否则返回None。
# 使用 search 方法在整个字符串中搜索匹配
search re.search(r\d, abc123def)
if search:print(search.group()) # 输出: 123findall() 方法
findall(pattern, string, flags0) 方法会返回所有非重叠匹配项的列表。如果匹配模式中有一个或多个捕获组(group)则会返回元组列表。
# 使用 findall 方法找到所有匹配的数字
numbers re.findall(r\d, abc123def456)
print(numbers) # 输出: [123, 456]finditer() 方法
与 findall() 方法类似不过返回值为一个迭代器其中每一个元素都是一个匹配对象。
sub() 方法
sub(pattern, repl, string, count0, flags0) 方法可以将匹配项替换为 repl 参数的值repl 可以是一个字符串或一个函数如果是函数每个匹配项都会作为参数传递给这个函数。count 用于指定最大替换次数默认 0替换所有匹配项。
# 使用 sub 方法替换所有的数字为 #
replaced re.sub(r\d, #, abc123def456)
print(replaced) # 输出: abc#def#subn() 方法
与 sub() 方法类似不过返回值是一个包含新字符串和替换次数的元组。
split() 方法
split(pattern, string, maxsplit0, flags0) 方法可以根据匹配项来分割字符串。maxsplit 用于指定最大分割次数默认 0表示分割所有匹配项。
# 使用 split 方法根据数字分割字符串
parts re.split(r\d, abc123def456ghi)
print(parts) # 输出: [abc, def, ghi]compile() 方法
compile(pattern, flags0) 方法实际并不进行匹配而是返回一个正则表达式匹配模式对象这个对象可以使用 match、search、findall 等方法来进行匹配。常用于同一个正则表达式需要重复的与不同文本进行匹配的情况避免重复编译相同的模式提高效率。
p re.compile(r\d)
search p.search(abc123def)
if search:print(search.group()) # 输出: 123flags 参数
几乎 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(rabc, ABCabc, flagsre.IGNORECASE)
print(case_insensitive) # 输出: [ABC, abc]# 多行模式的匹配
multiline re.search(r^abc, def\nabc, flagsre.MULTILINE)
if multiline:print(multiline.group()) # 输出: abc# 让点号匹配换行符
dotall re.search(ra.b, a\nb, flagsre.DOTALL)
if dotall:print(dotall.group()) # 输出: a\nb# 使用 ASCII 字符集
ascii_char re.findall(r\w, café, flagsre.ASCII)
print(ascii_char) # 输出: [caf]# 使用 VERBOSE 模式允许正则表达式分行并添加注释
verbose re.compile(r\b # 单词边界\w # 一个或多个字母数字字符\b # 单词边界
, flagsre.VERBOSE)
print(verbose.findall(Hello, world!)) # 输出: [Hello, world]match 对象
match 对象有一些常用的属性和方法来获取需要的数据
属性说明string返回传递给 match 或 search 等函数的原始字符串。re返回用于匹配的正则表达式对象。pos返回用于匹配的字符串的起始位置。endpos返回用于匹配的字符串的结束位置。lastindex返回最后一个被捕获的分组在 Match 对象中的索引。lastgroup返回最后一个被捕获的分组的名称。
方法说明group(num0)返回整个匹配的字符串或者指定编号的分组。groups(defaultNone)返回一个包含所有捕获组的元组如果没有匹配则为 defaultgroupdict(defaultNone)返回一个字典包含所有命名的捕获组。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(rUser ID: (\d), User ID: 12345)
if match:print(match.group(1)) # 输出第一个捕获组匹配到的内容: 12345# 使用命名捕获组
match re.search(rUser ID: (?Pid\d), User ID: 12345)
if match:print(match.group(id)) # 输出命名捕获组 id 匹配到的内容: 12345# 获取匹配的起始和结束位置
match re.search(rID, 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(?Pfirst\w) (?Psecond\w), Hello World)
if match:print(match.groupdict()) # 输出所有命名捕获组的内容: {first: Hello, second: World}