前提、回顾

1、因为最近工作中都是在跟XML格式的报文打交道:主要就是XML报文的解析、入库。在做自动化时,需要解析XML报文,前面虽然学习过下BeautifulSoup,结果这次在写脚本时,突然发现前面学的还不是很全(当时也没想到会用到这么多,就随便了解了下),所以现在又得在回顾、补充下

2、在实际写的时候发现,其实BeautifulSoup在解析XML时跟解析HTML差不多,只是说在指定解析器时需要指定为"xml"并且"xml"一定要小写,如果是大写的话,好像会报错

3、tag对象:tag就是HTML或XML文档中的一个个标签对,其实我们在解析时主要就是想要找到每一个或指定tag标签(tag对象),以及tag对象中的字符串(NavigableString对象、Comment对象)以及tag对象的名字(name属性)及其属性(attrs属性)

 

 

遍历文档树

子节点

1、一个Tag可能包含多个字符串或其它的Tag,这些都是这个Tag的子节点。Beautiful Soup提供了许多操作和遍历子节点的属性

2、获取某个具体的tag对象的方法前面介绍过了:soup对象.标签对名(返回第一个符合要求的tag对象)或使用find_all("标签名")来查找符合要求的全部tag对象

3、有个获取tag的小窍门,可以在文档树的tag中多次调用这个方法

注: Beautiful Soup中字符串不支持这些属性,因为字符串没有子节点

例1:

from bs4 import BeautifulSoup
html = """
  <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 
  </p> 
"""

soup = BeautifulSoup(html, "lxml")

tag_p = soup.p
print(tag_p)
print("通过点取属性的方式只能获得当前名字的第一个tag")
print(soup.find_all("a"))
print("find_all()可以得到所有的标签")
tag_p_a = tag_p.a
print(tag_p_a)

"""
<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 
  </p>
通过点取属性的方式只能获得当前名字的第一个tag
[<a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>, <a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>]
find_all()可以得到所有的标签
<a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>
"""

 

.contents

1、tag的.content属性可以将tag的子节点以列表的方式输出。列表中的元素不止有tag对象还有navgableString对象(navgableString对象也是列表中的一个元素)

2、值得注意的是,哪怕只有一个换行\n也会占用contents一个位置

3、.contents属性只能找出一个节点的子节点,如果某个子节点还存在子节点,那么子节点的子节点会与子节点作为一体输出

4、字符串没有.contents属性,因为字符串没有子节点

例2:

from bs4 import BeautifulSoup

html = """
<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>
 </body>
</html>
"""

soup = BeautifulSoup(html,"lxml")

tag_head = soup.head            
tag_title = tag_head.contents    #获取某个节点的子节点
print(tag_head)
print(tag_title)      

for i in soup.find_all("p"):    #获取某个全部标签对下的子节点
    print(i.contents)

"""
for i in soup.find_all("p"):
    for n in i.contents:
        if str(type(n)) == "<class 'bs4.element.Tag'>":
            print("tag对象有:",n)
        else:
            print("字符串对象有:",n)
"""

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

注:
上面例子中如果列表中的元素是一个tag对象,那么就可以使用tag对象的.name和.attrs属性。以及navgableString对象属性(.string)

 

.children

1、通过tag的.children生成器,可以对tag的子节点进行循环

2、.children返回的是一个list类型的迭代器

3、.children属性也是用来找某个标签对下面的子节点的(对于子节点下面的子节点是不能直接找出的)

例3:

from bs4 import BeautifulSoup

html = """
<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>
 </body>
</html>
"""

soup = BeautifulSoup(html,"lxml")

tag_head = soup.head
tag_title = tag_head.children    #获取某个节点的子节点
print(tag_head)
print(tag_title)

for i in soup.find_all("p"):    #获取某个全部标签对下的子节点
    tag = i.children  #以生成器的形式返回
    print(tag)
    print("--------")
    for n in tag:
        print(n)

"""
<head>
<title>The Dormouse's story</title>
</head>
<list_iterator object at 0x0000028AD1BB7470>
<list_iterator object at 0x0000028AD1BB74E0>
--------
<b>The Dormouse's story</b>
<list_iterator object at 0x0000028AD1BB7550>
--------
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.
  
<list_iterator object at 0x0000028AD1BB74E0>
--------
...
"""

 

.descendants

1、.contents和.children属性仅包含tag的直接子节点:只会返回目标tag对象下的子节点,若子节点下还有子节点(孙节点)则是与子节点作为一体返回的

2、.descendants属性可以对所有tag的子孙节点进行递归循环

3、.descendants同样是list迭代器,只不过指的是子孙节点,用法同children

4、输出数据中不止有tag对象还有navgableString对象

5、<title>The</title>也包含一个子节点(字符串The),这种情况下字符串"The"也属于<title>标签的子孙节点。此时对<title>使用.contents属性时会返回"The"
例4:

from bs4 import BeautifulSoup

html = """
<html>
 <head>
  <title>The Dormouse's story</title>
 </head> 
 <body> 
  <p class="title"><b>The Dormouse's story</b></p> 
  <p class="story">Once
    <a href="elsie" class="sister" id="link1">Elsie</a>
    <a href="lacie" class="sister" id="link2">Lacie</a>
    <a href="tillie" class="sister" id="link3">Tillie</a>well
  </p> 
  <p class="story">...</p>
 </body>
</html>
"""

soup = BeautifulSoup(html, "lxml")

tag_body = soup.body
tag_title = tag_body.descendants
print(tag_body)
print(tag_title)

for i in tag_title:
    print(i)
"""
for i in tag_title:
    if str(type(i)) == "<class 'bs4.element.Tag'>":
        print("tag对象有:",i)
"""

"""
<body>
<p class="title"><b>The Dormouse's story</b></p>
<p class="story">Once
    <a class="sister" href="elsie" id="link1">Elsie</a>
<a class="sister" href="lacie" id="link2">Lacie</a>
<a class="sister" href="tillie" id="link3">Tillie</a>well
  </p>
<p class="story">...</p>
</body>
<generator object descendants at 0x0000025567E36D58>

<p class="title"><b>The Dormouse's story</b></p>
<b>The Dormouse's story</b>
The Dormouse's story

<p class="story">Once
    <a class="sister" href="elsie" id="link1">Elsie</a>
<a class="sister" href="lacie" id="link2">Lacie</a>
<a class="sister" href="tillie" id="link3">Tillie</a>well
  </p>
Once
    
<a class="sister" href="elsie" id="link1">Elsie</a>
Elsie

<a class="sister" href="lacie" id="link2">Lacie</a>
Lacie

<a class="sister" href="tillie" id="link3">Tillie</a>
Tillie
well
  
<p class="story">...</p>
...
"""

注:从上面的输出结果可以看出
1、使用.descendants属性来找tag对象的子孙节点是:先找出tag对象的子节点(此时子节点可能嵌套了孙节点),然后再从子节点中去找孙节点。会将嵌套了孙节点的子节点、孙节点、navgableString对象一起输出

2、.descendants属性输出数据中不止有tag对象还有navgableString对象,所以可以使用一个if判断语句来区分二者:if str(type(i)) == "<class 'bs4.element.Tag'>"
 

例5:

from bs4 import BeautifulSoup

xml = """
<?xml version="1.0" encoding="utf-8"?>
<nitf> 
  <head> 
    <title>Colombia Earthquake</title> 
  </head>  
  <body> 
    <headline> 
      <hl1>143 Dead in Colombia Earthquake</hl1> 
    </headline>  
    <byline> 
      <bytag>By Jared Kotler, Associated Press Writer</bytag> 
    </byline>  
    <dateline> 
      <location>Bogota, Colombia</location>  
      <date>Monday January 25 1999 7:28 ET</date> 
    </dateline> 
  </body> 
</nitf>
"""

soup = BeautifulSoup(xml,"xml")

tag_name_list = []
tag_value_list = []
for i in soup.body.descendants:
    if str(type(i)) == "<class 'bs4.element.Tag'>":
        tag_name_list.append(i.name)
    elif i != "\n":
        tag_value_list.append(i)
print(tag_value_list)
print(tag_name_list)
#不能直接将这两个列表组成字典,因为有些标签是嵌套其子节点的,它们是没有navgableString对象的(.string返回的是None)

dict1 ={}
for n in tag_name_list:
    print(soup.find(n).name)
    print(str(soup.find(n).string))
    #print({soup.find(n).name:str(soup.find(n).string)})
    if str(soup.find(n).string) != "None":
        dict1[soup.find(n).name] = str(soup.find(n).string)
print(dict1)

"""
['143 Dead in Colombia Earthquake', 'By Jared Kotler, Associated Press Writer', 'Bogota, Colombia', 'Monday January 25 1999 7:28 ET']
['headline', 'hl1', 'byline', 'bytag', 'dateline', 'location', 'date']
headline
None
hl1
143 Dead in Colombia Earthquake
byline
None
bytag
By Jared Kotler, Associated Press Writer
dateline
None
location
Bogota, Colombia
date
Monday January 25 1999 7:28 ET
{'hl1': '143 Dead in Colombia Earthquake', 'location': 'Bogota, Colombia', 'bytag': 'By Jared Kotler, Associated Press Writer', 'date': 'Monday January 25 1999 7:28 ET'}
"""

 

.strings

1、前面说的要获得一个tag对象的navgableString对象可以使用.string属性(tag对象.string),只是说.string属性有点局限性
    ⑴如果tag只有一个NavigableString类型子节点,那么这个tag可以使用.string得到子节点
    ⑵如果一个tag仅有一个子节点,那么这个tag也可以使用.string方法,输出结果与当前唯一子节点的.string结果相同
    ⑶如果tag包含了多个子节点,tag就无法确定.string方法应该调用哪个子节点的内容, .string的输出结果是None

2、如果tag对象中包含多个字符串(多个子节点),可以使用.strings(tag对象.strings)来循环获取各个子节点下的navgableString对象
    ⑴输出的字符串中可能包含了很多空格或空行,使用.stripped_strings可以去除多余空白内容

例6:

from bs4 import BeautifulSoup

html = """
<html>
 <head>
  <title>The Dormouse's story</title>
 </head> 
 <body> 
  <p class="story">Once
    <a href="elsie" class="sister" id="link1">Elsie</a>ss
    <a href="lacie" class="sister" id="link2">Lacie</a>
    <a href="tillie" class="sister" id="link3">Tillie</a>well
  </p> 
  <p class="story">...</p>
 </body>
</html>

"""

soup = BeautifulSoup(html, "lxml")

title_string = soup.title.string
print(title_string)
print("-------")
p_string = soup.p.string#第一个p标签对下有很多子节点
print(p_string)
print("-------")
ps_string = soup.p.strings    #这里也可以使用.stripped_strings
print(ps_string)
print("-------")
for i in ps_string:    #可以使用repr()或str()将NavigableString对象转为string
    print(i)

"""
The Dormouse's story
-------
None
-------
<generator object _all_strings at 0x00000289D26A6D58>
-------
Once
    
Elsie
ss
   
Lacie

Tillie
well
"""

 

 

CSS选择器

1、Beautiful Soup支持大部分的CSS选择器,在Tag或BeautifulSoup对象的.select()方法中传入字符串参数,即可使用CSS选择器的语法找到tag

2、返回信息为:所以符合查找要求的tag对象组成的列表。感觉跟find_all()方法一样

例7:

from bs4 import BeautifulSoup

html = """
<html>
 <head>
  <title>The Dormouse's story</title>
 </head> 
 <body> 
  <p class="story">Once
    <a href="elsie" class="sister" id="link1">Elsie</a>ss
    <a href="lacie" class="sister" id="link2">Lacie</a>
    <a href="tillie" class="sister" id="link3">Tillie</a>well
  </p> 
  <p class="story">...</p>
 </body>
</html>
"""

soup = BeautifulSoup(html, "lxml")

tag_a = soup.select("a")
print(tag_a)

"""
[<a class="sister" href="elsie" id="link1">Elsie</a>,
 <a class="sister" href="lacie" id="link2">Lacie</a>,
 <a class="sister" href="tillie" id="link3">Tillie</a>]
"""

 

 

修改文档树


修改tag对象的名称和属性

1、前面说过tag对象的.name和.attres属性返回的是一个字典,可以处理字典的方法来进行处理
例8:

from bs4 import BeautifulSoup

html = """
<a href="elsie" class="sister" id="link1">Elsie</a>ss
"""

soup = BeautifulSoup(html, "lxml")

tag_a = soup.find("a")
tag_a.name = "aa"
tag_a["href"] = "rrrr"
print(tag_a)

"""
<aa class="sister" href="rrrr" id="link1">Elsie</aa>
"""

 

修改.string

1、给tag对象的 .string属性赋值,就相当于用当前的内容替代了原来的内容

2、注意: 如果当前的tag包含了其它tag,那么给它的.string属性赋值会覆盖掉原有的所有内容。包括子tag(子tag将不存在)

3、tag中包含的字符串不能编辑,但是可以被替换成其它的字符串,用replace_with()方法

例9:

from bs4 import BeautifulSoup
html = """
  <title>The Dormouse's story</title>
  <p class="story">p标签下的NavigableString对象
    <a href="elsie" class="sister" id="link1">Elsie</a>ss
    <a href="lacie" class="sister" id="link2">Lacie</a>
    <a href="tillie" class="sister" id="link3">Tillie</a>well
  <blockquote>No longer bold</blockquote>
  </p> 
"""

soup = BeautifulSoup(html, "lxml")

tag_title = soup.find("title")
tag_title.string = "aa"
print(tag_title)

tag_p = soup.find("p")
tag_p.string = "qqqq"
print(tag_p)

tag_blockquote = soup.find("blockquote")
tag_blockquote.string.replace_with("wwww")
print(tag_blockquote)

"""
<title>aa</title>
<p class="story">qqqq</p>
<blockquote>wwww</blockquote>
"""

 

append()

1、Tag.append()方法想tag中添加内容,就好像Python的列表的.append()方法

2、如果想添加一段文本内容到文档中也没问题,可以调用Python的append()方法或调用工厂方法BeautifulSoup.new_string()方法

3、如果想要创建一段注释,或 NavigableString 的任何子类,将子类作为new_string()方法的第二个参数传入(需要from bs4 import Comment)

例10:

from bs4 import BeautifulSoup
from bs4 import Comment
html = """
  <title>The Dormouse's story</title>
  <p class="story">
    <a href="elsie" class="sister" id="link1">Elsie</a>ss
    <a href="lacie" class="sister" id="link2">Lacie</a>
    <a href="tillie" class="sister" id="link3">Tillie</a>well
  </p> 
"""

soup = BeautifulSoup(html, "lxml")
tag_title = soup.find("title")

tag_title.append("aaaa")

new_string_1 = soup.new_string("cccc")
tag_title.append(new_string_1)

new_string_2 = soup.new_string("bbbb",Comment)#需要调用from bs4 import Comment

tag_title.append(new_string_2)
print(tag_title)

"""
<title>The Dormouse's storyaaaacccc<!--bbbb--></title>
"""



clear()

Tag.clear()方法移除当前tag对象的内容:将指定tag对象下的NavigableString对象和子节点以及子节点下的NavigableString对象

例11:

from bs4 import BeautifulSoup
html = """
  <title>The Dormouse's story</title>
  <p class="story">p标签下的NavigableString对象
    <a href="elsie" class="sister" id="link1">Elsie</a>ss
    <a href="lacie" class="sister" id="link2">Lacie</a>
    <a href="tillie" class="sister" id="link3">Tillie</a>well
  </p> 
"""

soup = BeautifulSoup(html, "lxml")
tag_p = soup.find("p")
tag_p.clear()

print(tag_p)

"""
<p></p>
"""

 

new_tag()

1、创建一个tag最好的方法是调用工厂方法BeautifulSoup.new_tag()

2、我们可以使用 new_tag 方法来生成一个新的标签,然后使用 append() insert() 、insert_after() 、insert_before()方法来将标签添加到 HTML 树中
例12:

from bs4 import BeautifulSoup
html = """
<title>The Dormouse's story</title>
<p>
</p> 
"""
soup = BeautifulSoup(html, "lxml")
tag_p = soup.find("p")

new_tag_1 = soup.new_tag("a", href="http://www.example.com")#定义一个新的tag
tag_p.append(new_tag_1)#在哪里加新tag

new_tag_2 = soup.new_tag("c")#定义一个新的tag
soup.find("p").append(new_tag_2)#在哪里加新tag

print(tag_p)

"""
<p>
<a href="http://www.example.com"></a><c></c></p>
"""

例12_1:

from bs4 import BeautifulSoup
html = """
    <headline> 
    </headline>
"""
soup = BeautifulSoup(html, "xml")#解析器为xml
new_tag = soup.new_tag("a", href="http://www.example.com")#定义一个新的tag
soup.find("headline").append(new_tag)
#soup.a.string = "222"#如果想要新标签对有结束标签可以向其添加string,空string也可以
print(soup.headline)

"""
<headline>
<a href="http://www.example.com"/></headline>
"""

注:
从上面两个例子中可以看出
1、tag对象.append()方法不仅可以向tag对象中添加字符串(NavigableString对象),还可以向tag对象中添加新的tag对象(默认是添加位置为指定tag的中间的最后)

2、解析器为xml和解析为为lxml时,在添加tag对象时输出会有点不一致(为xml时添加的tag对象没有结束标签,当然其他地方也会有不同),所以感觉BeautifulSoup还是拿来解析HTML文件比较好

 

insert_before()和insert_after()

1、insert_before()方法在当前tag对象或文本节点前插入内容

2、insert_after()方法在当前tag对象或文本节点后插入内容

例13:

from bs4 import BeautifulSoup
html = """
<b>stop</b>
"""
soup = BeautifulSoup(html, "lxml")

new_tag = soup.new_tag("a")#定义一个新的tag
new_tag.string = "Don't"#定义新tag的string对象
soup.find("b").string.insert_before(new_tag)

print(soup.b)

"""
<b><a>Don't</a>stop</b>
"""

例13_1:

from bs4 import BeautifulSoup
html = """
<b>stop</b>
"""
soup = BeautifulSoup(html, "lxml")

soup.b.string.insert_after(soup.new_string("ever"))

print(soup.b)

"""
<b>stopever</b>
"""

注:
insert_before()和insert_after():它们两个的使用方法感觉都必须是NavigableString对象.方法()即tag对象.string.方法()。不然的话不会生效

 

 

补充

1、BeautifulSoup库中还有其他方法与属性

2、只要是一个tag对象,就可以使用下面的tag对象的属性和方法(以tag对象开头的)

目的方法返回值描述
获取soup对象BeautifulSoup("XML文件","解析器")soup对象BeautifulSoup方法用于返回一个HTML等文件的Soup对象
获取tag对象soup对象.标签名tag对象返回第一个符合要求的tag对象(标签对)
Soup对象.find("标签名")tag对象返回第一个符合要求的tag对象(标签对)
Soup对象.find_all("标签名")列表返回整个文档中由全部符合要求的tag对象(标签对)组成的列表
Soup对象.select("标签名")列表返回整个文档中由全部符合要求的tag对象(标签对)组成的列表
获取tag对象的nametag对象.name     (可被修改)字符串返回一个tag对象的名字。可先通过find_all()方法,返回全部符合要求的tag对象
获取tag对象的attrstag对象.attrs    (可被修改)字典返回一个由tag对象的属性组成的字典。可先通过find_all()方法,返回全部符合要求的tag对象
获取NavigableString对象tag对象.string    (可被修改)NavigableString对象返回tag对象的NavigableString对象。可先通过find_all()方法,返回全部符合要求的tag对象
tag对象.strings迭代器返回一个节点下所有的NavigableString对象,包括子孙节点的
获取tag对象的子节点tag对象.contents列表以列表的形式返回节点的所有节点,包括NavigableString对象
tag对象.childrenlist类型的迭代器以迭代器的形式返回节点下的所有子节点,包括NavigableString对象
获取tag对象的子孙节点tag对象.descendantslist类型的迭代器以迭代器的形式返回节点下的所有子孙节点,包括NavigableString对象
其他.parent 获取某个元素的父节点
.parents 可以递归得到元素的所有父辈节点
.next_sibling.previous_sibling 属性来查询兄弟节点
.next_siblings和.previous_siblings 可以对当前节点的兄弟节点迭代输出
.next_elements  .previous_elements 可以向前或向后访问文档的解析内容
find_parents()  find_parent() 用来搜索当前节点的父辈节点
find_next_siblings() 返回所有符合条件的后面的兄弟节点
find_previous_siblings() 返回所有符合条件的前面的兄弟节点
find_all_next() 返回所有符合条件的节点
find_all_previous() 返回所有符合条件的节点
new_string() 添加一段文本内容到文档中
insert() 把元素插入到指定的位置
insert_before() 在当前tag或文本节点前插入内容
insert_after() 在当前tag或文本节点后插入内容

 

 

Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐