04.BeautifulSoup使用

一、BeautifulSoup

1、简介

是一个可以从HTML或XML文件中提取数据的Python库。

BeautifulSoup最主要的功能是从网页抓取数据,BeautifulSoup自动将输入文档转换为Unicode编码,输出文档转换为utf-8编码。BeautifulSoup支持Python标准库中的HTML解析器,还支持一些第三方的解析器,如果我们不安装它,则 Python 会使用 Python默认的解析器,lxml 解析器更加强大,速度更快,推荐使用lxml 解析器。Beautiful Soup自动将输入文档转换为Unicode编码,输出文档转换为utf-8编码。你不需要考虑编码方式,除非文档没有指定一个编码方式,这时,Beautiful Soup就不能自动识别编码方式了。然后,你仅仅需要说明一下原始编码方式就可以了。

精髓:将HTML 转换成 标签对象。(这是利用html的结构性)
'''
首先,一个节点中是可以包含多个子节点和多个字符串的。例如html节点中包含着head和body节点。所以BeautifulSoup就可以将一个HTML的网页用这样一层层嵌套的节点来进行表示。
'''

BeautifulSoup 有四大节点对象:
1、BeautifulSoup:解析网页获得的对象。
2、Tag(重点掌握):通过BeautifulSoup对象来提取数据,基本都围绕着这个对象来进行操作。
3、NavigableString(可以遍历的字符串):一般被标签包裹在其中的的文本就是 NavigableString格式。
4、Comment:指的是在网页中的注释以及特殊字符串。</pre>

2、BeautifulSoup的优点?

相对于正则来说更加的简单方便。

二、使用:

安装:pip install beautifulsoup4

导包:from bs4 import BeautifulSoup

指定解释器:BeautifulSoup解析网页需要指定一个可用的解析器,以下是主要几种解析器

bs解析器.png

若是没有指定,会默认使用 html.parser,并且会出现警告,提示你没有指定。

UserWarning: No parser was explicitly specified, so I'm using the best available HTML parser for 
this system ("lxml"). This usually isn't a problem, but if you run this code on another system, or 
in a different virtual environment, it may use a different parser and behave differently.
​
The code that caused this warning is on line 40 of the file /home/pyvip/Python_crawler_pratice
/bs_xpath/bs4的使用.py. To get rid of this warning, pass the additional argument 'features="lxml"' 
to the BeautifulSoup constructor.
​
 soup = BeautifulSoup(html_str)

提示:如果一段HTML或XML文档格式不正确的话,那么在不同的解析器中返回的结果可能是不一样的,所以要指定某一个解析器。

1、Tag 的使用:

可以利用beautifulsoup4对象加标签名 轻松地获取这些标签的内容,这些对象的类 bs4.element.Tag。
但是注意,它查找的是在所有内容中的 第一个 符合要求的标签。
对于 Tag,它有两个重要的属性,是name 和 attrs.
①创建BeautifulSoup对象:
soup = BeautifulSoup(html_str) #将html文件转换成可操作对象。
print( type(soup) ) #返回结果:<class 'bs4.BeautifulSoup'>

• ②获取标签:返回的只有第一个满足要求的标签的所有内容
a1 = soup.a #获取第一个符合条件的标签
print(a1) #返回结果:<a href="http://www.taobao.com">淘宝<a>

• ③获取属性:
soup.a["href"] #获取第一个符合条件的标签的属性
soup.a.get('href')
二者等价,返回结果均为:'http://www.taobao.com'
soup.a.attrs #输出a标签的全部属性,类型为字典
soup.a.name #输出标签的名称,即为 a。
soup.name #beautifulsoup4对象本身特殊,返回的是[document],不是列表。

• ④获取内容:

text = soup.a.text
print(text) #返回结果:'淘宝'</pre>

(1)亲戚标签选择(遍历文档树):

属性:

1.children:获取Tag的所有直接子节点,返回<class 'list_iterator'> 迭代器

例:

         p = soup.p
         print(p.children)
         print(list(p.children))

 ['\n', <a class="product" href="https://www.baidu.com">关于Python</a>, '\n', 
<a href="http://www.taobao.com">好好学习</a>, '\n', <a href="javascript:void(0)">      人生苦短</a>,
 '\n', <a href="javascript:void(0)">我用Python</a>, '\n']

注意:在这里 \n 也算是一个子节点哦

2.contents:获取Tag的所有直接子节点,返回<class 'list'> 列表

例1:
print(type(p.contents)) #list
print(p.contents) #可通过索引获取它的某一个元素。

注:children和contents返回的都是当前Tag下的直接子节点,不过一个是迭代器,一个是列表

3.descendants: 获取Tag的所有子孙节点,返回<class 'generator'>,生成器

例1:

 print(type(p.descendants))
 print(list(p.descendants))

['\n', <a href="http://www.taobao.com">淘宝</a>, '淘宝', '\n', <span class="link">
<a class="product" href="https://www.baidu.com">关于Python</a>
<a href="http://www.taobao.com">好好学习</a>
<a href="javascript:void(0)">人生苦短</a>
<a href="javascript:void(0)">我用Python</a>
</span>, '\n', <a class="product" href="https://www.baidu.com">关于Python</a>, 
'关于Python', '\n', <a href="http://www.taobao.com">好好学习</a>, '好好学习', '\n',
 <a href="javascript:void(0)">人生苦短</a>, '人生苦短', '\n', <a href="javascript:void(0)">我用Python</a>, 
'我用Python', '\n', '\n', <span class="about-me">关于我: <i class="PyWhich py-wechat"></i> 贤思齐</span>, 
'关于我: ', <i class="PyWhich py-wechat"></i>, ' 贤思齐', '\n']
4.strings: 如果Tag包含多个字符串,即在子孙节点中所有文本内容,可以用此获取,而后进行遍历(\n也会 看作一个字符串).返回<class 'generator'>

例1:

 print(type(p.strings))
 print(list(p.strings))

['\n', '淘宝', '\n', '\n', '关于Python', '\n', '好好学习', '\n',
 '人生苦短', '\n', '我用Python', '\n', '\n', '关于我: ', ' 贤思齐', '\n']            
5.string: 与strings不同,只会返回一个且若有多条文本只会返回 None。

例1:
print(p.string) #None

6.stripped_strings: 与strings用法一致,只不过可以去除掉那些多余的空白内容,<class 'generator'>

例1:

 print(type(p.stripped_strings))  #  <class 'generator'>
 print(list(p.stripped_strings)) 

['淘宝', '关于Python', '好好学习', '人生苦短', '我用Python', '关于我:', '贤思齐']、
7.parent: 获取Tag的直接父节点,

例1:
print(type(p.parent)) #<class 'bs4.element.Tag'>
print(p.parent)

8.parents: 递归得到父辈元素的所有节点,返回一个生成器
9.next_sibling: 获取Tag的下一个兄弟节点

注意:如果查询的Tag下一行是空行,那么输出的是 '\n' ,因为'\n'也被视作一个节点。
例1:也可以这么做
print(p.next_sibling.next_sibling)#获取下一个的下一个

10.next_siblings:返回的是生成器
11.previous_sibling:获取Tag的上一个兄弟节点
12.previous_siblings:返回的是生成器

(2)find_all(搜索文档树):

1.find_all(name, attrs, recursive, text, **kwargs) #返回所有符合条件的Tag,默认在所有子孙节 点搜索。

切记:返回的是BeautifulSoup特有的结果集(<class 'bs4.element.ResultSet'>),里面装的是标签对象。

参数:
-name:通过标签名搜索可以是列表,可以找出所有符合的标签,并返回 列表。
例:
BeautifulSoup对象.find_all(['p','a'])#获取所有p标签,和a标签

-attrs:通过attrs搜索,通过传递给attrs一个字典,来搜索参数。
例:
BeautifulSoup对象.find_all(attrs={'class':'MW'})#获取class为MW的所有Tag.

-text:单独添加text返回的是符合text的字符串列表。
例1:
BeautifulSoup对象.find_all(text='China')#返回的是China的字符串,要是想得到包 含该文本的标签呢?
例2:
BeautifulSoup对象.find_all('a',text='China')#返回的是文本为China的a标签。

-recursive:通过设置recursive=False,将搜索范围限制在直接子节点中。
recursive 意为递归:True,递归,所有子孙元素;False,不递归,只有子元素。

-kwargs:与正则表达式结合
例1:
import re
beautifulsoup对象.find_all(re.compile('^b'))
返回以b开头的标签

(3)CSS选择器

BeautifulSoup支持发部分的CSS选择器

方法 : BeautifulSoup对象.select()
参数 : str,即可使用CSS选择器的语法找到目标Tag.
返回值 : 切记( 选择的结果以列表形式返回 )

from bs4 import BeautifulSoup
soup = BeautifulSoup('html文本','解析工具推荐lxml')

1、通过标签名查找:
例1:
soup.select('title') #获取title标签
选择所有p标签中的第三个标签
soup.select("p:nth-of-type(3)") 相当于soup.select(p)[2]
选择body标签下的所有a标签
soup.select("body a")
直接子标签查找:
beautifulsoup对象.select('p > a') #获取 p标签下的直接子标签
兄弟节点标签查找:
soup.select("#link1 ~ .mysis")# 选择id=link1后的所有兄弟节点标签
soup.select("#link1 + .mysis")# 选择id=link1后的下一个兄弟节点标签

2、通过类名查找:
例1:
soup.select("a.mysis")# 选择a标签,其类属性为mysis的标签
soup.select("a['mysis']") #也可以这样查找,这是属性查找,[]也可写成class=‘mysis’

3、通过id查找:
例1:
soup.select('a#link1')#选择a标签,其id属性为link1的标签

4、属性查找:
例1:
选择a标签,其属性中存在myname的所有标签
soup.select("a[myname]")
选择a标签,其属性href=http://example.com/lacie的所有标签
soup.select("a[href='http://example.com/lacie']")
选择a标签,其href属性以http开头
soup.select('a[href^="http"]')
选择a标签,其href属性以lacie结尾
soup.select('a[href$="lacie"]')
选择a标签,其href属性包含.com
soup.select('a[href*=".com"]')
从html中排除某标签,此时soup中不再有script标签
[s.extract() for s in soup('script')]
如果想排除多个呢
[s.extract() for s in soup(['script','fram']) ]

5、获取内容:get_text()、strings属性

get_text()方法:返回的是列表。 strings属性:返回的是迭代对象。

例1:

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>;
</p>
and they lived at the bottom of a well.
<p class="story">...</p>
</body>
"""
from bs4 import BeautifulSoup
'''
以列表的形式返回
'''

 soup = BeautifulSoup(html_doc, 'html.parser')
 s = soup.select('p.story') #选择 class为story的  p标签。
 s[0].get_text()  # p节点及子孙节点的文本内容
 s[0].get_text("|")  # 指定文本内容的分隔符
 s[0].get_text("|", strip=True)  # 去除文本内容前后的空白
 print(s[0].get("class"))  # p节点的class属性值列表(除class外都是返回字符串)

2、NavigableString 的使用:

-NavigableString: (常用)
介绍:意思为可以遍历的字符串,一般被标签包裹在其中的文本就是NavigableString格式,而获取标签内部的文 本需要 string 属性。
例1:

 html = '''<html>
 <td>some text</td> 
 <td></td>
 <td><p>more text</p></td>
 <td>even <p>more text</p></td>
 </html>'''

 from bs4 import BeautifulSoup
 soup = BeautifulSoup(html, 'lxml')
 tds = soup.find_all('td')
​
 for td in tds:
 print(td.string)
 print(type(td.string ))

 for td in tds:
 print(td.text)
 print(td.text)

string 属性的返回类型是 bs4.element.NavigableString,而 text 属性的返回类型是 str。并且若标 签内部没有文本 string 属性返回的是None ,而text属性不会返回None</pre>

3、Comment 的使用:

介绍:在网页中的注释以及特殊字符串。Comment 对象是一个特殊类型的 NavigableString 对象,其输出的内 容不包括注释符号。

html_str="""<!DOCTYPE html>
<html>
<head>
<title>39爬虫少年们</title>
  <meta charset="utf-8">
  <link rel="stylesheet" href="http://www.taobao.com">
  <link rel="stylesheet" href="https://www.baidu.com">
  <link rel="stylesheet" href="http://at.alicdn.com/t/font_684044_un7umbuwwfp.css">
</head>
<body>
<!-- footer start -->
<footer id="footer">
    <div class="footer-box">
        <div class="footer-content"  >
            <p class="top-content"  id="111">
                    <a  href="http://www.taobao.com">淘宝</a> 
                    <span class="link">
                        <a class="product"  href="https://www.baidu.com">关于Python</a> 
                        <a  href="http://www.taobao.com">好好学习</a> 
                        <a href="javascript:void(0)">人生苦短</a> 
                        <a href="javascript:void(0)">我用Python</a>
                    </span>
                <span class="about-me">关于我: <i class="PyWhich py-wechat"></i> 贤思齐</span>
            </p>
            <p class="bottom-content">
                <span>地址: xxxx</span>
                <span>联系方式: <a href="tel:400-1567-315">400-1567-315</a> (24小时在线)</span>
            </p>
        </div>
        <p class="copyright-desc">
            Copyright &copy; 2008 - 2019 xxx有限公司. All Rights Reserved
        </p>
    </div>
</footer>

</body>
</html>
"""</pre>

小测试:

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>;
    </p>
        and they lived at the bottom of a well.
    <p class="story">...</p>
</body>

from bs4 import BeautifulSoup
'''
以列表的形式返回
'''

soup = BeautifulSoup(html_doc, 'html.parser')
soup.select('title')  # title标签
soup.select("p:nth-of-type(3)")  # 第三个p节点
soup.select('body a')  # body下的所有子孙a节点
soup.select('p > a')  # 所有p节点下的所有a直接节点
soup.select('p > #link1')  # 所有p节点下的id=link1的直接子节点
soup.select('#link1 ~ .sister')  # id为link1的节点后面class=sister的所有兄弟节点
soup.select('#link1 + .sister')  # id为link1的节点后面class=sister的第一个兄弟节点
soup.select('.sister')  # class=sister的所有节点
soup.select('[class="sister"]')  # class=sister的所有节点
soup.select("#link1")  # id=link1的节点
soup.select("a#link1")  # a节点,且id=link1的节点
soup.select('a[href]')  # 所有的a节点,有href属性
soup.select('a[href="http://example.com/elsie"]')  # 指定href属性值的所有a节点
soup.select('a[href^="http://example.com/"]')  # href属性以指定值开头的所有a节点
soup.select('a[href$="tillie"]')  # href属性以指定值结尾的所有a节点
soup.select('a[href*=".com/el"]')  # 支持正则匹配</pre>
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
禁止转载,如需转载请通过简信或评论联系作者。
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,837评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,551评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,417评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,448评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,524评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,554评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,569评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,316评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,766评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,077评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,240评论 1 343
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,912评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,560评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,176评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,425评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,114评论 2 366
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,114评论 2 352

推荐阅读更多精彩内容