我是靠谱客的博主 无辜钥匙,这篇文章主要介绍深入理解BeautifulSoup,现在分享给大家,希望可以做个参考。

我生也有涯,而知也无涯,以有涯随无涯,殆矣!——庄子

如同Python的所有库一样,BeautifulSoup易于上手,想要如臂使指却要下一番功夫。我们不妨通过一个例子来细细品味几个有趣的细节,来看看我们是否真的了解了BeautifulSoup。

  • 初识——BeautifulSoup是通过将网页数据解析成搜索树来加速元素查找的python 库
  • BeautifulSoup总结下来两种搜索文档树的方法最重要:find_all(name,attrs,string)和select(selector)
  • 例子地址——URL:https://www.itcodemonkey.com/article/14987.html
  • 这个例子,我们随后会发现比百度文档简单很多
复制代码
1
2
3
import requests from bs4 import BeautifulSoup as Soup
复制代码
1
2
resp = requests.get('https://www.itcodemonkey.com/article/14987.html')
复制代码
1
2
soup = Soup(resp.text,'lxml')
复制代码
1
2
soup.title
复制代码
1
2
<title>​一个超有意思的 Python 综合能力测试网站 - IT程序猿 </title>
复制代码
1
2
soup.title.name #访问标签名而不是标签的name属性,即使有name属性,也需要通过get()提取
复制代码
1
2
'title'
复制代码
1
2
soup.title.attrs #title标签没有任何属性
复制代码
1
2
{}
复制代码
1
2
soup.title.text
复制代码
1
2
'u200b一个超有意思的 Python 综合能力测试网站 - IT程序猿 '
复制代码
1
2
soup.title.text[1:]
复制代码
1
2
'一个超有意思的 Python 综合能力测试网站 - IT程序猿 '
复制代码
1
2
tags_search_by_attrs = soup.find_all(attrs={'class':'kq__article-power'})
复制代码
1
2
tags_search_by_attrs
复制代码
1
2
3
[<div class="kq__article-power"> <p>来自:<a href="https://mp.weixin.qq.com/s/x5n2YeK5J56h-VAzb1Cejw" target="_blank">高级农民工</a>(微信号:Mocun6),作者:苏克1900</p> </div>]

find 与 find_all 方法也支持筛选条件为正则表达式的情况

复制代码
1
2
import re
复制代码
1
2
tags_search_by_string = soup.find_all(string=re.compile('power'))
复制代码
1
2
tags_search_by_string
复制代码
1
2
[]
复制代码
1
2
tags_search_by_string = soup.find_all(string=re.compile('作者:苏克1900'))
复制代码
1
2
tags_search_by_string
复制代码
1
2
['(微信号:Mocun6),作者:苏克1900']

BeautifulSoup有NavigableString对象,找到的就是文本内容对象而不是包含此文本内容的 p标签,如果需要的是p标签,可以如下:

复制代码
1
2
tags_search_by_string[0].findParent()
复制代码
1
2
<p>来自:<a href="https://mp.weixin.qq.com/s/x5n2YeK5J56h-VAzb1Cejw" target="_blank">高级农民工</a>(微信号:Mocun6),作者:苏克1900</p>
  • 从这里我们看到find_all()传string参数找到的是NavigaleString对象
  • 如果要找到文字所在标签,使用.findParent方法,如下
复制代码
1
2
type(tags_search_by_string[0])
复制代码
1
2
bs4.element.NavigableString

find跟find_all的区别让我们想起selinium的find_element_by_id跟find_elements_by_id的区别,Python里面经常碰到这种匹配一个与多个的简单逻辑.譬如select与select_one,正则re库中的re.find与re.find_all,还有mongodb中的sheet.insert_one与sheet.insert_many

复制代码
1
2
tag1 = soup.find(attrs={'class':'kq__article-power'})
复制代码
1
2
tag1
复制代码
1
2
3
<div class="kq__article-power"> <p>来自:<a href="https://mp.weixin.qq.com/s/x5n2YeK5J56h-VAzb1Cejw" target="_blank">高级农民工</a>(微信号:Mocun6),作者:苏克1900</p> </div>
复制代码
1
2
tag1.text
复制代码
1
2
'n来自:高级农民工(微信号:Mocun6),作者:苏克1900 '
复制代码
1
2
tag1.text.strip()
复制代码
1
2
'来自:高级农民工(微信号:Mocun6),作者:苏克1900'

tag.text属性返回标签内所有标签的文本内容,比stripped_strings显得更方便,而且保留了一定的文本排版格式,建议优先使用

  • tag.string是获取本标签文本内容
  • tag.strings是以生成器返回tag内所有标签文本
  • tag.stripped_strings是以列表返回所有非空字符文本,并去除前后不可见字符
  • tag.text属性返回tag内部所有标签文本的’’.join(),等于’’.join(tag.strings)
复制代码
1
2
list(tag1.strings)
复制代码
1
2
['n', '来自:', '高级农民工', '(微信号:Mocun6),作者:苏克1900', ' ']
复制代码
1
2
''.join(tag1.strings)
复制代码
1
2
'n来自:高级农民工(微信号:Mocun6),作者:苏克1900 '
复制代码
1
2
list(tag1.stripped_strings)
复制代码
1
2
['来自:', '高级农民工', '(微信号:Mocun6),作者:苏克1900']
复制代码
1
2
type(tag1.string) #有strings属性,就没有string属性
复制代码
1
2
NoneType

标签的唯一性体现在 id , name , class 这三个属性中,前端设计中, id , class是css样式表定义需要的,name是javascript可能需要的

复制代码
1
2
content_tag = soup.find(id='article_content')
复制代码
1
2
content_tag.name #TagName是div
复制代码
1
2
'div'
复制代码
1
2
content_tag.attrs
复制代码
1
2
{'id': 'article_content'}

我们可方便的使用get方法获取到标签属性值

复制代码
1
2
content_tag.get('id')
复制代码
1
2
'article_content'

也可以简单用访问符[],如下,等同与.get

复制代码
1
2
content_tag['id']
复制代码
1
2
'article_content'
复制代码
1
2
3
for tag in soup.select('div p'): print(tag.text)
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
来自:高级农民工(微信号:Mocun6),作者:苏克1900 这里是「每周分享」的第 28 期。往期分享内容可以在公众号后台的 「不务正业」菜单中找到,Python 类的文章在另一个「不误正业」菜单中。 这一期的话题是:一个学习 Python 的趣味网站 。 最近在网上看到一个非常有意思的 Python 游戏通关网站,一共有 33 关,每一关都需要利用 Python 知识解题找到答案,然后进入下一关。很考验对 Python 的综合掌握能力,比如有的闯关需要用到正则表达式,有的要用到爬虫。 我们平常学 Python 都是按章节顺序、包或者模块来学,容易前学后忘。正好可以拿这个网站来综合测试一下对 Python 的掌握情况,以便查缺补漏。 来说说这个网站怎么玩。 这是网站主页面,很有历史感对吧,诞生了已有十几年了。但千万不要因为看着像老古董而小瞧它。 我们来玩玩看,点击「get challenged」开始挑战。 第 0 关是 Warming up 热身环节: 这一关要求是修改 URL 链接,给的提示是电脑上的数学表达式:2 的 38 次方,所以大概就是需要计算出数值,然后修改url 进入下一关。 所以这关就是考 Python 的基本数值运算,你知道怎么算么? 打开 Python 自带终端,一行代码就能计算出结果: 把原链接中的 0替换为 274877906944回车就会进入下一关: 游戏这就正式开始了。图片中的笔记本给了三组字母,很容易发现规律:前面的字母往后移动两位就是后面的字母。 那么需要做的就是根据这个规律把下面的提示字符串,做位移解密得到真正的句子含义: 这道题考察字符串编码和 for 循环相关知识,代码实现如下: 得到结果: 作者很风趣,当然不能手动去一个推算了,推荐用 string.maketrans() 这个方法解决,我们上面采取的是比较直接的方法,官方给出了更为精简的方法: 然后把 url 中的 map 改为ocr回车就来到了第 2 关: 作者接着说过关的提示可能在书里(当然不可能了)也可能在网页源代码里。那就右键查看源代码往下拉看到绿色区域,果然找到了问题: 意思就是:要在下面这一大串字符里找到出现次数最少的几个字符 考察了这么几个知识点: 正则表达式提取字符串 list 计数 条件语句 如果是你,你会怎么做? 来看下,十行代码快速实现: 首先,用 Requests 请求网页然后用正则提取出字符串,接着 for 循环计算每个字符出现的次数。 可以看到出现次数最少的就是最后几个字符,合起来是「equality」,替换 url 字符就闯过过了第 2 关进入下一关继续挑战。是不是有点意思? 后面每一关都需要用到相关的 Python 技巧解决,比如第 4 关: 这一关作者弄了个小恶作剧,需要手动输入数值到 url 中然后回车,你以为这样就完了么?并没有它有会不断重复弹出新的数值让你输入,貌似无穷尽。 所以,这一关肯定不能采取手动输入的方法闯关,自然要用到 Python 了。要实现自动填充修改 url 回车跳转到新 url,循环直到网页再也无法跳转为止这一功能。 如果是你,你会怎么做? 其实,一段简单的爬虫加正则就能搞定。思路很简单,把每次网页中的数值提取出来替换成新的 url 再请求网页,循环下去,代码实现如下: 输出结果如下: 可以看到,最终循环了 85 次找到了最后一个数字16044,输入到 url 中就闯关成功。 33 关既有趣又能锻炼使用 Python 解决问题的技巧,感兴趣的话去玩玩看。 网址:http://www.pythonchallenge.com/ 如果遇到不会做的题,可以在这里找到参考答案: 中参考文教程: https://www.cnblogs.com/jimnox/archive/2009/12/08/tips-to-python-challenge.html 官方参考教程: http://garethrees.org/2007/05/07/python-challenge/ © 2017-2018 IT程序猿 闽ICP备08108865号-1

现在我们回过头总结一下

BeautifulSoup4将复杂HTML文档转换成一个复杂的树形结构,每个节点都是Python对象,所有对象可以归纳为4种:

  • Tag对象,对应html文档对象树的所有标签节点
  • BeautifulSoup对象,大部分时候可以将其当成特殊的Tag对象,也就是说,两者属性想通
  • NavigableString对象,即.string属性返回的对象
  • Comment对象,一种特殊的Navigable对象

接下来随便看下select_one与select方法

复制代码
1
2
soup.name
复制代码
1
2
'[document]'

上面这行代码是怎么回事?实际上soup对象对应html DOM(文档对象树),即document对象,所有不奇怪

复制代码
1
2
soup.html.name #html标签是根标签
复制代码
1
2
'html'
复制代码
1
2
tag_one = soup.select_one('div h2')
复制代码
1
2
s = tag_one.string
复制代码
1
2
type(s)
复制代码
1
2
bs4.element.NavigableString
复制代码
1
2
s
复制代码
1
2
'u200b一个超有意思的 Python 综合能力测试网站'

我们知道这里没有media标签,我们试着看看如果查找会发生什么

复制代码
1
2
soup.img
复制代码
1
2
<img src="/themes/simplebootx/Public/assets/images/logo.jpg"/>
复制代码
1
2
type(soup.media) #返回是None
复制代码
1
2
NoneType

NavigbleString好像就是字符串,嗯,还是挺大区别的,试试下面

复制代码
1
2
s.findParent()
复制代码
1
2
3
<h2>​一个超有意思的 Python 综合能力测试网站</h2> ## 目前也就这个findPrarent方法有点用--通过文本找标签--但是绝对鸡肋

在学习scrapy的时候我被scrapy的resp.css方法所采用的pseudo-element思路震撼,bs4的NavigableString是相似的思路,但好像不太一样

没用的NavigableString都说了,不妨再加上Comment对象

复制代码
1
2
tag_with_com = soup.select_one('div.article-infobox')
复制代码
1
2
tag_with_com
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
<div class="article-infobox"> <!-- <span>2019-06-04 11:16:15 by 腾云苏克1900</span> --> <span> 2019-06-04 11:16:15 分类:<a href="/category/LovePython/" target="_blank">Python编程</a> </span> <!-- <span> <a href="javascript:;"><i class="fa fa-eye"></i><span>407</span></a> <a href="/article/do_like/id/14987" class="js-count-btn"><i class="fa fa-thumbs-up"></i><span class="count">0</span></a> <a href="/user/favorite/do_favorite/id/14987" class="js-favorite-btn" data-title="​一个超有意思的 Python 综合能力测试网站" data-url="/article/index/id/14987/cid/12" data-key="ff47aHivkIvsKwOnbWEhDDusXW2GsFcKAwqUheOLnziCwBiraKjR1ZDyGc21M7qxxUpoTDm275NxfLs"> <i class="fa fa-star-o"></i> </a> </span> --> </div>
复制代码
1
2
tag_with_com.contents
复制代码
1
2
3
4
5
6
7
8
9
10
['n', ' <span>2019-06-04 11:16:15 by 腾云苏克1900</span> ', 'n', <span> 2019-06-04 11:16:15 分类:<a href="/category/LovePython/" target="_blank">Python编程</a> </span>, 'n', ' <span>ntt ttt<a href="javascript:;"><i class="fa fa-eye"></i><span>407</span></a>ntttttt<a href="/article/do_like/id/14987" class="js-count-btn"><i class="fa fa-thumbs-up"></i><span class="count">0</span></a>ntttttt<a href="/user/favorite/do_favorite/id/14987" class="js-favorite-btn" data-title="u200b一个超有意思的 Python 综合能力测试网站" data-url="/article/index/id/14987/cid/12" data-key="ff47aHivkIvsKwOnbWEhDDusXW2GsFcKAwqUheOLnziCwBiraKjR1ZDyGc21M7qxxUpoTDm275NxfLs">nttttttt<i class="fa fa-star-o"></i>ntttttt</a>nttttt</span> ', 'n']
复制代码
1
2
q = tag_with_com.contents[1]
复制代码
1
2
q
复制代码
1
2
' <span>2019-06-04 11:16:15 by 腾云苏克1900</span> '
复制代码
1
2
type(q)
复制代码
1
2
bs4.element.Comment

接下介绍重要的find_all方法。学习爬虫初期,我只使用select和select_one方法,其他如find、find_all完全摒弃——事实证明,find_all还是有用的。

  • find_all返回一个满足过滤器条件的tag的列表
  • find_all(过滤器)的作用体现在当你需要满足某种条件的所有tag,而用css selector不太方便的时候,譬如你需要遍历所有标签
复制代码
1
2
tags_by_filter = soup.find_all(True)
复制代码
1
2
len(tags_by_filter)
复制代码
1
2
496
复制代码
1
2
import re
复制代码
1
2
tags_by_fs = soup.find_all(string=re.compile('超有意思'))
  • 注意按位传参find_all第一个是name,即标签名
  • **find_all( name , attrs , recursive , string , kwargs )
  • 可以如上按不限量关键字传参string=XXX,或者id=XXX,
复制代码
1
2
tags_by_fs
复制代码
1
2
3
4
5
['u200b一个超有意思的 Python 综合能力测试网站 - IT程序猿 ', 'u200b一个超有意思的 Python 综合能力测试网站', ' <span>ntt ttt<a href="javascript:;"><i class="fa fa-eye"></i><span>414</span></a>ntttttt<a href="/article/do_like/id/14987" class="js-count-btn"><i class="fa fa-thumbs-up"></i><span class="count">0</span></a>ntttttt<a href="/user/favorite/do_favorite/id/14987" class="js-favorite-btn" data-title="u200b一个超有意思的 Python 综合能力测试网站" data-url="/article/index/id/14987/cid/12" data-key="bd5bYLBaA374IaeyFBnpH9EkbjSP1HFeBba4nuijZGIjxySbjNrj/CIpNIhCOvEl3J7Z/P6BT3c6Pfk">nttttttt<i class="fa fa-star-o"></i>ntttttt</a>nttttt</span> ']
  • 节目最后说明一下BeautifulSoup的核心就是CSS Selector,就是要采取一种比XPath更优秀的解析方式
  • scrapy的resp.css方法对应soup.select方法,但是实话说前者真的很厉害
复制代码
1
2
3
for t in tags_by_fs: print(type(t))
复制代码
1
2
3
4
<class 'bs4.element.NavigableString'> <class 'bs4.element.NavigableString'> <class 'bs4.element.Comment'>

如前面所说,find_all(string=正则表达式)这种形式找到的是匹配的NavigableString对象,嗯,Comment对象是特殊的NavigableString对象,所以也会被找到

复制代码
1
2
tags_by_fs[2].findParent()
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
<div class="article-infobox"> <!-- <span>2019-06-04 11:16:15 by 腾云苏克1900</span> --> <span> 2019-06-04 11:16:15 分类:<a href="/category/LovePython/" target="_blank">Python编程</a> </span> <!-- <span> <a href="javascript:;"><i class="fa fa-eye"></i><span>414</span></a> <a href="/article/do_like/id/14987" class="js-count-btn"><i class="fa fa-thumbs-up"></i><span class="count">0</span></a> <a href="/user/favorite/do_favorite/id/14987" class="js-favorite-btn" data-title="​一个超有意思的 Python 综合能力测试网站" data-url="/article/index/id/14987/cid/12" data-key="bd5bYLBaA374IaeyFBnpH9EkbjSP1HFeBba4nuijZGIjxySbjNrj/CIpNIhCOvEl3J7Z/P6BT3c6Pfk"> <i class="fa fa-star-o"></i> </a> </span> --> </div>

总结一下就是"注释"是特殊的文本

复制代码
1
2
special_tag = tags_by_fs[2].findParent()
复制代码
1
2
special_tag.text
复制代码
1
2
'nn 2019-06-04 11:16:15 分类:Python编程nnn'

最后

以上就是无辜钥匙最近收集整理的关于深入理解BeautifulSoup的全部内容,更多相关深入理解BeautifulSoup内容请搜索靠谱客的其他文章。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(48)

评论列表共有 0 条评论

立即
投稿
返回
顶部