序言
1. 内容介绍
本章详细介绍了BeautifulSoup 的安装、 BeautifulSoup 对象创建、BeautifulSoup 对象相关节点选取等内容。
2. 理论目标
- 了解 BeautifulSoup 的安装
- 了解 BeautifulSoup 对象创建
- 了解 BeautifulSoup 对象相关节点选取
3. 实践目标
- 能掌握 BeautifulSoup 的安装及对象创建,完成网页关键信息选取
4. 实践案例
无
5. 内容目录
-
1.BeautifulSoup 简介与安装
-
2.BeautifulSoup 创建及对象种类
-
3.BeautifulSoup 遍历文档树
-
4.BeautifulSoup 搜索文档树
-
5.BeautifulSoup CSS选择器
第1节 BeautifulSoup 简介与安装
1. BeautifulSoup 简介
Beautiful Soup 是一个HTML/XML的解析器,主要的功能也是如何解析和提取 HTML/XML 数据。
通过解析文档为用户提供需要抓取的数据,已成为和lxml、html6lib 一样出色的python 解释器,为用户灵活地提供不同的解析策略或强劲的速度。
简单来说,就是把HTML/XML生成规范性文档,方便查找目标元素。
2. BeautifulSoup 安装
使用 BeautifulSoup,除了安装beautifulsoup4,最好也安装解析器 lxml
- BeautifulSoup 安装:
pip install beautifulsoup4
- 解析器 lxml 安装:
pip install lxml
第2节 BeautifulSoup 创建及对象种类
1. BeautifulSoup 创建
## 导入BeautifulSoup 库,Beautiful Soup 4 已经被移植到 BS4,因此导入时我们需要 import bs4 from bs4 import BeautifulSoup ## 导入html 文档 with open('example.html', 'rb') as f: html = f.read() print(type(html)) bs = BeautifulSoup(html, "lxml") #解析html 文档内容,创建对象 print(bs)
<class 'bytes'> <!DOCTYPE html> <html> <head> <meta content="text/html;charset=utf-8" http-equiv="content-type"/> <meta content="IE=Edge" http-equiv="X-UA-Compatible"/> <meta content="always" name="referrer"/> <link href="https://ss1.bdstatic.com/5eN1bjq8AAUYm2zgoY3K/r/www/cache/bdorz/baidu.min.css" rel="stylesheet" type="text/css"/> <title>百度一下,你就知道 </title> </head> <body link="#0000cc"> <div id="wrapper"> <div id="head"> <div class="head_wrapper"> <div id="u1"> <a class="mnav" href="http://news.baidu.com" name="tj_trnews"><!-- 新闻 --></a> <a class="mnav" href="https://www.hao123.com" name="tj_trhao123">hao123 </a> <a class="mnav" href="http://map.baidu.com" name="tj_trmap">地图 </a> <a class="mnav" href="http://v.baidu.com" name="tj_trvideo">视频 </a> <a class="mnav" href="http://tieba.baidu.com" name="tj_trtieba">贴吧 </a> <a class="bri" href="//www.baidu.com/more/" name="tj_briicon" style="display: block;">更多产品 </a> </div> </div> </div> </div> </body> </html>
- Beautiful Soup 自动将输入文档转换为Unicode编码,输出文档转换为utf-8编码。
exampe.html 内容
<!DOCTYPE html> <html> <head> <meta content="text/html;charset=utf-8" http-equiv="content-type" /> <meta content="IE=Edge" http-equiv="X-UA-Compatible" /> <meta content="always" name="referrer" /> <link href="https://ss1.bdstatic.com/5eN1bjq8AAUYm2zgoY3K/r/www/cache/bdorz/baidu.min.css" rel="stylesheet" type="text/css" /> <title>百度一下,你就知道 </title> </head> <body link="#0000cc"> <div id="wrapper"> <div id="head"> <div class="head_wrapper"> <div id="u1"> <a class="mnav" href="http://news.baidu.com" name="tj_trnews"><!--新闻--> </a> <a class="mnav" href="https://www.hao123.com" name="tj_trhao123">hao123 </a> <a class="mnav" href="http://map.baidu.com" name="tj_trmap">地图 </a> <a class="mnav" href="http://v.baidu.com" name="tj_trvideo">视频 </a> <a class="mnav" href="http://tieba.baidu.com" name="tj_trtieba">贴吧 </a> <a class="bri" href="//www.baidu.com/more/" name="tj_briicon" style="display: block;">更多产品 </a> </div> </div> </div> </div> </body> </html>
2. BeautifulSoup 对象种类
BeautifulSoup4将复杂HTML文档转换成一个复杂的树形结构,每个节点都是Python对象,所有对象可以归纳为4种:
- BeautifulSoup
- Tag
- NavigableString
- Comment
BeautifulSoup
BeautifulSoup对象表示的是一个文档的内容,可以当作 Tag 对象。
print(bs) print(bs.name) print(type(bs.name)) print(bs.attrs)
<!DOCTYPE html> <html> <head> <meta content="text/html;charset=utf-8" http-equiv="content-type"/> <meta content="IE=Edge" http-equiv="X-UA-Compatible"/> <meta content="always" name="referrer"/> <link href="https://ss1.bdstatic.com/5eN1bjq8AAUYm2zgoY3K/r/www/cache/bdorz/baidu.min.css" rel="stylesheet" type="text/css"/> <title>百度一下,你就知道 </title> </head> <body link="#0000cc"> <div id="wrapper"> <div id="head"> <div class="head_wrapper"> <div id="u1"> <a class="mnav" href="http://news.baidu.com" name="tj_trnews"><!-- 新闻 --></a> <a class="mnav" href="https://www.hao123.com" name="tj_trhao123">hao123 </a> <a class="mnav" href="http://map.baidu.com" name="tj_trmap">地图 </a> <a class="mnav" href="http://v.baidu.com" name="tj_trvideo">视频 </a> <a class="mnav" href="http://tieba.baidu.com" name="tj_trtieba">贴吧 </a> <a class="bri" href="//www.baidu.com/more/" name="tj_briicon" style="display: block;">更多产品 </a> </div> </div> </div> </div> </body> </html> [document] <class 'str'> {}
Tag
Tag通俗点讲就是HTML中的一个个标签。
soup 加标签名轻松地获取这些标签的内容,查找的是在所有内容中的第一个符合要求的标签
# 获取title标签的所有内容 print("这是一个title 标签:\n", bs.title) print("\n") # 获取head标签的所有内容 print("这是一个head 标签:\n", bs.head) print("\n") # 获取第一个a标签的所有内容 print("这是一个a 标签:\n", bs.a) print("\n") # 类型 print("这是一个a 标签的类型:\n", type(bs.a))
这是一个title 标签: <title>百度一下,你就知道 </title>
这是一个head 标签: <head> <meta content="text/html;charset=utf-8" http-equiv="content-type"/> <meta content="IE=Edge" http-equiv="X-UA-Compatible"/> <meta content="always" name="referrer"/> <link href="https://ss1.bdstatic.com/5eN1bjq8AAUYm2zgoY3K/r/www/cache/bdorz/baidu.min.css" rel="stylesheet" type="text/css"/> <title>百度一下,你就知道 </title> </head>
这是一个a 标签: <a class="mnav" href="http://news.baidu.com" name="tj_trnews"><!-- 新闻 --></a>
这是一个a 标签的类型: <class 'bs4.element.Tag'>
Tag 有两个重要的属性
- name
- attrs
# bs 对象本身比较特殊,它的 name 即为 [document] print(bs.name) # 对于内部标签,输出的值便为标签本身的名称 print(bs.head.name) # 打印标签的所有属性,得到的类型是一个字典。 print(bs.a.attrs) # 获取属性内容 print(bs.a['class']) # 等价 bs.a.get('class') # 修改属性和内容,此处修改后就无法获取 bs.a['class'] = "newClass" print(bs.a) # 删除属性 del bs.a['class'] print(bs.a)
[document]
head
{'class': ['mnav'], 'href': 'http://news.baidu.com', 'name': 'tj_trnews'}
['mnav']
<a class="newClass" href="http://news.baidu.com" name="tj_trnews"><!-- 新闻 --></a>
<a href="http://news.baidu.com" name="tj_trnews"><!-- 新闻 --></a>
NavigableString
即标签内部的内容、文字。
## 获取标签及相应内容 print(bs.title) print(bs.title.string) print("\n类型是:") print(type(bs.title.string))
<title>百度一下,你就知道 </title>
百度一下,你就知道
类型是:
<class 'bs4.element.NavigableString'>
Comment
Comment 对象是一个特殊类型的NavigableString 对象,其输出的内容不包括注释符号,但包括注释符号里面的内容。
可以简单认为,如果标签内部的内容、文字是注释内容(文档说明内容),即为Comment,否则为NavigableString(网页内容)。
如果不处理,可能会对我们的文本处理造成意想不到的麻烦。
# <a class="mnav" href="http://news.baidu.com" name="tj_trnews"><!-- 新闻 --></a> print(bs.a) print(bs.a.string) # 新闻 print(type(bs.a.string)) # <class 'bs4.element.Comment'> print(str(type(bs.a.string)) == "<class 'bs4.element.Comment'>")
<a class="mnav" href="http://news.baidu.com" name="tj_trnews"><!-- 新闻 --></a>
新闻
<class 'bs4.element.Comment'>
True
第3节 BeautifulSoup 遍历文档树
BeautifulSoup 主要提供以下属性遍历文档树
序号 | 属性 | 作用说明 |
---|---|---|
1 | .contents | 获取Tag的所有子节点,返回一个list |
2 | .children | 获取Tag的所有子节点,返回一个生成器 |
3 | .descendants | 获取Tag的所有子孙节点 |
4 | .strings | 如果Tag包含多个字符串,即在子孙节点中有内容,可以用此获取,而后进行遍历 |
5 | .stripped_strings | 与strings用法一致,只不过可以去除掉那些多余的空白内容 |
6 | .parent | 获取Tag的父节点 |
7 | .parents | 递归得到父辈元素的所有节点,返回一个生成器 |
8 | .previous_sibling | 获取当前Tag的上一个节点,属性通常是字符串或空白,真实结果是当前标签与上一个标签之间的顿号和换行符 |
10 | .previous_siblings | 获取当前Tag的上面所有的兄弟节点,返回一个生成器 |
9 | .next_sibling | 获取当前Tag的下一个节点,属性通常是字符串或空白,真是结果是当前标签与下一个标签之间的顿号与换行符 |
11 | .next_siblings | 获取当前Tag的下面所有的兄弟节点,返回一个生成器 |
12 | .previous_element | 获取解析过程中上一个被解析的对象(字符串或tag),与previous_sibling不同,这里包括不同级的 |
14 | .previous_elements | 返回一个生成器,可以向前访问文档的解析内容 |
13 | .next_element | 获取解析过程中下一个被解析的对象(字符串或tag),与next_sibling不同,这里包括不同级的 |
15 | .next_elements | 返回一个生成器,可以向后访问文档的解析内容 |
16 | .has_attr | 判断Tag是否包含属性 |
1. 子孙标签
print(".contents:获取Tag的所有子节点,返回一个list(含换行符):\n") print(bs.head.contents) print("*"*50 + "此处为分隔符" + "*"*50 + "\n") print(".children:获取Tag的所有子节点,返回一个生成器(含换行符):\n") print(bs.head.children) print("\n遍历结果:") for i in bs.head.children: print(i) print("*"*50 + "此处为分隔符" + "*"*50 + "\n") print(".descendants:获取Tag的所有子孙节点,不同的是,.descendants对所有tag 的子孙节点进行递归循环:\n") print(bs.head.descendants) print("\n遍历结果:") for i in bs.head.descendants: print(i)
.contents:获取Tag的所有子节点,返回一个list(含换行符):
['\n', <meta content="text/html;charset=utf-8" http-equiv="content-type"/>, '\n', <meta content="IE=Edge" http-equiv="X-UA-Compatible"/>, '\n', <meta content="always" name="referrer"/>, '\n', <link href="https://ss1.bdstatic.com/5eN1bjq8AAUYm2zgoY3K/r/www/cache/bdorz/baidu.min.css" rel="stylesheet" type="text/css"/>, '\n', <title>百度一下,你就知道 </title>, '\n']
*********************************************此处为分隔符*****************************************
.children:获取Tag的所有子节点,返回一个生成器(含换行符):
<list_iterator object at 0x000002388A6CB940>
遍历结果:
<meta content="text/html;charset=utf-8" http-equiv="content-type"/>
<meta content="IE=Edge" http-equiv="X-UA-Compatible"/>
<meta content="always" name="referrer"/>
<link href="https://ss1.bdstatic.com/5eN1bjq8AAUYm2zgoY3K/r/www/cache/bdorz/baidu.min.css" rel="stylesheet" type="text/css"/>
<title>百度一下,你就知道 </title>
**************************************此处为分隔符*************************************
.descendants:获取Tag的所有子孙节点,不同的是,.descendants对所有tag 的子孙节点进行递归循环:
<generator object Tag.descendants at 0x000002388A6D2970>
遍历结果:
<meta content="text/html;charset=utf-8" http-equiv="content-type"/>
<meta content="IE=Edge" http-equiv="X-UA-Compatible"/>
<meta content="always" name="referrer"/>
<link href="https://ss1.bdstatic.com/5eN1bjq8AAUYm2zgoY3K/r/www/cache/bdorz/baidu.min.css" rel="stylesheet" type="text/css"/>
<title>百度一下,你就知道 </title>
百度一下,你就知道
2. 子孙标签内容(文本内容)
print(".strings:如果Tag包含多个字符串,即在子孙节点中有内容,可以用此获取,而后进行遍历:\n") print(bs.head.strings) print("\n遍历结果:") for i in bs.head.strings: print(i) print("*"*50 + "此处为分隔符" + "*"*50 + "\n") print(".stripped_strings:与strings用法一致,只不过可以去除掉那些多余的空白内容:\n") print(bs.head.stripped_strings) print("\n遍历结果:") for i in bs.head.stripped_strings: print(i)
.strings:如果Tag包含多个字符串,即在子孙节点中有内容,可以用此获取,而后进行遍历:
<generator object Tag._all_strings at 0x000002388A6D2190>
遍历结果:
百度一下,你就知道
******************************************此处为分隔符*****************************************
.stripped_strings:与strings用法一致,只不过可以去除掉那些多余的空白内容:
<generator object Tag.stripped_strings at 0x000002388A6D2190>
遍历结果:
百度一下,你就知道
3. 父标签
print(bs.a) print(".parent:获取Tag的父节点:\n") print(bs.a.parent) print("\n" + "*"*50 + "此处为分隔符" + "*"*50 + "\n") print(".parents:递归得到父辈元素的所有节点,返回一个生成器:\n") print(bs.a.parents) print("\n遍历结果:") n = 1 for i in bs.a.parents: print("第{}个父标签\n".format(str(n))) print(i) n = n + 1
<a class="mnav" href="http://news.baidu.com" name="tj_trnews"><!-- 新闻 --></a>
.parent:获取Tag的父节点:
<div id="u1">
<a class="mnav" href="http://news.baidu.com" name="tj_trnews"><!-- 新闻 --></a>
<a class="mnav" href="https://www.hao123.com" name="tj_trhao123">hao123 </a>
<a class="mnav" href="http://map.baidu.com" name="tj_trmap">地图 </a>
<a class="mnav" href="http://v.baidu.com" name="tj_trvideo">视频 </a>
<a class="mnav" href="http://tieba.baidu.com" name="tj_trtieba">贴吧 </a>
<a class="bri" href="//www.baidu.com/more/" name="tj_briicon" style="display: block;">更多产品 </a>
</div>
*******************************************此处为分隔符*******************************************
.parents:递归得到父辈元素的所有节点,返回一个生成器:
<generator object PageElement.parents at 0x000002388A79AF20>
遍历结果:
第1个父标签
<div id="u1">
<a class="mnav" href="http://news.baidu.com" name="tj_trnews"><!-- 新闻 --></a>
<a class="mnav" href="https://www.hao123.com" name="tj_trhao123">hao123 </a>
<a class="mnav" href="http://map.baidu.com" name="tj_trmap">地图 </a>
<a class="mnav" href="http://v.baidu.com" name="tj_trvideo">视频 </a>
<a class="mnav" href="http://tieba.baidu.com" name="tj_trtieba">贴吧 </a>
<a class="bri" href="//www.baidu.com/more/" name="tj_briicon" style="display: block;">更多产品 </a>
</div>
第2个父标签
<div class="head_wrapper">
<div id="u1">
<a class="mnav" href="http://news.baidu.com" name="tj_trnews"><!-- 新闻 --></a>
<a class="mnav" href="https://www.hao123.com" name="tj_trhao123">hao123 </a>
<a class="mnav" href="http://map.baidu.com" name="tj_trmap">地图 </a>
<a class="mnav" href="http://v.baidu.com" name="tj_trvideo">视频 </a>
<a class="mnav" href="http://tieba.baidu.com" name="tj_trtieba">贴吧 </a>
<a class="bri" href="//www.baidu.com/more/" name="tj_briicon" style="display: block;">更多产品 </a>
</div>
</div>
...
4. 上一个节点
print(".previous_sibling:获取当前Tag的上一个节点,属性通常是字符串或空白,真实结果是当前标签与上一个标签之间的顿号和换行符:\n") print(bs.body.previous_sibling) print("\n" + "*"*50 + "此处为分隔符" + "*"*50 + "\n") print(".previous_siblings:获取当前Tag的上面所有的兄弟节点,返回一个生成器:\n") print(bs.body.previous_siblings) print("\n遍历结果:") n = 1 for i in bs.body.previous_siblings: print("往上数,第{}个同级标签\n".format(str(n))) print(i) n = n + 1
.previous_sibling:获取当前Tag的上一个节点,属性通常是字符串或空白,真实结果是当前标签与上一个标签之间的顿号和换行符:
*********************************************此处为分隔符*********************************************
.previous_siblings:获取当前Tag的上面所有的兄弟节点,返回一个生成器:
<generator object PageElement.previous_siblings at 0x000002388A6D2040>
遍历结果:
往上数,第1个同级标签
往上数,第2个同级标签
<head>
<meta content="text/html;charset=utf-8" http-equiv="content-type"/>
<meta content="IE=Edge" http-equiv="X-UA-Compatible"/>
<meta content="always" name="referrer"/>
<link href="https://ss1.bdstatic.com/5eN1bjq8AAUYm2zgoY3K/r/www/cache/bdorz/baidu.min.css" rel="stylesheet" type="text/css"/>
<title>百度一下,你就知道 </title>
</head>
往上数,第3个同级标签
5. 下一个节点
print(".next_sibling:获取当前Tag的下一个节点,属性通常是字符串或空白,真是结果是当前标签与下一个标签之间的顿号与换行符,同级节点:\n") print(bs.a.next_sibling) print("\n" + "*"*50 + "此处为分隔符" + "*"*50 + "\n") print(".next_siblings:获取当前Tag的下面所有的兄弟节点,返回一个生成器:\n") print(bs.a.next_siblings) print("\n遍历结果:") n = 1 for i in bs.a.next_siblings: print("往下数,第{}个同级标签\n".format(str(n))) print(i) n = n + 1
.next_sibling:获取当前Tag的下一个节点,属性通常是字符串或空白,真是结果是当前标签与下一个标签之间的顿号与换行符,同级节点:
**********************************************此处为分隔符**********************************************
.next_siblings:获取当前Tag的下面所有的兄弟节点,返回一个生成器:
<generator object PageElement.next_siblings at 0x000002388A6D2C10>
遍历结果:
往下数,第1个同级标签
往下数,第2个同级标签
<a class="mnav" href="https://www.hao123.com" name="tj_trhao123">hao123 </a>
往下数,第3个同级标签
往下数,第4个同级标签
<a class="mnav" href="http://map.baidu.com" name="tj_trmap">地图 </a>
往下数,第5个同级标签
往下数,第6个同级标签
<a class="mnav" href="http://v.baidu.com" name="tj_trvideo">视频 </a>
往下数,第7个同级标签
往下数,第8个同级标签
<a class="mnav" href="http://tieba.baidu.com" name="tj_trtieba">贴吧 </a>
往下数,第9个同级标签
往下数,第10个同级标签
<a class="bri" href="//www.baidu.com/more/" name="tj_briicon" style="display: block;">更多产品 </a>
往下数,第11个同级标签
6. 上一个元素
print(".previous_element:获取解析过程中上一个被解析的对象(字符串或tag),与previous_sibling不同的,这里包括不同级的:\n") print(bs.a.previous_element) print("\n" + "*"*50 + "此处为分隔符" + "*"*50 + "\n") print(".previous_elements:返回一个生成器,可以向前访问文档的解析内容:\n") print(bs.a.previous_elements) print("\n遍历结果:") n = 1 for i in bs.a.previous_elements: print("往上数,第{}个元素\n".format(str(n))) print(i) n = n + 1
.previous_element:获取解析过程中上一个被解析的对象(字符串或tag),与previous_sibling不同的,这里包括不同级的:
**********************************************此处为分隔符**********************************************
.previous_elements:返回一个生成器,可以向前访问文档的解析内容:
<generator object PageElement.previous_elements at 0x000002388A6D30B0>
遍历结果:
往上数,第1个元素
往上数,第2个元素
<div id="u1">
<a href="http://news.baidu.com" name="tj_trnews"><!-- 新闻 --></a>
<a class="mnav" href="https://www.hao123.com" name="tj_trhao123">hao123 </a>
<a class="mnav" href="http://map.baidu.com" name="tj_trmap">地图 </a>
<a class="mnav" href="http://v.baidu.com" name="tj_trvideo">视频 </a>
<a class="mnav" href="http://tieba.baidu.com" name="tj_trtieba">贴吧 </a>
<a class="bri" href="//www.baidu.com/more/" name="tj_briicon" style="display: block;">更多产品 </a>
</div>
往上数,第3个元素
往上数,第4个元素
...
往上数,第27个元素
html
7. 下一个元素
print(".next_element:获取解析过程中下一个被解析的对象(字符串或tag),可能与next_sibling不同,这里包括不同级的:\n") print(bs.meta.next_element) print("\n" + "*"*50 + "此处为分隔符" + "*"*50 + "\n") print(".next_elements:返回一个生成器,可以向后访问文档的解析内容:\n") print(bs.meta.next_elements) print("\n遍历结果:") n = 1 for i in bs.meta.next_elements: print("往下数,第{}个元素\n".format(str(n))) print(i) n = n + 1
.next_element:获取解析过程中下一个被解析的对象(字符串或tag),可能与next_sibling不同,这里包括不同级的:
**********************************************此处为分隔符**********************************************
.next_elements:返回一个生成器,可以向后访问文档的解析内容:
<generator object PageElement.next_elements at 0x000002388A6D2190>
遍历结果:
往下数,第1个元素
往下数,第2个元素
<meta content="IE=Edge" http-equiv="X-UA-Compatible"/>
往下数,第3个元素
...
8. 属性判断
print(".has_attr:判断Tag是否包含属性:\n") print(bs.a.has_attr("href"))
.has_attr:判断Tag是否包含属性:
True
第4节 BeautifulSoup 搜索文档树
BeautifulSoup 通过find_all 和find 对文档树进行搜索。
正在上传…重新上传取消
1. find_all的使用
find_all () 方法搜索当前 tag 的所有 tag 子节点,并判断是否符合过滤器的条件。
- 语法:find_all(name, attrs, recursive, text, **kwargs, limit)
name 参数
查找所有名字为 name 的 tag
- 字符串过滤:匹配标签名等于字符串的tag,返回tag列表。
a_list = bs.find_all("a") print(a_list)
[<a href="http://news.baidu.com" name="tj_trnews"><!-- 新闻 --></a>, <a class="mnav" href="https://www.hao123.com" name="tj_trhao123">hao123 </a>, <a class="mnav" href="http://map.baidu.com" name="tj_trmap">地图 </a>, <a class="mnav" href="http://v.baidu.com" name="tj_trvideo">视频 </a>, <a class="mnav" href="http://tieba.baidu.com" name="tj_trtieba">贴吧 </a>, <a class="bri" href="//www.baidu.com/more/" name="tj_briicon" style="display: block;">更多产品 </a>]
- 正则表达式过滤:匹配标签名符合正则表达式的tag,返回tag列表。
import re pattern = re.compile("^b") a_list = bs.find_all(pattern) print(a_list)
[<body link="#0000cc"> <div id="wrapper"> <div id="head"> <div class="head_wrapper"> <div id="u1"> <a href="http://news.baidu.com" name="tj_trnews"><!-- 新闻 --></a> <a class="mnav" href="https://www.hao123.com" name="tj_trhao123">hao123 </a> <a class="mnav" href="http://map.baidu.com" name="tj_trmap">地图 </a> <a class="mnav" href="http://v.baidu.com" name="tj_trvideo">视频 </a> <a class="mnav" href="http://tieba.baidu.com" name="tj_trtieba">贴吧 </a> <a class="bri" href="//www.baidu.com/more/" name="tj_briicon" style="display: block;">更多产品 </a> </div> </div> </div> </div> </body>]
- 列表:匹配标签名符合列表中的任一元素的tag,返回tag列表。
a_list = bs.find_all(["a", "meta"]) print(a_list)
[<meta content="text/html;charset=utf-8" http-equiv="content-type"/>, <meta content="IE=Edge" http-equiv="X-UA-Compatible"/>, <meta content="always" name="referrer"/>, <a href="http://news.baidu.com" name="tj_trnews"><!-- 新闻 --></a>, <a class="mnav" href="https://www.hao123.com" name="tj_trhao123">hao123 </a>, <a class="mnav" href="http://map.baidu.com" name="tj_trmap">地图 </a>, <a class="mnav" href="http://v.baidu.com" name="tj_trvideo">视频 </a>, <a class="mnav" href="http://tieba.baidu.com" name="tj_trtieba">贴吧 </a>, <a class="bri" href="//www.baidu.com/more/" name="tj_briicon" style="display: block;">更多产品 </a>]
- 方法:传入一个方法,方法只接受一个元素参数, 如果这个方法返回 True 表示当前元素匹配并且被找到,如果不是则返 False。
## 校验当前元素,如果包含 class 属性却不包含 id 属性。 def has_class_but_no_id(tag): return tag.has_attr('class') and not tag.has_attr('id') a_list = bs.find_all(has_class_but_no_id) print(a_list)
[<div class="head_wrapper"> <div id="u1"> <a href="http://news.baidu.com" name="tj_trnews"><!-- 新闻 --></a> <a class="mnav" href="https://www.hao123.com" name="tj_trhao123">hao123 </a> <a class="mnav" href="http://map.baidu.com" name="tj_trmap">地图 </a> <a class="mnav" href="http://v.baidu.com" name="tj_trvideo">视频 </a> <a class="mnav" href="http://tieba.baidu.com" name="tj_trtieba">贴吧 </a> <a class="bri" href="//www.baidu.com/more/" name="tj_briicon" style="display: block;">更多产品 </a> </div> </div>, <a class="mnav" href="https://www.hao123.com" name="tj_trhao123">hao123 </a>, <a class="mnav" href="http://map.baidu.com" name="tj_trmap">地图 </a>, <a class="mnav" href="http://v.baidu.com" name="tj_trvideo">视频 </a>, <a class="mnav" href="http://tieba.baidu.com" name="tj_trtieba">贴吧 </a>, <a class="bri" href="//www.baidu.com/more/" name="tj_briicon" style="display: block;">更多产品 </a>]
attrs 参数
针对部分tag 属性在搜索不能使用,比如 HTML5 中的 data-* 属性。
data_soup = BeautifulSoup('<div data-foo="value">foo!</div>') data_list = data_soup.find_all(attrs={"data-foo": "value"}) print(data_list)
[<div data-foo="value">foo!</div>]
recursive 参数
调用 tag 的 find_all() 方法时,Beautiful Soup 会检索当前 tag 的所有子孙节点,如果只想搜索 tag 的直接子节点,可以使用参数 recursive=False。
a_list = bs.find_all(text="html", recursive=False) print(a_list)
<!DOCTYPE html>
<html>
<head>
<meta content="text/html;charset=utf-8" http-equiv="content-type"/>
<meta content="IE=Edge" http-equiv="X-UA-Compatible"/>
<meta content="always" name="referrer"/>
<link href="https://ss1.bdstatic.com/5eN1bjq8AAUYm2zgoY3K/r/www/cache/bdorz/baidu.min.css" rel="stylesheet" type="text/css"/>
<title>百度一下,你就知道 </title>
</head>
<body link="#0000cc">
<div id="wrapper">
<div id="head">
<div class="head_wrapper">
<div id="u1">
<a class="mnav" href="http://news.baidu.com" name="tj_trnews"><!-- 新闻 --></a>
<a class="mnav" href="https://www.hao123.com" name="tj_trhao123">hao123 </a>
<a class="mnav" href="http://map.baidu.com" name="tj_trmap">地图 </a>
<a class="mnav" href="http://v.baidu.com" name="tj_trvideo">视频 </a>
<a class="mnav" href="http://tieba.baidu.com" name="tj_trtieba">贴吧 </a>
<a class="bri" href="//www.baidu.com/more/" name="tj_briicon" style="display: block;">更多产品 </a>
</div>
</div>
</div>
</div>
</body>
</html>
['html']
a_list = bs.find_all(text="地图 ", recursive=False) print(a_list)
[]
text 参数
通过 text 参数可以搜搜文档中的字符串内容
a_list = bs.find_all(text="地图 ") print(a_list)
['地图 ']
limit 参数
find_all() 方法返回全部的搜索结构,使用limit 参数限制返回结果的数量。效果与 SQL 中的 limit 关键字类似。
a_list = bs.find_all("a", limit=2) print(a_list)
[<a href="http://news.baidu.com" name="tj_trnews"><!-- 新闻 --></a>, <a class="mnav" href="https://www.hao123.com" name="tj_trhao123">hao123 </a>]
**kwargs 参数
查找属性值符合要求的tag。class 是 python 的关键词,需加个下划线。
## 单条件 a_list = bs.find_all(id="wrapper") print(a_list)
[<div id="wrapper">
<div id="head">
<div class="head_wrapper">
<div id="u1">
<a href="http://news.baidu.com" name="tj_trnews"><!-- 新闻 --></a>
<a class="mnav" href="https://www.hao123.com" name="tj_trhao123">hao123 </a>
<a class="mnav" href="http://map.baidu.com" name="tj_trmap">地图 </a>
<a class="mnav" href="http://v.baidu.com" name="tj_trvideo">视频 </a>
<a class="mnav" href="http://tieba.baidu.com" name="tj_trtieba">贴吧 </a>
<a class="bri" href="//www.baidu.com/more/" name="tj_briicon" style="display: block;">更多产品 </a>
</div>
</div>
</div>
</div>]
## 正则表达式匹配 import re pattern = re.compile("baidu.com") a_list = bs.find_all(href=pattern) print(a_list)
[<a href="http://news.baidu.com" name="tj_trnews"><!-- 新闻 --></a>, <a class="mnav" href="http://map.baidu.com" name="tj_trmap">地图 </a>, <a class="mnav" href="http://v.baidu.com" name="tj_trvideo">视频 </a>, <a class="mnav" href="http://tieba.baidu.com" name="tj_trtieba">贴吧 </a>, <a class="bri" href="//www.baidu.com/more/" name="tj_briicon" style="display: block;">更多产品 </a>]
## 多条件 a_list = bs.find_all(class_="mnav",href="http://news.baidu.com") print(a_list)
[]
2. find的使用
find() 与 find_all() 方法唯一的区别是 find_all() 方法的返回结果是值包含一个元素的列表,而 find() 方法直接返回去全部结果
- 语法:find( name , attrs , recursive , text , **kwargs )
从结果可以看出find_all(),尽管传入了limit=1,但是返回值仍然为一个列表,当我们只需要取一个值时,远不如find()方法方便。但是如果未搜索到值时,将返回一个None。
第5节 BeautifulSoup CSS选择器
BeautifulSoup支持发部分的CSS选择器,在Tag获取BeautifulSoup对象的.select()方法中传入字符串参数,即可使用CSS选择器的语法找到Tag,返回类型是 list
- 通过标签名查找:标签名
- 通过类名查找:.类名
- 通过id查找:#id值
- 组合查找:多个查找条件组合
- 属性查找:标签名[属性名 = ‘属性值’]
- 直接子标签查找:父标签名 > 子标签名
- 兄弟节点标签查找:标签名 ~ 标签名
- 获取内容:.get_text()
1. 通过标签名查找
a_list = bs.select("a") print(a_list)
[<a href="http://news.baidu.com" name="tj_trnews"><!-- 新闻 --></a>, <a class="mnav" href="https://www.hao123.com" name="tj_trhao123">hao123 </a>, <a class="mnav" href="http://map.baidu.com" name="tj_trmap">地图 </a>, <a class="mnav" href="http://v.baidu.com" name="tj_trvideo">视频 </a>, <a class="mnav" href="http://tieba.baidu.com" name="tj_trtieba">贴吧 </a>, <a class="bri" href="//www.baidu.com/more/" name="tj_briicon" style="display: block;">更多产品 </a>]
2. 通过类名查找
a_list = bs.select('.mnav') print(a_list)
[<a class="mnav" href="https://www.hao123.com" name="tj_trhao123">hao123 </a>, <a class="mnav" href="http://map.baidu.com" name="tj_trmap">地图 </a>, <a class="mnav" href="http://v.baidu.com" name="tj_trvideo">视频 </a>, <a class="mnav" href="http://tieba.baidu.com" name="tj_trtieba">贴吧 </a>]
3. 通过id查找
a_list = bs.select('#wrapper') print(a_list)
[<div id="wrapper">
<div id="head">
<div class="head_wrapper">
<div id="u1">
<a href="http://news.baidu.com" name="tj_trnews"><!-- 新闻 --></a>
<a class="mnav" href="https://www.hao123.com" name="tj_trhao123">hao123 </a>
<a class="mnav" href="http://map.baidu.com" name="tj_trmap">地图 </a>
<a class="mnav" href="http://v.baidu.com" name="tj_trvideo">视频 </a>
<a class="mnav" href="http://tieba.baidu.com" name="tj_trtieba">贴吧 </a>
<a class="bri" href="//www.baidu.com/more/" name="tj_briicon" style="display: block;">更多产品 </a>
</div>
</div>
</div>
</div>]
4. 组合查找
a_list = bs.select('div .mnav') print(a_list)
[<a class="mnav" href="https://www.hao123.com" name="tj_trhao123">hao123 </a>, <a class="mnav" href="http://map.baidu.com" name="tj_trmap">地图 </a>, <a class="mnav" href="http://v.baidu.com" name="tj_trvideo">视频 </a>, <a class="mnav" href="http://tieba.baidu.com" name="tj_trtieba">贴吧 </a>]
5. 属性查找
a_list = bs.select('div[id="head"]') print(a_list)
[<div id="head">
<div class="head_wrapper">
<div id="u1">
<a href="http://news.baidu.com" name="tj_trnews"><!-- 新闻 --></a>
<a class="mnav" href="https://www.hao123.com" name="tj_trhao123">hao123 </a>
<a class="mnav" href="http://map.baidu.com" name="tj_trmap">地图 </a>
<a class="mnav" href="http://v.baidu.com" name="tj_trvideo">视频 </a>
<a class="mnav" href="http://tieba.baidu.com" name="tj_trtieba">贴吧 </a>
<a class="bri" href="//www.baidu.com/more/" name="tj_briicon" style="display: block;">更多产品 </a>
</div>
</div>
</div>]
6. 直接子标签查找
a_list = bs.select('div > a') print(a_list)
[<a href="http://news.baidu.com" name="tj_trnews"><!-- 新闻 --></a>, <a class="mnav" href="https://www.hao123.com" name="tj_trhao123">hao123 </a>, <a class="mnav" href="http://map.baidu.com" name="tj_trmap">地图 </a>, <a class="mnav" href="http://v.baidu.com" name="tj_trvideo">视频 </a>, <a class="mnav" href="http://tieba.baidu.com" name="tj_trtieba">贴吧 </a>, <a class="bri" href="//www.baidu.com/more/" name="tj_briicon" style="display: block;">更多产品 </a>]
7. 兄弟节点标签查找
a_list = bs.select('a ~ a') print(a_list)
[<a class="mnav" href="https://www.hao123.com" name="tj_trhao123">hao123 </a>, <a class="mnav" href="http://map.baidu.com" name="tj_trmap">地图 </a>, <a class="mnav" href="http://v.baidu.com" name="tj_trvideo">视频 </a>, <a class="mnav" href="http://tieba.baidu.com" name="tj_trtieba">贴吧 </a>, <a class="bri" href="//www.baidu.com/more/" name="tj_briicon" style="display: block;">更多产品 </a>]
8. 获取内容
a_list = bs.select('a') for i in a_list: print(i.get_text())
hao123
地图
视频
贴吧
更多产品
开始实验
第6节 附录