Python爬虫入门(#7)——BeautifulSoup(其一)


前面使用过BeautifulSoup来处理返回的html文档,这个库可以让我们不需要依赖正则而找到我们所需要的内容

基础内容

基本对象

首先要了解一下一些基础的属性和方法

BeautifulSoup将html解析为树形结构

from bs4 import BeautifulSoup
# 以文件形式解析html文档
soup = BeautifulSoup(open("filePath"))

# 以字符串形式解析html文档
soup = BeautifulSoup("<html>....<html/>")

解析完成后整个文档会被转换为树形结构,每一个节点都是一个对象
整个结构中有四种对象

  1. Tag
    Tag对象对应Html文档中的标签(tag)例如<p> <h1> <a>等,因此Tag对象也具有名称和属性,对应标签的名字和属性
    我们可以直接采用点操作符来取整个结构中的Tag对象,这种方法只会取找到的第一个符合要求的对象
soup = BeautifulSoup("<p id="p1" >This is a Tag <p/>")
# name指明了标签的名称
print(soup.p.name)
# 标签的属性则可以使用键值对的取法
print(soup.p['id'])
# 如果要取多个属性则可以使用attrs
print(soup.p.attrs)

如果同一个属性有多个值,则会返回列表

  1. NavigableString
    可遍历的字符串,包含在Tag内部的字符串,通常就是标签所包含的文本内容
    这部分不可编辑,但是可以使用replace_with()方法替换
print(soup.p.string)
# 替换文本内容
soup.p.string.replace_with("This is a  another tag")
print(soup.p.string)

在BeautifulSoup之外使用NavigableString对象时,最好unicode()转换

  1. BeautifulSoup
    BeautifulSoup指的是整个文档的全部内容,基本可以类似为Tag,但并不是真正的Tag,所以没有name和attrs
# 仅仅包含一个值为document的特殊name属性
print(soup.name)
  1. Comment
    上述对象几乎包含了所有的html的内容,但是有一些特殊的对象,比如文档的注释,他属于一种特殊的NavigableString类型
soup = BeautifulSoup("<b><!--Hey, buddy. Want to buy a used parser?--></b>")
comment = soup.b.string
type(comment)
基本方法
遍历文档树

子节点
一个Tag可能包含其他的Tag或者字符串,这都是这个Tag的子节点,下面我们就讨论一下遍历并操作这些子节点的方法

  1. 点取子节点
    最简单的方法就是使用name来取我们想要的子节点,例如获取某个标签只要使用这个标签的名字
# 获取title
print(soup.title)

这个方法可以多次调用,进而获取更加内层的子孙节点

print(soup.body.p)

这种方式只会获取当前名字的第一个Tag,如果想要获得所有的当前名字Tag,则要使用搜索文档树中的方法find_all(),后续再说

  1. contents和children取直接子节点
    Tag具有contents和children属性,也可用于获得子节点,但是只能获取其直接子节点
    contents:将Tag的子节点以列表的方式输出,返回子节点列表
    children:返回子节点列表的迭代器,可用于循环遍历
print(soup.body.contents)

for child in soup.body.children:
    print(child)
  1. descendants取子孙节点
    上述第二个方法只能取直接节点,这自然是不够方便的,如果我们想要遍历所有的子孙节点,就需要用到descendants属性
for child in soup.descentants:
	print(child)

这个方法可以遍历所有的子孙节点

  1. string取字符串节点
    如果这个Tag下只有一个string类型的节点,无论他在第几个或是第几层,string属性都能够唯一的取到这个节点,相反如果有多个string类型的子节点,则会返回None

  2. strings或stripped_strings取多个字符串节点
    strings可以获得所有字符串节点的列表
    stripped_strings则可以去除空格和空白的节点,在去除换行Tag时十分有用

父节点
文档树中的Tag或者字符串都有父节点

  1. parent取父节点
    BeautifulSoup的父节点是None
print(soup.parent)
print(soup.p.parent)
  1. parents递归所有父辈节点
    此属性可以递归遍历到根节点,返回迭代器

兄弟节点
同一层,同一个节点的子节点互称兄弟节点,具有相同的缩进级别

  1. next_sibling和previous_sibling查询兄弟节点

注意自动生成的换行等也会被认为是兄弟节点,例如<a>标签的下一个兄弟节点一定是换行符

  1. next_siblings和previous_siblings对兄弟节点迭代输出

回退和前进
重现解析器初始化过程的方法,解析器把文档当作字符串连续解析

  1. next_element和previous_element获得解析顺序前后的节点
    这两个方法会分别按照解析顺序取得之后和之前解析的节点,因为解析按照字符串顺序解析,因此这个方法获得的节点和兄弟节点可能不同
  2. next_elements和previous_elements获得解析前后的迭代器
搜索文档树

主要介绍两个搜索方法:find()find_all()
要使用这两个方法我们就需要了解他们的过滤器参数

  1. 字符串
    可以传入一个字符串作为过滤参数,搜索时会查找完全匹配字符串的内容
  2. 正则表达式
    也可以使用正则表达式,会使用search方法来查找正则匹配的内容
  3. 列表
    传入列表参数,则会查找与列表中任意一项匹配的内容
  4. True
    返回所有的Tag,但是不会返回字符串节点
  5. 方法
    如果没有合适的过滤器,也可以定义一个方法,这个方法接受一个参数,当方法返回True时表示匹配成功,否则返回False

find_all:
find_all(name, attrs, recursive, string, **kwargs)

搜索当前Tag的所有tag子节点并判断是否符合过滤条件

  1. name:可以查找所有名字为name的Tag,字符串对象自动忽略
  2. keword:对于指定参数名传入的参数,如果参数名不是内置的参数名,则会作为指定名字的tag的属性来搜索,例如
soup.find_all(id="1")
# 指定的参数名id并不是内置的参数名(name, attrs, recursive, string)
# 所以这个参数名会被当做tag的属性来搜索,所以这个方法会搜索所有的tag的id属性

指定参数名传入的参数可以是字符串,正则,列表,True

True表示所有包含该参数名的tag

也可以使用多个指定的名字的参数来过滤多个属性,对于h5的某些属性也可能不支持搜索,这时可以使用attrs参数来指定一个包含这些属性的字典来解决
3. css:可以按照css类名来搜索tag,但是class是保留字,因此使用class_作为参数名

class_ 接受字符串,正则,方法,True

因为类名是多值属性,一个tag可能指定了多个css类,因此匹配时可以仅仅匹配其中的某一个类,也可以用空格分隔多个类名进行完全匹配,完全匹配时顺序敏感

css_soup = BeautifulSoup('<p class="body strikeout"></p>')
css_soup.find_all("p", class_="strikeout")
# [<p class="body strikeout"></p>]

css_soup.find_all("p", class_="body")
# [<p class="body strikeout"></p>]

css_soup.find_all("p", class_="body strikeout")
# [<p class="body strikeout"></p>]
  1. string:string可以搜索文档中的字符串内容,接受字符串,正则,列表,True
    当string和搜索tag的参数一同使用时,会转而搜索string()方法的值与传入的string值相同的tag
  2. limit:find_all()方法返回搜索到的全部内容,因此有可能因为文档树过大而导致执行缓慢,如果不需要全部搜索结果,则可以使用limit参数限制返回的结果数量
soup.find_all("a", limit=2)
  1. recursive参数
    find_all()方法会搜索当前tag的所有子孙节点,如果只想搜索直接子节点,则可以用recursive=False来实现

find_all()方法有简写形式,即省略方法名
soup("a")soup.find_all("a")是等价的

find:
find_all(name, attrs, recursive, string, **kwargs)
和find_all()唯一的区别就是find()仅返回所搜到的第一个结果,而find_all()返回列表,同样搜索失败时,find()返回None,find_all()返回空列表

对于其他的方法,其参数和区别与上述两个方法类似,同时看名字也能明白其作用,不做赘述

find_parents() 和 find_parent()
搜索当前节点的父辈节点
find_next_siblings() 和 find_next_sibling()
返回所有符合条件的后面的兄弟节点,只返回符合条件的后面的第一个tag节点
find_previous_siblings() 和 find_previous_sibling()
返回所有符合条件的前面的兄弟节点,返回第一个符合条件的前面的兄弟节点:
find_all_next() 和 find_next()
返回之后所有符合条件的节点,返回之前第一个符合条件的节点
find_all_previous() 和 find_previous()
返回之前所有符合条件的节点,返回之前第一个符合条件的节点

css选择器
这是另外一个用于搜索文档树的重要方法
使用select()方法可以用css选择器的语法来找到tag,具体的语法需要另外学习,但是确实相当方便

其实一般情况下可以在浏览器控制台直接获得css选择器字符串,这个我在之前的文章中提到过

爬虫入门#2这篇

修改文档树
  1. 修改Tag的名称和属性
tag.name = "another name"
tag['class'] = 'another class'

del tag['class']
del tag['id']
  1. 修改string
    赋值即为修改

  2. append()
    向Tag中添加内容,追加到contents的最后

  3. NavigableString()和new_tag()
    添加一段文本内容到文档中也可以使用NavigableString的构造方法

soup = BeautifulSoup("<b></b>")
tag = soup.b
tag.append("Hello")
new_string = NavigableString(" there")
tag.append(new_string)
tag
# <b>Hello there.</b>
tag.contents
# [u'Hello', u' there']

还可以用来添加注释

new_comment = soup.new_string("Nice to see you.", Comment)
tag.append(new_comment)
tag
# <b>Hello there<!--Nice to see you.--></b>
tag.contents
# [u'Hello', u' there', u'Nice to see you.']

创建一个Tag使用BeautifulSoup.new_tag()

soup = BeautifulSoup("<b></b>")
original_tag = soup.b

new_tag = soup.new_tag("a", href="http://www.example.com")
original_tag.append(new_tag)
original_tag
# <b><a href="http://www.example.com"></a></b>

new_tag.string = "Link text."
original_tag
# <b><a href="http://www.example.com">Link text.</a></b>
  1. insert()
    这个方法与append类似,区别就是可以指定插入的位置
markup = '<a href="http://example.com/">I linked to <i>example.com</i></a>'
soup = BeautifulSoup(markup)
tag = soup.a

tag.insert(1, "but did not endorse ")
tag
# <a href="http://example.com/">I linked to but did not endorse <i>example.com</i></a>
tag.contents
# [u'I linked to ', u'but did not endorse', <i>example.com</i>]
  1. insert_before() 和 insert_after()
    与上一个方法不同,上一个insert是在当前tag内部指定位置插入子节点,而这个方法则是在当前tag的前后插入同级别节点

  2. clear()
    移除当前tag的内容(全部子节点)

  3. extract()
    将当前tag移除文档树,并作为结果返回(删除当前节点并返回)

  4. decompose()
    将当前tag移除文档树并销毁

  5. replace_with()
    移除某段内容(当前节点)并用新tag或者文本节点代替

  6. warp()
    对指定元素进行包装并返回包装的结果

soup = BeautifulSoup("<p>I wish I was bold.</p>")
soup.p.string.wrap(soup.new_tag("b"))
# <b>I wish I was bold.</b>

soup.p.wrap(soup.new_tag("div"))
# <div><p><b>I wish I was bold.</b></p></div>
  1. unwarp()
    移除tag内的所有的tag标签,解包装
markup = '<a href="http://example.com/">I linked to <i>example.com</i></a>'
soup = BeautifulSoup(markup)
a_tag = soup.a

a_tag.i.unwrap()
a_tag
# <a href="http://example.com/">I linked to example.com</a>
输出
  1. 格式化输出
    prettify()将文档树格式化后输出,每个标签占一行,BeautifulSoup和Tag均可调用

  2. 压缩输出
    仅仅得到文档的字符串,而不需要注重格式,可以直接将文档树对象字符串化

str(soup)
unicode(soup)
  1. 输出格式
    略…没咋看明白,也不是很重要
  2. get_text()
    只想得到tag包含的文本,可以调用这个方法来获取所有子孙节点的unicode格式字符串
    可以指定分割字符串get_text("\")
    可以指定去除空白get_text(strip=True)
    可以获得列表形式的文本内容soup.stripped_strings
指定文档解析器

不同的解析器对于不标准的html文档有不同的解析结果,但是对于标准的文档解析结构都是一样,常用的有lxml,html5lib,html.parser通常使用lxml的居多,现在对于这个无需做过多了解

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值