对于前段时间写的那篇[****python爬虫A****](https://blog.csdn.net/qq_43613772/article/details/89603947?utm_source=app)介绍了正则表达式的相关用法以及urllib库,进行了一些简单的爬取,但是一旦正则表达式写的有问题,得到的可能就不是我们想要的结果了。而且对于一个网页来说,都有一定的特殊结构和层级关系,而且很多节点都有id或class来作区分,所以借助它们的结构和属性来提取不也可以吗?
这一节中,我们就来介绍一个强大的解析工具Beautiful Soup,它借助网页的结构和属性等特性来解析网页。有了它,我们不用再去写一些复杂的正则表达式,只需要简单的几条语句,就可以完成网页中某个元素的提取。Beautiful Soup就是Python的一个HTML或XML的解析库,可以用它来方便地从网页中提取数据。
***首先准备安装Beautiful Soup和lxml。***
#### 用法:
1、节点选择器:
```
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>
""" #假设的HTML代码,直接写出出来啦,比较直观观察运行情况
from bs4 import BeautifulSoup
soup = BeautifulSoup(html, 'lxml')
print(soup.title)
print(type(soup.title))
print(soup.title.string)
print(soup.head)
print(soup.p)
```
运行结果如下:
```
<title>The Dormouse's story</title>
<class 'bs4.element.Tag'>
The Dormouse's story
<head><title>The Dormouse's story</title></head>
<p class="title" name="dromouse"><b>The Dormouse's story</b></p>
```
这里依然选用刚才的HTML代码,首先打印输出title节点的选择结果,输出结果正是title节点加里面的文字内容。接下来,输出它的类型,是bs4.element.Tag类型,这是Beautiful Soup中一个重要的数据结构。经过选择器选择后,选择结果都是这种Tag类型。Tag具有一些属性,比如string属性,调用该属性,可以得到节点的文本内容,所以接下来的输出结果正是节点的文本内容。
接下来,我们又尝试选择了head节点,结果也是节点加其内部的所有内容。最后,选择了p节点。不过这次情况比较特殊,我们发现结果是第一个p节点的内容,后面的几个p节点并没有选到。也就是说,当有多个节点时,这种选择方式只会选择到第一个匹配的节点,其他的后面节点都会忽略。
节点选择器我自我感觉没我们接下来介绍的方法选择器灵活,这里节点选择器的关联选择和嵌套选择等内容不再阐述。
一、方法选择器
前面所讲的选择方法都是通过属性来选择的,这种方法非常快,但是如果进行比较复杂的选择的话,它就比较烦琐,不够灵活了。幸好,Beautiful Soup还为我们提供了一些查询方法,比如find_all()和find()等,调用它们,然后传入相应的参数,就可以灵活查询了。
**find_all()**
API如下:```find_all(name , attrs , recursive , text , **kwargs)```
1、name:可以根据节点查元素
```
html='''
<div class="panel">
<div class="panel-heading">
<h4>Hello</h4>
</div>
<div class="panel-body">
<ul class="list" id="list-1">
<li class="element">Foo</li>
<li class="element">Bar</li>
<li class="element">Jay</li>
</ul>
<ul class="list list-small" id="list-2">
<li class="element">Foo</li>
<li class="element">Bar</li>
</ul>
</div>
</div>
'''
from bs4 import BeautifulSoup
soup = BeautifulSoup(html, 'lxml')
print(soup.find_all(name='ul'))
print(type(soup.find_all(name='ul')[0]))
```
运行结果:
```
[<ul class="list" id="list-1">
<li class="element">Foo</li>
<li class="element">Bar</li>
<li class="element">Jay</li>
</ul>, <ul class="list list-small" id="list-2">
<li class="element">Foo</li>
<li class="element">Bar</li>
</ul>]
<class 'bs4.element.Tag'>
```
2、attrs:除了根据节点名查询,我们也可以传入一些属性来查询
```
html='''
<div class="panel">
<div class="panel-heading">
<h4>Hello</h4>
</div>
<div class="panel-body">
<ul class="list" id="list-1" name="elements">
<li class="element">Foo</li>
<li class="element">Bar</li>
<li class="element">Jay</li>
</ul>
<ul class="list list-small" id="list-2">
<li class="element">Foo</li>
<li class="element">Bar</li>
</ul>
</div>
</div>
'''
from bs4 import BeautifulSoup
soup = BeautifulSoup(html, 'lxml')
print(soup.find_all(attrs={'id': 'list-1'}))
print(soup.find_all(attrs={'name': 'elements'}))
```
运行结果:
```
[<ul class="list" id="list-1" name="elements">
<li class="element">Foo</li>
<li class="element">Bar</li>
<li class="element">Jay</li>
</ul>]
[<ul class="list" id="list-1" name="elements">
<li class="element">Foo</li>
<li class="element">Bar</li>
<li class="element">Jay</li>
</ul>]
```
3、text:参数可用来匹配节点的文本,传入的形式可以是字符串,可以是正则表达式对象
```
import re
html='''
<div class="panel">
<div class="panel-body">
<a>Hello, this is a link</a>
<a>Hello, this is a link, too</a>
</div>
</div>
'''
from bs4 import BeautifulSoup
soup = BeautifulSoup(html, 'lxml')
print(soup.find_all(text=re.compile('link')))
```
运行结果:
```
['Hello, this is a link', 'Hello, this is a link, too']
```
find()
除了find_all()方法,还有find()方法,只不过后者返回的是单个元素,也就是第一个匹配的元素,而前者返回的是所有匹配的元素组成的列表
```
html='''
<div class="panel">
<div class="panel-heading">
<h4>Hello</h4>
</div>
<div class="panel-body">
<ul class="list" id="list-1">
<li class="element">Foo</li>
<li class="element">Bar</li>
<li class="element">Jay</li>
</ul>
<ul class="list list-small" id="list-2">
<li class="element">Foo</li>
<li class="element">Bar</li>
</ul>
</div>
</div>
'''
from bs4 import BeautifulSoup
soup = BeautifulSoup(html, 'lxml')
print(soup.find(name='ul'))
print(type(soup.find(name='ul')))
print(soup.find(class_='list'))
```
运行结果:
```
<ul class="list" id="list-1">
<li class="element">Foo</li>
<li class="element">Bar</li>
<li class="element">Jay</li>
</ul>
<class 'bs4.element.Tag'>
<ul class="list" id="list-1">
<li class="element">Foo</li>
<li class="element">Bar</li>
<li class="element">Jay</li>
</ul>
```
这里的返回结果不再是列表形式,而是第一个匹配的节点元素,类型依然是Tag类型。
另外,还有许多查询方法,其用法与前面介绍的find_all()、find()方法完全相同,只不过查询范围不同,这里简单说明一下。
*find_parents()和find_parent():前者返回所有祖先节点,后者返回直接父节点。
find_next_siblings()和find_next_sibling():前者返回后面所有的兄弟节点,后者返回后面第一个兄弟节点。
find_previous_siblings()和find_previous_sibling():前者返回前面所有的兄弟节点,后者返回前面第一个兄弟节点。
find_all_next()和find_next():前者返回节点后所有符合条件的节点,后者返回第一个符合条件的节点。
find_all_previous()和find_previous():前者返回节点后所有符合条件的节点,后者返回第一个符合条件的节点。*
网友评论