bs4 - HTML操作
html_doc = """
<html><head><title>The Dormouse's story</title></head>
<body>
<p class="title"><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>
"""
soup = BeautifulSoup(html_doc, 'html.parser')
遍历文档树
子节点
一个
Tag
可能包含多个字符串或其它的Tag
,这些都是这个Tag
的子节点。Beautiful Soup
提供了许多操作和遍历子节点的属性注意:
Beautiful Soup
中字符串节点不支持这些属性,因为字符串没有子节点
tag的名字
head_tag = soup.head
# <head><title>The Dormouse's story</title></head>
head_name = head_tag.name
# head
.contents 和 .children
tag
的.contents
属性可以将tag
的子节点以列表的方式输出
head_tag = soup.head
# <head><title>The Dormouse's story</title></head>
head_tag.contents
# [<title>The Dormouse's story</title>]
title_tag = head_tag.contents[0]
# <title>The Dormouse's story</title>
title_tag.contents
# [u'The Dormouse's story']
通过tag
的 .children
生成器,可以对tag
的子节点进行循环
for child in soup.title.children:
print(child)
# The Dormouse's story
.descendants
.descendants
属性可以对所有tag
的子孙节点进行递归循环
for child in head_tag.descendants:
print(child)
# <title>The Dormouse's story</title>
# The Dormouse's story
BeautifulSoup
有一个直接子节点(<html>
节点),却有很多子孙节点
len(list(soup.children))
# 1
len(list(soup.descendants))
# 25
.string
tag
如果仅有一个子节点或只有一个 NavigableString
类型子节点,都可以用.string
获取子节点(文本)
# 只有一个NavigableString类型子节点
soup.title.string
# u'The Dormouse's story'
# 仅有一个子节点
soup.head.contents
# [<title>The Dormouse's story</title>]
soup.head.string
# u'The Dormouse's story'
.strings 和 .stripped_strings
如果tag
中包含多个字符串 ,可以使用 .strings
来循环获取
for string in soup.strings:
print(repr(string))
# u"The Dormouse's story"
# u'\n\n'
# u"The Dormouse's story"
# u'\n\n'
# u'Once upon a time there were three little sisters; and their names were\n'
# u'Elsie'
# u',\n'
# u'Lacie'
# u' and\n'
# u'Tillie'
# u';\nand they lived at the bottom of a well.'
# u'\n\n'
# u'...'
# u'\n'
使用 .stripped_strings
可以去除多余空白内容
for string in soup.stripped_strings:
print(repr(string))
# u"The Dormouse's story"
# u"The Dormouse's story"
# u'Once upon a time there were three little sisters; and their names were'
# u'Elsie'
# u','
# u'Lacie'
# u'and'
# u'Tillie'
# u';\nand they lived at the bottom of a well.'
# u'...'
父节点
.parent 和 .parents
通过 .parent
属性来获取某个元素的父节点
title_tag = soup.title
# <title>The Dormouse's story</title>
title_tag.parent
# <head><title>The Dormouse's story</title></head>
通过元素的 .parents
属性可以递归得到元素的所有父辈节点
link = soup.a
# <a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>
for parent in link.parents:
if parent is None:
print(parent)
else:
print(parent.name)
# p
# body
# html
# [document]
# None
兄弟节点
.next_sibling 和 .previous_sibling
在文档树中,使用 .next_sibling
和 .previous_sibling
属性来查询兄弟节点
"""
<html>
<body>
<a>
<b>text1</b>
<c>text2</c>
</a>
</body>
</html>
"""
sibling_soup.b.next_sibling
# <c>text2</c>
sibling_soup.c.previous_sibling
# <b>text1</b>
.next_siblings 和 .previous_siblings
通过 .next_siblings
和 .previous_siblings
属性可以对当前节点的兄弟节点迭代输出
for sibling in soup.a.next_siblings:
print(repr(sibling))
# u',\n'
# <a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>
# u' and\n'
# <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>
# u'; and they lived at the bottom of a well.'
# None
for sibling in soup.find(id="link3").previous_siblings:
print(repr(sibling))
# ' and\n'
# <a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>
# u',\n'
# <a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>
# u'Once upon a time there were three little sisters; and their names were\n'
# None
前进和回退
.next_element 和 .previous_element
.next_element
属性指向解析过程中下一个被解析的对象(字符串或tag
),结果可能与 .next_sibling
相同,但通常是不一样的
last_a_tag = soup.find("a", id="link3")
# <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>
last_a_tag.next_sibling
# '; \nand they lived at the bottom of a well.'
这个<a>
标签的 .next_element
属性结果是在<a>
标签被解析之后的解析内容,不是<a>
标签后的句子部分,应该是字符串Tillie
。这是因为在原始文档中,字符串Tillie
在分号前出现,解析器先进入<a>
标签,然后是字符串Tillie
,然后关闭</a>
标签,然后是分号和剩余部分。分号与<a>
标签在同一层级,但是字符串Tillie
会被先解析
last_a_tag.next_element
# u'Tillie'
.previous_element
属性刚好与 .next_element
相反,它指向当前被解析的对象的前一个解析对象
last_a_tag.previous_element
# u' and\n'
last_a_tag.previous_element.next_element
# <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>
.next_elements 和 .previous_elements
通过 .next_elements
和 .previous_elements
的迭代器就可以向前或向后访问文档的解析内容,就好像文档正在被解析一样
for element in last_a_tag.next_elements:
print(repr(element))
# u'Tillie'
# u';\nand they lived at the bottom of a well.'
# u'\n\n'
# <p class="story">...</p>
# u'...'
# u'\n'
# None
搜索文档树
find() 和 find_all()
find/find_all( name , attrs , recursive , string , **kwargs )
使用方法相同,唯一区别,
find
返回值是元素本身,不存在时返回None
;find_all
返回值是n
个元素的列表,不存在时返回空列表
搜索当前tag
的所有子节点,并判断是否符合过滤器的条件
soup.find_all("title")
# [<title>The Dormouse's story</title>]
soup.find_all("p", "title")
# [<p class="title"><b>The Dormouse's story</b></p>]
soup.find_all("a")
# [<a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>,
# <a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>,
# <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>]
soup.find_all(id="link2")
# [<a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>]
import re
soup.find(string=re.compile("sisters"))
# u'Once upon a time there were three little sisters; and their names were\n'
keyword参数
1、如果一个参数不是函数的形参搜索时会把该参数当作指定名字tag
的属性来搜索
soup.find_all(id='link2')
# [<a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>]
soup.find_all(href=re.compile("elsie"))
# [<a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>]
2、在文档树中查找所有包含 id
属性的tag
,无论 id
的值是什么
soup.find_all(id=True)
# [<a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>,
# <a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>,
# <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>]
3、使用多个指定名字的参数可以同时过滤tag
的多个属性
soup.find_all(href=re.compile("elsie"), id='link1')
# [<a class="sister" href="http://example.com/elsie" id="link1">three</a>]
4、有些tag
属性的搜索不能使用,比如HTML5
中的 data-*
属性。但是可以通过 find_all()
方法的 attrs
参数定义一个字典参数来搜索包含特殊属性的tag
data_soup = BeautifulSoup('<div data-foo="value">foo!</div>')
data_soup.find_all(data-foo="value")
# SyntaxError: keyword can't be an expression
data_soup.find_all(attrs={"data-foo": "value"})
# [<div data-foo="value">foo!</div>]
按CSS搜索
Beautiful Soup的4.1.1版本开始,可以通过
class_
参数搜索有指定CSS
类名的tag
soup.find_all("a", class_="sister")
# [<a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>,
# <a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>,
# <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>]
1、class_
参数同样接受不同类型的 过滤器 ,字符串,正则表达式,方法或 True
soup.find_all(class_=re.compile("itl"))
# [<p class="title"><b>The Dormouse's story</b></p>]
def has_six_characters(css_class):
return css_class is not None and len(css_class) == 6
soup.find_all(class_=has_six_characters)
# [<a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>,
# <a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>,
# <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>]
2、tag
的 class
属性是,按照CSS
类名搜索tag
时,可以分别搜索tag
中的每个CSS
类名,也可以通过CSS
值完全匹配:
css_soup = BeautifulSoup('<p class="body strikeout"></p>', 'lxml')
css_soup.find_all("p", class_="strikeout")
# [<p class="body strikeout"></p>]
css_soup.find_all("p", class_="body")
# [<p class="body strikeout"></p>]
css_soup.find_all("p", class_="body strikeout")
# [<p class="body strikeout"></p>]
注意:完全匹配 class
的值时,如果CSS
类名的顺序与实际不符,将搜索不到结果:
css_soup.find_all("p", class_="strikeout body")
# []
string参数
通过 string
参数可以搜索文档中的字符串内容.与 name
参数的可选值一样, string
参数接受:字符串 , 正则表达式 , 列表, True 。
soup.find_all(string="Elsie")
# [u'Elsie']
soup.find_all(string=["Tillie", "Elsie", "Lacie"])
# [u'Elsie', u'Lacie', u'Tillie']
soup.find_all(string=re.compile("Dormouse"))
[u"The Dormouse's story", u"The Dormouse's story"]
def is_the_only_string_within_a_tag(s):
"""Return True if this string is the only child of its parent tag."""
return s == s.parent.string
soup.find_all(string=is_the_only_string_within_a_tag)
# [u"The Dormouse's story", u"The Dormouse's story", u'Elsie', u'Lacie', u'Tillie', u'...']
还可以与其它参数混合使用来过滤tag
.Beautiful Soup
会找到 .string
方法与 string
参数值相符的tag
soup.find_all("a", string="Elsie")
# [<a href="http://example.com/elsie" class="sister" id="link1">Elsie</a>]
limit参数
限制返回结果的数量
soup.find_all("a", limit=2)
# [<a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>,
# <a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>]
recursive参数
调用tag
的 find_all()
方法时,Beautiful Soup
会检索当前tag
的所有子孙节点,如果只想搜索tag
的直接子节点,可以使用参数 recursive=False
.
soup.html.find_all("title")
# [<title>The Dormouse's story</title>]
soup.html.find_all("title", recursive=False)
# []
<title>
标签在<html>
标签下, 但并不是直接子节点,<head>
标签才是直接子节点. 在允许查询所有后代节点时Beautiful Soup
能够查找到<title>
标签. 但是使用了recursive=False
参数之后,只能查找直接子节点,这样就查不到<title>
标签了
tag也可以被调用
BeautifulSoup
对象和 tag
对象可以被当作一个方法来使用,这个方法的执行结果与调用这个对象的 find_all()
方法相同,如下:
soup.find_all("a")
soup("a")
或者:
soup.title.find_all(string=True)
soup.title(string=True)
拓展:soup.head.title
是tag
的名字方法的简写.这个简写的原理就是多次调用当前tag
的 find()
方法
soup.head.title
# <title>The Dormouse's story</title>
soup.find("head").find("title")
# <title>The Dormouse's story</title>
find_parents() 和 find_parent()
find_parents( name , attrs , recursive , string , **kwargs )
a_string = soup.find(string="Lacie")
# u'Lacie'
a_string.find_parents("a")
# [<a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>]
a_string.find_parent("p")
# <p class="story">Once upon a time there were three little sisters; and their names were
# <a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>,
# <a class="sister" href="http://example.com/lacie" id="link2">Lacie</a> and
# <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>;
# and they lived at the bottom of a well.</p>
a_string.find_parents("p", class="title")
# []
文档中的一个<a>
标签是当前子节点的直接父节点,所以可以被找到.还有一个<p>
标签是目标子节点的间接父辈节点,所以也可以被找到.包含class
值为title
的<p>
标签不是目标子节点的父辈节点,所以通过 find_parents()
方法搜索不到.
find_next_siblings() 和 find_next_sibling()
find_next_siblings( name , attrs , recursive , string , **kwargs
find_next_siblings()
方法返回所有符合条件的后面的兄弟节点, find_next_sibling()
只返回符合条件的后面的第一个tag
节点
first_link = soup.a
# <a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>
first_link.find_next_siblings("a")
# [<a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>,
# <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>]
first_story_paragraph = soup.find("p", "story")
first_story_paragraph.find_next_sibling("p")
# <p class="story">...</p>
find_previous_siblings() 和 find_previous_sibling()
find_previous_siblings( name , attrs , recursive , string , **kwargs )
find_previous_siblings()
方法返回所有符合条件的前面的兄弟节点, find_previous_sibling()
方法返回第一个符合条件的前面的兄弟节点
last_link = soup.find("a", id="link3")
# <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>
last_link.find_previous_siblings("a")
# [<a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>,
# <a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>]
first_story_paragraph = soup.find("p", "story")
first_story_paragraph.find_previous_sibling("p")
# <p class="title"><b>The Dormouse's story</b></p>
find_all_next() 和 find_next()
find_all_next( name , attrs , recursive , string , **kwargs )
这2个方法通过.next_elements
属性对当前tag
之后的tag
和字符串进行迭代, find_all_next()
方法返回所有符合条件的节点, find_next()
方法返回第一个符合条件的节点
first_link = soup.a
# <a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>
first_link.find_all_next(string=True)
# [u'Elsie', u',\n', u'Lacie', u' and\n', u'Tillie',
# u';\nand they lived at the bottom of a well.', u'\n\n', u'...', u'\n']
first_link.find_next("p")
# <p class="story">...</p>
find_all_previous() 和 find_previous()
find_all_previous( name , attrs , recursive , string , **kwargs )
这2个方法通过.previous_elements
属性对当前节点前面的tag
和字符串进行迭代, find_all_previous()
方法返回所有符合条件的节点, find_previous()
方法返回第一个符合条件的节点
first_link = soup.a
# <a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>
first_link.find_all_previous("p")
# [<p class="story">Once upon a time there were three little sisters; ...</p>,
# <p class="title"><b>The Dormouse's story</b></p>]
first_link.find_previous("title")
# <title>The Dormouse's story</title>
CSS选择器
Beautiful Soup
支持大部分的CSS
选择器 http://www.w3.org/TR/CSS2/selector.html , 在 Tag
或 BeautifulSoup
对象的 .select()
方法中传入字符串参数, 即可使用CSS
选择器的语法找到tag
soup.select("title")
# [<title>The Dormouse's story</title>]
soup.select("p:nth-of-type(3)")
# [<p class="story">...</p>]
1、通过tag
标签逐层查找
soup.select("body a")
# [<a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>,
# <a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>,
# <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>]
soup.select("html head title")
# [<title>The Dormouse's story</title>]
2、找到某个tag
标签下的直接子标签
soup.select("head > title")
# [<title>The Dormouse's story</title>]
soup.select("p > a")
# [<a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>,
# <a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>,
# <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>]
soup.select("p > a:nth-of-type(2)")
# [<a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>]
soup.select("p > #link1")
# [<a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>]
soup.select("body > a")
# []
3、找到兄弟节点标签
soup.select("#link1 ~ .sister")
# [<a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>,
# <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>]
soup.select("#link1 + .sister")
# [<a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>]
4、通过CSS
的类名查找
soup.select(".sister")
# [<a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>,
# <a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>,
# <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>]
soup.select("[class~=sister]")
# [<a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>,
# <a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>,
# <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>]
5、通过tag
的id
查找
soup.select("#link1")
# [<a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>]
soup.select("a#link2")
# [<a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>]
6、同时用多种CSS
选择器查询元素
soup.select("#link1,#link2")
# [<a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>,
# <a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>]
7、通过是否存在某个属性来查找
soup.select('a[href]')
# [<a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>,
# <a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>,
# <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>]
8、通过属性的值来查找
soup.select('a[href="http://example.com/elsie"]')
# [<a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>]
soup.select('a[href^="http://example.com/"]')
# [<a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>,
# <a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>,
# <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>]
soup.select('a[href$="tillie"]')
# [<a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>]
soup.select('a[href*=".com/el"]')
# [<a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>]
9、通过语言设置来查找
multilingual_markup = """
<p lang="en">Hello</p>
<p lang="en-us">Howdy, y'all</p>
<p lang="en-gb">Pip-pip, old fruit</p>
<p lang="fr">Bonjour mes amis</p>
"""
multilingual_soup = BeautifulSoup(multilingual_markup)
multilingual_soup.select('p[lang|=en]')
# [<p lang="en">Hello</p>,
# <p lang="en-us">Howdy, y'all</p>,
# <p lang="en-gb">Pip-pip, old fruit</p>]
10、返回查找到的元素的第一个
soup.select_one(".sister")
# <a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>
修改文档树
修改tag的名称和属性
重命名一个tag
,改变属性的值,添加或删除属性
soup = BeautifulSoup('<b class="boldest">Extremely bold</b>')
tag = soup.b
tag.name = "blockquote"
tag['class'] = 'verybold'
tag['id'] = 1
tag
# <blockquote class="verybold" id="1">Extremely bold</blockquote>
del tag['class']
del tag['id']
tag
# <blockquote>Extremely bold</blockquote>
修改 .string
给tag
的 .string
属性赋值,就相当于用当前的内容替代了原来的内容
markup = '<a href="http://example.com/">I linked to <i>example.com</i></a>'
soup = BeautifulSoup(markup)
tag = soup.a
tag.string = "New link text."
tag
# <a href="http://example.com/">New link text.</a>
注意: 如果当前的tag
包含了其它tag
,那么给它的 .string
属性赋值会覆盖掉原有的所有内容包括子tag
append()
Tag.append()
方法向tag
中添加内容
soup = BeautifulSoup("<a>Foo</a>")
soup.a.append("Bar")
soup
# <html><head></head><body><a>FooBar</a></body></html>
soup.a.contents
# [u'Foo', u'Bar']
insert()
Tag.insert()
方法与 Tag.append()
方法类似,区别是不会把新元素添加到父节点 .contents
属性的最后,而是把元素插入到指定的位置
markup = '<a href="http://example.com/">I linked to <i>example.com</i></a>'
soup = BeautifulSoup(markup)
tag = soup.a
tag.insert(1, "but did not endorse ")
tag
# <a href="http://example.com/">I linked to but did not endorse <i>example.com</i></a>
tag.contents
# [u'I linked to ', u'but did not endorse', <i>example.com</i>]
insert_before() 和 insert_after()
insert_before()
方法在当前tag
或文本节点前插入内容
soup = BeautifulSoup("<b>stop</b>")
tag = soup.new_tag("i")
tag.string = "Don't"
soup.b.string.insert_before(tag)
soup.b
# <b><i>Don't</i>stop</b>
insert_after()
方法在当前tag
或文本节点后插入内容
soup.b.i.insert_after(soup.new_string(" ever "))
soup.b
# <b><i>Don't</i> ever stop</b>
soup.b.contents
# [<i>Don't</i>, u' ever ', u'stop']
replace_with()
PageElement.replace_with()
方法移除文档树中的某段内容,并用新tag
或文本节点替代它
markup = '<a href="http://example.com/">I linked to <i>example.com</i></a>'
soup = BeautifulSoup(markup)
a_tag = soup.a
new_tag = soup.new_tag("b")
new_tag.string = "example.net"
a_tag.i.replace_with(new_tag)
a_tag
# <a href="http://example.com/">I linked to <b>example.net</b></a>
本文来自博客园,仅供参考学习,如有不当之处还望不吝赐教,不胜感激!转载请注明原文链接:https://www.cnblogs.com/rong-z/p/17879908.html
作者:cnblogs用户