爬虫 BeautifulSoup模块
【一】介绍
【1】说明
- BeautifulSoup库是python的一个第三方库,主要用于处理HTML和XML文档
- 他提供了一些简单的、python式的函数来解析、导航、搜索以及修改分析树,使得从网页抓取的数据变得简单高效
- BeautifulSoup自动将输入文档的转换为Unicode编码,输出文档转换为UTF8编码,用户无需担心编码问题
- BeautifulSoup将复杂HTML文档转换成一个复杂的树形结构,并将每个节点表示为Python对象
【2】安装引入
- 安装:注意是4
pip install BeautifulSoup4
- 导入
from bs4 import BeautifulSoup
【3】解析器
解析器 | 使用方法 | 特点 |
---|---|---|
自带html.parser | BeautifulSoup(页面源码,‘html.parser’) | 简单易用:标准库的一部分,无需安装 速度适中:性能不是最快的,但对于大所数常见任务足以 功能基础:提供了基本的HTML解析功能,对复杂的HTML或错误会吃力 |
第三方lxml | BeautifulSoup(页面源码,‘lxml’) | 性能优越:所有python HTML/XML解析器中性能最好的 功能丰富:支持Xpath和CSS选择器 需要自行导入安装 |
第三方html5lib | BeautifulSoup(页面源码,‘html5lib’) | 兼容性好:能够处理不符合规范的HTML代码 纯Python实现:无需任何外部依赖,可在任何支持Python的环境中使用 速度相对较慢:兼容性和纯Python导致 它符合HTML5规范 |
【4】四种主要对象
-
BeautifulSoup 对象
- 当使用Beautiful Soup解析一个HTML或XML文档时,会得到一个
BeautifulSoup
对象。 - 这个对象基本上是一个包含了整个解析后的文档内容的容器。
- 可以通过
BeautifulSoup
对象来访问和搜索文档中的其他对象,如Tag
、NavigableString
和Comment
。
- 当使用Beautiful Soup解析一个HTML或XML文档时,会得到一个
-
Tag 对象
Tag
对象对应于HTML或XML文档中的标签。- 例如,在
<p>This is a paragraph.</p>
中,<p>
和</p>
是标签,它们会被解析为Tag
对象。 - 每个
Tag
对象都有名称和属性,可以通过.name
和.attrs
来访问。 - 此外,
Tag
对象还可以包含其他Tag
对象、NavigableString
对象或Comment
对象,这些可以通过.contents
和.children
等属性来访问。 - tag的属性可以被添加、删除或修改
- tag的属性操作方法与字典一样
-
NavigableString 对象
NavigableString
对象代表标签之间的文本内容。- 在上面的例子中,“This is a paragraph.”就是一个
NavigableString
对象。 - 与普通的Python字符串不同,
NavigableString
对象可以与文档中的其他部分(如Tag
对象)进行交互和搜索。
-
Comment 对象
Comment
对象代表HTML或XML文档中的注释。- 在HTML中,注释以
<!--
开始,以-->
结束。 - 这些注释在解析时会被转换为
Comment
对象。 - 与
Tag
和NavigableString
对象类似,Comment
对象也是文档树的一部分,可以通过Beautiful Soup进行搜索和访问。
【二】文档树操作
【1】基础方法
-
soup.tag.name
- 获取标签的名称
-
soup.tag.attrs
- 获取标签的所有属性,返回一个字典
- 没有属性就是None
-
soup.tag[attribute]
- 获取标签的指定属性
- 属性有多个值的返回一个列表
-
soup.tag.string
- 获取标签中的单个字符串的内容
- 如果标签内含有多个子节点(包括其他标签或字符串),则结果为None
-
soup.tag.strings
- 返回一个生成器
- 可以遍历标签及所有其子节点的字符串内容
-
soup.tag.text
- 获取标签及其所有子节点的文本内容
-
soup.tag.stripped_strings
- 返回一个生成器
- 可以遍历标签及其所有子节点的非空白字符串(空格换行都可以去掉)内容
-
soup.tag.tag.tag
- 可以嵌套选择
- 类似于链式操作
from bs4 import BeautifulSouphtml = """
<p class='btn btn-primary' id='b1'>
前面几个空格<b>后面有空格 </b>
前面有空格</p>
"""
soup = BeautifulSoup(html, 'lxml')# 获取标签的名称
print(soup.p.name)
# 获取标签所有属性
print(soup.p.attrs)
# 获取指定属性
print(soup.p['class'])
# 获取标签下单个字符串内容
print(soup.p.string) # 因为含有子标签,为None
print(soup.b.string)
# 获取标签和子节点的所有文本内容
print(soup.p.text)
# 获取标签下的所有文本内容,生成器
print(soup.p.strings)
for string in soup.p.strings:print(string)
# 去除空行和空格
print(soup.p.stripped_strings)
for string in soup.p.stripped_strings:print(string)
【2】遍历文档树
-
按照某种顺序(如深度优先或广度优先)访问文档树中的每个节点
-
soup.tag.contents
- 当前标签的直接 子节点列表(包括子标签和字符串内容)
-
soup.tag.children
- 当前标签的直接 子节点生成器
-
soup.tag.descendants
- 当前标签的所有 子节点生成器
- 是深度优先遍历,会识别换行和空格
-
soup.tag.parent
- 当前节点的父节点
-
soup.tag.parents
- 当前节点的所有祖先节点生成器
-
soup.tag.next_sibling
- 当前节点的上一个兄弟节点**(会识别到换行和空格)**
-
soup.tag.previous_sibling
- 当前节点的上一个兄弟节点**(会识别到换行和空格)**
-
soup.tag.next_siblings
- 当前节点之后的所有兄弟节点迭代器
-
soup.tag.previous_siblings
- 当前节点之前的所有兄弟节点迭代器
from bs4 import BeautifulSouphtml = """
<html lang="en">
<body><b>...</b><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="link">Elsie</a>,
and they lived at the bottom of a well.</p><p>...</p>
</body>
</html>
"""
soup = BeautifulSoup(html, 'lxml')# 直接子节点列表
print(soup.p.contents)
print(soup.p.children) # <list_iterator object at 0x0000016CFBEE9750>
# 父节点列表
print(soup.p.parent)
print(soup.p.parents)
# 兄弟节点
print(soup.p.next_sibling)
print(soup.p.previous_sibling)
# 所有兄弟节点
print(soup.p.next_siblings)
print(soup.p.previous_siblings)
【3】搜索文档树
- 根据某些条件(如标签、属性、文本内容等)在文档树中查找节点
(1)查找多个find_all
- 语法
find_all(name, attrs, recursive, limit, string, **kwargs)
-
name:
-
字符串
- 传入标签名,返回所有对应的标签
- 例如:
name='a'
拿到所有的a标签
-
正则表达式
- 根据正则表达式匹配标签
- 例如:
name=re.compile("^b")
拿到所有的b开头的标签
-
列表
- 匹配列表中的元素,满足一个的就可以
- 例如:name=[‘a’, ‘p’] 拿到所有的a标签和p标签
-
方法
-
自定义方法匹配元素
-
例如:匹配含有class属性且没有ID属性的标签
-
def has_class_but_no_id(tag):return tag.has_attr('class') and not tag.has_attr('id')soup.find_all(name=has_class_but_not_id)
-
-
True
- 返回所有的标签
- 不会返回的当前标签的字符串内容
- 默认就是True
-
-
attrs和**kwargs
-
根据属性进行查找,多个属性之间是与的关系
- 也可以使用正则
- class属性的特殊,一空格分割多个且需要写成class_
- 自定义属性可以使用attrs,也可以直接写
- arrts属性直接是与的关系
-
例如:
-
# 查找含有href属性,值是指定网址的标签 soup.find_all(href="http://example.com/") soup.find_all(attrs={'href': "http://example.com/"})
-
# 也可以使用正则 soup.find_all(href=re.compile("http://"))
-
# class属性的特殊,一空格分割多个且需要写成class_ # id属性就不能空格分隔多个 from bs4 import BeautifulSoup html = """ <p class="c1 c2" id='id 1' >...</p> """ soup = BeautifulSoup(html, 'lxml') print(soup.find_all(class_='c1', id='id 1'))
-
# 自定义属性可以使用attrs,也可以直接写 from bs4 import BeautifulSoup html = """ <p special='ss' >...</p> """ soup = BeautifulSoup(html, 'lxml') print(soup.find_all(special='ss')) print(soup.find_all(attrs={'special': 'ss'}))
-
# arrts属性直接是与的关系 from bs4 import BeautifulSoup html = """ <p ss='ss' aa='aa' >...</p> """ soup = BeautifulSoup(html, 'lxml') print(soup.find_all(attrs={'ss': 'ss', 'aa': 'aa'})) print(soup.find_all(attrs={'ss': 'ss', 'aa': 'a'}))
-
-
recursive
- 用于控制是否递归往下查询
- 默认为True,会遍历当前tag的所有子孙节点
- 想要直接子节点,可设置
recursive =Fasle
-
limit
- 限制返回的结果数量
- 达到一定数量直接停止,消耗资源较少
- 和Sql的limit一样
-
string
- 根据内容搜索标签
- 可以是字符串、列表、正则表达式等
- 和name属性一样
(2)查找单个find
- 和find_all()的参数一样
- 区别在于
- 数量
- find返回第一个满足条件的
- find_all返回所有满足条件
- find_all(limit=1)等价于find()
- 格式:如果为空
- find返回None
- find_all返回空列表
- 数量
(3)拓展
-
find_parents() 和 find_parent()
- find_parents():返回所有符合条件的父级tag,结果是一个生成器。
- find_parent():返回第一个符合条件的父级tag。
-
find_next_siblings() 和 find_next_sibling()
- find_next_siblings():返回所有符合条件的后续兄弟tag,结果是一个列表。
- find_next_sibling():返回第一个符合条件的后续兄弟tag。
-
find_all_next() 和 find_next()
- find_all_next():返回所有符合条件的后续tag和文本内容,结果是一个生成器。
- find_next():返回第一个符合条件的后续tag或文本内容。
【三】CSS选择器
-
官网:CSS 选择器参考手册 (w3school.com.cn)
-
使用方法:基本都是页面复制selector
- soup.select_one():返回查找的第一个
- soup.select()[0].attrs:获取属性
- soup.select()[0].get_text()
-
CSS 选择器
选择器 例子 例子描述 .class .intro 选择 class=“intro” 的所有元素。 .class1.class2 .name1.name2 选择 class 属性中同时有 name1 和 name2 的所有元素。 .class1 .class2 .name1 .name2 选择作为类名 name1 元素后代的所有类名 name2 元素。 #id #firstname 选择 id=“firstname” 的元素。 * * 选择所有元素。 element p 选择所有 元素。
element.class p.intro 选择 class=“intro” 的所有 元素。
element,element div, p 选择所有 元素和所有元素。
element element div p 选择 元素内的所有元素。
element>element div > p 选择父元素是 的所有元素。
element+element div + p 选择紧跟 元素的首个元素。
element1~element2 p ~ ul 选择前面有 元素的每个
- 元素。
[attribute] [target] 选择带有 target 属性的所有元素。 [attribute=value] [target=_blank] 选择带有 target=“_blank” 属性的所有元素。 [attribute~=value] [title~=flower] 选择 title 属性包含单词 “flower” 的所有元素。 [attribute|=value] [lang|=en] 选择 lang 属性值以 “en” 开头的所有元素。 [attribute^=value] a[href^=“https”] 选择其 src 属性值以 “https” 开头的每个 元素。 [attribute$=value] a[href$=“.pdf”] 选择其 src 属性以 “.pdf” 结尾的所有 元素。 [attribute*=value] a[href*=“w3school”] 选择其 href 属性值中包含 “abc” 子串的每个 元素。 :active a:active 选择活动链接。 ::after p::after 在每个 的内容之后插入内容。
::before p::before 在每个 的内容之前插入内容。
:checked input:checked 选择每个被选中的 元素。 :default input:default 选择默认的 元素。 :disabled input:disabled 选择每个被禁用的 元素。 :empty p:empty 选择没有子元素的每个 元素(包括文本节点)。
:enabled input:enabled 选择每个启用的 元素。 :first-child p:first-child 选择属于父元素的第一个子元素的每个 元素。
::first-letter p::first-letter 选择每个 元素的首字母。
::first-line p::first-line 选择每个 元素的首行。
:first-of-type p:first-of-type 选择属于其父元素的首个 元素的每个
元素。
:focus input:focus 选择获得焦点的 input 元素。 :fullscreen :fullscreen 选择处于全屏模式的元素。 :hover a:hover 选择鼠标指针位于其上的链接。 :in-range input:in-range 选择其值在指定范围内的 input 元素。 :indeterminate input:indeterminate 选择处于不确定状态的 input 元素。 :invalid input:invalid 选择具有无效值的所有 input 元素。 :lang(language) p:lang(it) 选择 lang 属性等于 “it”(意大利)的每个 元素。
:last-child p:last-child 选择属于其父元素最后一个子元素每个 元素。
:last-of-type p:last-of-type 选择属于其父元素的最后 元素的每个
元素。
:link a:link 选择所有未访问过的链接。 :not(selector) :not§ 选择非 元素的每个元素。
:nth-child(n) p:nth-child(2) 选择属于其父元素的第二个子元素的每个 元素。
:nth-last-child(n) p:nth-last-child(2) 同上,从最后一个子元素开始计数。 :nth-of-type(n) p:nth-of-type(2) 选择属于其父元素第二个 元素的每个
元素。
:nth-last-of-type(n) p:nth-last-of-type(2) 同上,但是从最后一个子元素开始计数。 :only-of-type p:only-of-type 选择属于其父元素唯一的 元素的每个
元素。
:only-child p:only-child 选择属于其父元素的唯一子元素的每个 元素。
:optional input:optional 选择不带 “required” 属性的 input 元素。 :out-of-range input:out-of-range 选择值超出指定范围的 input 元素。 ::placeholder input::placeholder 选择已规定 “placeholder” 属性的 input 元素。 :read-only input:read-only 选择已规定 “readonly” 属性的 input 元素。 :read-write input:read-write 选择未规定 “readonly” 属性的 input 元素。 :required input:required 选择已规定 “required” 属性的 input 元素。 :root :root 选择文档的根元素。 ::selection ::selection 选择用户已选取的元素部分。 :target #news:target 选择当前活动的 #news 元素。 :valid input:valid 选择带有有效值的所有 input 元素。 :visited a:visited 选择所有已访问的链接。