python网络爬虫——Beautiful Soup的使用

什么是Beautiful Soup?
是一个可以从HTML或XML文件中提取数据的Python库,它能够通过你喜欢的转换器实现惯用的文档导航,查找,修改文档的方式。
安装

pip install beautifulsoup4

解析器
Beautiful Soup支持Python标准库中的HTML解析器,还支持一些第三方的解析器,比如lxml。

pip install lxml

如何使用
将一段文档传入Beautiful Soup的构造方法,就能得到一个文档的对象,可以传入一个字符串或一个文件句柄。

>>> BeautifulSoup("Sacrébleu!")
Sacr\xe9bleu!

首先文档被转换成Unicode,并且HTML的实例都被转换成Unicode编码,然后Beautiful Soup选择最合适的解析器来分析这段文档。

对象的种类

####Tag
Tag对象与XML和HTNL原生文当中的相同:

>>> soup=BeautifulSoup('<b class="boldest">Extremely bold</b>')
>>> type(soup.b)
<class 'bs4.element.Tag'>

Tag对象的属性:name和attributes

  • Name
>>> tag=soup.b
>>> tag.name
u'b'
#如果改变tag的名字,将会影响通过当前BeautifulSoup对象生成的HTML文档
>>> tag.name='blockquote'
>>> tag
<blockquote class="boldest">Extremely bold</blockquote>

Attributes
tag的属性的操作方法与字典相同:

>>> tag['class']
[u'boldest']
>>> tag.attrs
{u'class': [u'boldest']}

tag的属性可以被添加,删除或修改。

#修改添加属性
>>> tag['class']='verybold'
>>> tag['id']=1
>>> tag
<blockquote class="verybold" id="1">Extremely bold</blockquote>

#删除属性
>>> del tag['class']
>>> del tag['id']
>>> tag
<blockquote>Extremely bold</blockquote>
>>> tag['class']
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/home/kiosk/.local/lib/python2.7/site-packages/bs4/element.py", line 1071, in __getitem__
    return self.attrs[key]
KeyError: 'class'

多值属性
有时候一个属性可能对应多个值

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

如果某个属性看起来有多个值,但任何版本的HTML定义中都没有定义它为对值属性,那么将会返回一个字符串。

#id是唯一的,不会有多个id
>>> id_soup=BeautifulSoup('<p id="my id"></p>')
>>> id_soup.p['id']
u'my id'

将tag转换成字符串时,多值属性会合并为一个值

>>> from bs4 import BeautifulSoup
>>> rel_soup=BeautifulSoup('<p>Back to the <a rel="index">homepage</a></p>')
>>> rel_soup.a['rel']

>>> rel_soup.a['rel']=['index','context']
>>> rel_soup.p
<p>Back to the <a rel="index context">homepage</a></p>

遍历字符串
字符串常被包含在tag内,Beautiful Soup用NavigableString类来包装tag中的字符串:

>>> soup=BeautifulSoup('<b class="boldest">Extremely bold</b>')
>>> tag=soup.b
>>> tag.string
u'Extremely bold'
>>> type(tag.string)
<class 'bs4.element.NavigableString'>

一个NavigableString字符串与Python中的Unicode字符串相同,通过 unicode() 方法可以直接将 NavigableString 对象转换成Unicode字符串。
tag中包含的字符串不能编辑,但是可以使用replace_with()方法替换:

>>> tag.string.replace_with("No longer bold")
u'Extremely bold'
>>> tag
<b class="boldest">No longer bold</b>

####BeautifulSoup
BeautifulSoup对象表示的时一个文档的全部内容。大部分时候可以把它当作Tag对象。
Tag , NavigableString , BeautifulSoup几乎覆盖了htnl和xml中的所有内容,但还有一些特殊对象,比如文当中的注释
注释以及特殊字符串


>>> markup="<b><!--Hello World--></b>"
>>> soup=BeautifulSoup(markup)
>>> comment=soup.b.string
>>> comment
u'Hello World'
>>> type(comment)
#Comment对象时一个特殊类型的NavigableString 对象
<class 'bs4.element.Comment'>

用CDATA来替换注释

>>> from bs4 import CData
>>> cdata=CData("Hello World")
>>> comment.replace_with(cdata)
u'Hello World'
>>> print(soup.b.prettify())
<b>
 <![CDATA[Hello World]]>
</b>

遍历文档树

子节点
一个Tag可能包含多个字符串或其他Tag,这些都是这个Tag的子节点。

  • tag的名字
html_doc="""
    <html><head><title>The Dormouse's story</title></head>
<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>;
and they lived at the bottom of a well.</p>
<p class="story">...</p>
"""

from bs4 import BeautifulSoup

#指定解析器lxml,默认为HTML
soup=BeautifulSoup(html_doc,'lxml')

print soup

#解析后
<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 class="sister" href="http://example.com/elsie" id="link1">Elsie</a>,
<a class="sister" href="http://example.com/lacie" id="link2">Lacie</a> and
<a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>;
and they lived at the bottom of a well.</p>
<p class="story">...</p>
</body></html>



print soup.head
# <head><title>The Dormouse's story</title></head>

print soup.title
# <title>The Dormouse's story</title>

#获得<p>标签中的一个<b>标签
print soup.p.b
#<b>The Dormouse's story</b>

通过点取属性的方式只能获得当前名字的第一个tag;如果想获得所有的,则用find_all().返回一个列表。

#获得所有的<a>标签
print soup.find_all('a')
# [<a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>,
#  <a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>, 
# <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>]

.contents和.children
.contents可以将子节点以列表形式输出,如果没有子节点就没有这个属性;
.children子节点变成生成器,可以for遍历;
.descendants
可以对所有tag的子孙节点进行递归循环;
.string
如果只有一个子节点,可以获得子节点。如果有多个,则输出None
.stripped_strings
可以去除多余空白内容
父节点
.parent获取父节点

title_tag=soup.title
print title_tag
print title_tag.parent
# <title>The Dormouse's story</title>
# <head><title>The Dormouse's story</title></head>

BeautifulSoup 对象的 .parent 是None:
.parents
可以递归得到元素的所有父辈节点。

link=soup.a
print link

for parent in link.parents:
    if parent is None:
        print  parent
    else:
        print parent.name
        
        
"""
<a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>
p
html
[document]
"""

兄弟节点
如果两个标签处于同一层,成为兄弟节点
.next_sibling 和 .previous_sibling
.next_siblings 和 .previous_siblings
对当前所有的兄弟节点迭代输出。
回退和前进
.next_element 和 .previous_element
next_element 属性指向解析过程中下一个被解析的对象。

last_a_tag=soup.find('a',id='link3')
print last_a_tag
print last_a_tag.next_sibling
print last_a_tag.next_element

"""
#.next_sibling和.next_element的区别
<a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>
#与<a>处于同层的下一个要被解析的
;
and they lived at the bottom of a well.
#处于<a>内层的
Tillie

"""

.next_elements 和 .previous_elements
可以向前或向后访问文档的解析内容

搜索文档树

过滤器

  • 字符串:传入一个字符串,查找与字符串完整匹配的内容。
print soup.find_all('b')
# [<b>The Dormouse's story</b>]
  • 正则表达式
    找出所有以b开头的标签
import re

for tag in soup.find_all(re.compile("^b")):
    print tag.name
    
# b
  • 列表
    如果传入列表参数,将与列表中的任意元素匹配的内容返回;
print soup.find_all(['a','b'])
"""
[<b>The Dormouse's story</b>,
 <a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>,
  <a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>, 
  <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>]

"""
  • True
    匹配任何值,但不会返回字符串节点
for tag in soup.find_all(True):
    print tag.name

"""
html
head
title
p
b
p
a
a
a
p
"""

方法
包含class属性不包含id属性

这里写代码片

find_all方法祥解

find_all(self, name=None, attrs={}, recursive=True, text=None,
                 limit=None, **kwargs):

搜索当前tag的所有tag子节点,并判断是否符合过滤器的条件;
name参数
可以查找名字为name的tag,字符串对象会被自动忽略

print soup.find_all("title")
# [<title>The Dormouse's story</title>]

搜索name参数的值可以是任意类型的过滤器,字符串,正则,列表,方法或者True;
keyword参数
如果一个指定名字的参数你是搜索内置的参数名,搜索时会把该参数当作指定名tag的属性来搜索;

print soup.find_all(id='link2')
# [<a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>]

print soup.find_all(href=re.compile("elsie"))
# [<a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>]

搜索指定名字的属性时可以是字符串,正则,列表,True
有些属性在搜索不能使用:

data_soup=BeautifulSoup('<div data-foo="value">foo!</div>')
print data_soup.find_all(data_foo="value")
# []
#可以通过attrs参数定义一个字典参数来搜索
print data_soup.find_all(attrs={"data-foo":"value"})
# [<div data-foo="value">foo!</div>]

按CSS搜索:
使用class_参数来指定,而不是class;

print soup.find_all("a",class_="sister")
# [<a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>,
#  <a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>,
#  <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>]


#class_参数同样接受字符串,过滤器等
print soup.find_all(class_=re.compile("itl"))
# [<p class="title"><b>The Dormouse's story</b></p>]

def has_six_class(css_class):
    return css_class is not None and len(css_class)==6

print soup.find_all(class_=has_six_class)
# [<a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>,
#  <a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>,
#  <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>]

如果class是多值的也可以指定任意一个值都能搜索到,也可以通过多个值匹配到.但是多值搜索如果顺序不符将搜索不到;
text参数
搜索文当中的字符串内容.还可以与其他参数混合使用:

print soup.find_all("a",text="Elsie")
# [<a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>]

limit参数
指定搜索返回的数量

print soup.find_all('a')
# [<a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>,
#  <a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>, 
# <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>]


print soup.find_all('a',limit=2)
# [<a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>, 
# <a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>]

recursive参数
调用tag的find_all方法时.会检索当前的所有子孙节点,如果只想搜索tag的直接子节点,可以将recursive=False

定义find_all的简便方法

soup.find_all("a")
soup("a")

find()方法
find只返回一个结果.find_all以列表的形式返回所有结果
find_parents() 和 find_parent()
搜索当前节点的父辈节点

a_string=soup.find(text="Lacie")
print a_string
# Lacie

#当前叶子节点的直接父节点
print a_string.find_parents("a")
# [<a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>]



#当前叶子的间接父节点
print a_string.find_parents("p")
# [<p class="story">Once upon a time there were three little sisters; and their names were\n
# <a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>,\n
# <a class="sister" href="http://example.com/lacie" id="link2">Lacie</a> and\n
# <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>;\n
# and they lived at the bottom of a well.</p>]

find_next_siblings() 合 find_next_sibling()
对后面解析的兄弟tag进行迭代,只返回单符合条件的第一个兄弟节点
find_previous_siblings() 和 find_previous_sibling()
和上面的一样,匹配当前节点的前面的节点
find_all_next() 和 find_next()
对当前tag的之后的tag和字符串进行迭代
find_all_previous() 和 find_previous()
对当前tag的之前的tag和字符串进行迭代

修改文档树

修改tag的名称和属性
获取到以后重新赋值,跟修改字典的value值一样
修改.string

markup='<a href="http://example.com/">I linked to <i>example.com</i></a>'
soup=BeautifulSoup(markup)

tag=soup.a
tag.string="New link text"
print tag

# <a href="http://example.com/">New link text</a>

如果当前tag包含其他的tag,那么.string属性会覆盖掉原有的所有内容.包括tag
append()

soup=BeautifulSoup("<a>Foo</a>")
soup.a.append("Bar")

print soup
# <a>FooBar</a>

print soup.a.contents
# [u'Foo', u'Bar']

BeautifulSoup.new_string() 和 .new_tag()
添加一段文本内容到文档

soup=BeautifulSoup("<b></b>")
tag=soup.b
tag.append("Hello")
new_string=soup.new_string("there")
tag.append(new_string)

print tag
print tag.contents

# <b>Hellothere</b>
# [u'Hello', u'there']

如果创建一段注释,或者 NavigableString 的任何子类,姜子类作为new_string方法的第二个参数传入:

soup=BeautifulSoup("<b></b>")
tag=soup.b
tag.append("Hello")
new_string=soup.new_string("there")
tag.append(new_string)
new_comment=soup.new_string("Nice to see you",Comment)
tag.append(new_comment)

print tag
print tag.contents

# <b>Hellothere<!--Nice to see you--></b>
# [u'Hello', u'there', u'Nice to see you']

创建一个tag最好的方法是调用工厂方法 BeautifulSoup.new_tag() :

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


new_tag=soup.new_tag("a",href="http://www.example.com")
first_tag.append(new_tag)
print first_tag

new_tag.string="Link text"
print first_tag

# <b><a href="http://www.example.com"></a></b>
# <b><a href="http://www.example.com">Link text</a></b>

insert()
将元素插入到指定位置

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")

print tag
print tag.contents

# <a href="http://example.com/">I linked to but did not endorse<i>example.com</i></a>
# [u'I linked to ', u'but did not endorse', <i>example.com</i>]

insert_before() 和 insert_after()
insert_before() 方法在当前tag或文本节点前插入内容

soup=BeautifulSoup("<b>stop</b>")
tag=soup.new_tag("i")
tag.string="Don't"
soup.b.string.insert_before(tag)

print soup.b
# <b><i>Don't</i>stop</b>

insert_before() 方法在当前tag或文本节点后插入内容

soup.b.i.insert_after(soup.new_string("ever"))
print soup.b
# <b><i>Don't</i>everstop</b>

clear()
移除当前tag的内容
extract()
移除当前tag的内容,并作为结果返回
decompose()
将当前节点移除文档树并完全销毁
replace_with()
替换文档树中的某段内容
wrap()
对指定的tag元素进行包装,并返回包装后的借果;

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

# <b>I wish I was bold.</b>

print soup.p.wrap(soup.new_tag("div"))
# <div><p><b>I wish I was bold.</b></p></div>

unwrap()
移除tag内的所有tag标签.

CSS选择器

Beautiful Soup支持大部分的选择器,在Tag或者Beautiful Soup对象的.select()方法中传入字符串参数,即可使用CSS选择器的语法找到tag:
通过标签选择

#选择title标签
print soup.select("title")
# [<title>The Dormouse's story</title>]

#在所有p标签中,选择第三个标签
print soup.select("p:nth-of-type(3)")
# [<p class="story">...</p>]


#选择body 下的a标签,全部显示
print soup.select('body a')
# [<a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>, 
# <a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>, 
# <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>]


#选择id=link1后的所有兄弟节点标签
print soup.select("#link1 ~ .sister")
# [<a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>,
#  <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>]


#选择id=link1后的下一个兄弟节点标签
print soup.select("#link1 + .sister")
# [<a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>]

通过类名查找

#选择a标签,类属性为sister的标签
print soup.select('a.sister')
# [<a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>,
#  <a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>,
#  <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>]

通过id查找

#选择a标签id为link1的标签
print soup.select('a#link1')
# [<a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>]

通过属性查找

#选择a标签,其属性中存在id的所有标签
print soup.select("a[id]")
# [<a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>,
#  <a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>,
# <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>]

#选择a标签,其属性href="http://example.com/lacie"的所有标签
print soup.select('a[href="http://example.com/lacie"]')
# [<a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>]


#选择a标签,其属性href以http开头的
print soup.select('a[href^="http"]')
# [<a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>,
# <a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>,
# <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>]

#选择a标签,其属性href以lacie结尾的
print soup.select('a[href$="lacie"]')
# [<a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>]

#选择a标签,其属性href包含tillie的
print soup.select('a[href*="tillie"]')
# [<a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>]

#从html中排除某标签,此时soup中不再有这个标签
[s.extract() for s in soup('a')]
print soup.select('a')
# []

#排除多个
[s.extract() for s in soup('a','p')]

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值