python开发bs程序_python 全栈开发,Day135(爬虫系列之第2章-BS和Xpath模块)

The Dormouse's story

Once upon a time there were three little sisters; and their names were

Elsie,

Lacie and

Tillie;

and they lived at the bottom of a well.

...

"""

上面这段代码不是完整的html标签,为什么呢?它没有body和html的结束标签!

没关系,bs4有容错机制!

使用BeautifulSoup解析这段代码,能够得到一个 BeautifulSoup 的对象

from bs4 importBeautifulSoup

soup= BeautifulSoup(html_doc, 'html.parser')

从文档中找到所有标签的链接:

for link in soup.find_all('a'):print(link.get('href'))

执行输出:

ContractedBlock.gif

ExpandedBlockStart.gif

http://example.com/elsie

http://example.com/lacie

http://example.com/tillie

View Code

从文档中获取所有文字内容:

print(soup.get_text())

执行输出:

ContractedBlock.gif

ExpandedBlockStart.gif

The Dormouse's story

The Dormouse's story

Once upon a time there were three little sisters; andtheir names were

Elsie,

LacieandTillie;andthey lived at the bottom of a well.

...

View Code

2. 标签对象

通俗点讲就是 HTML 中的一个个标签,Tag 对象与XML或HTML原生文档中的tag相同:

from bs4 importBeautifulSoup

soup= BeautifulSoup('Extremely bold','html.parser')

tag=soup.bprint(type(tag))

执行输出:

Tag的名字

soup对象再以爱丽丝梦游仙境的html_doc为例,操作文档树最简单的方法就是告诉它你想获取的tag的name.如果想获取

标签,只要用 soup.head :

from bs4 importBeautifulSoup

soup= BeautifulSoup(html_doc, 'html.parser')

soup= soup.head #获取head标签

print(soup)

soup= soup.title #获取title标签

print(soup)

执行输出:

ContractedBlock.gif

ExpandedBlockStart.gif

The Dormouse's story

The Dormouse's story

View Code

这是个获取tag的小窍门,可以在文档树的tag中多次调用这个方法.下面的代码可以获取

标签中的第一个 标签:

soup = soup.body.b #

标签中的第一个 标签

print(soup) #The Dormouse's story

通过点取属性的方式只能获得当前名字的第一个tag:

soup = soup.a #第一个a标签

print(soup)#Elsie

如果想要得到所有的标签,或是通过名字得到比一个tag更多的内容的时候,就需要用到 Searching the tree 中描述的方法,比如: find_all()

soup = soup.find_all('a') #所有a标签

print(soup)

执行输出:

ContractedBlock.gif

ExpandedBlockStart.gif

[Elsie,Lacie,Tillie]

View Code

结果是一个列表!

我们可以利用 soup加标签名轻松地获取这些标签的内容,注意,它查找的是在所有内容中的第一个符合要求的标签。

Tag的name和attributes属性

Tag有很多方法和属性,主要是 标签对象的名称,属性,文本。

现在介绍一下tag中最重要的属性: name和attributes

名称和属性

每个tag都有自己的名字,通过.name 来获取:

from bs4 importBeautifulSoup

soup= BeautifulSoup(html_doc, 'html.parser')

soup= soup.find_all('a') #所有a标签

for link insoup:print(link.name) # 标签名

print(link.get('id')) # id属性

print(link.get('class')) # class属性

print(link.get('href')) # herf属性

print(link.attrs) # 标签所有属性

#{'href': 'http://example.com/tillie', 'class': ['sister'], 'id': 'link3'}

tag的属性可以被添加,删除或修改. 再说一次, tag的属性操作方法与字典一样

修改

from bs4 importBeautifulSoup

soup= BeautifulSoup(html_doc, 'html.parser')

soup= soup.find_all('a') #所有a标签

soup[0]['class'] = 'verybold' # 修改class属性

soup[0]['id'] = 1 # 修改id属性

print(soup[0])#Elsie

删除

soup = soup.find_all('a') #所有a标签

del soup[0]['class']

del soup[0]['id']print(soup[0])#Elsie

文本获取

标签对象的文本获取

from bs4 importBeautifulSoup

soup= BeautifulSoup(html_doc, 'html.parser')print(soup.p.string) # p下的文本只有一个时,取到,否则为None

print(soup.p.strings) # 拿到一个生成器对象, 取到p下所有的文本内容

print(soup.p.text) #取到p下所有的文本内容

for line in soup.stripped_strings: #去掉空白

print(line)

执行输出:

ContractedBlock.gif

ExpandedBlockStart.gif

The Dormouse's story

The Dormouse's story

The Dormouse's story

The Dormouse's story

Once upon a time there were three little sisters; andtheir names were

Elsie

,

LacieandTillie

;andthey lived at the bottom of a well.

...

View Code

如果tag包含了多个子节点,tag就无法确定 .string 方法应该调用哪个子节点的内容, .string 的输出结果是 None,如果只有一个子节点那么就输出该子节点的文本,比如下面的这种结构,soup.p.string 返回为None,但soup.p.strings就可以找到所有文本

from bs4 importBeautifulSoup

tmp_html= """

哈哈哈哈

aaaa

bbbbb

"""

soup = BeautifulSoup(tmp_html, 'html.parser')

print(soup.p.string) # p下的文本只有一个时,取到,否则为None

print(soup.p.strings) # 拿到一个生成器对象, 取到p下所有的文本内容

for i in soup.stripped_strings:

print('-->',i)

执行输出:

None

-->哈哈哈哈-->aaaa--> bbbbb

因此,使用 string,标签下的文本只能有一个,否则返回None!

而使用text,返回所有文本!

这就是tag.text和tag.string的区别!

3.遍历文档树

关于DOM数,请参考链接:

1、嵌套选择

from bs4 importBeautifulSoup

soup= BeautifulSoup(html_doc, 'html.parser')print(soup.head.title.string) #The Dormouse's story

print(soup.body.a.string) #Elsie

2、子节点、子孙节点

from bs4 importBeautifulSoup

soup= BeautifulSoup(html_doc, 'html.parser')print(soup.p.contents) #p下所有子节点

print(soup.p.children) #得到一个迭代器,包含p下所有子节点

for i,child in enumerate(soup.p.children):

print(i,child)

print(soup.p.descendants) #获取子孙节点,p下所有的标签都会选择出来

for i,child in enumerate(soup.p.descendants):

print(i,child)

3、父节点、祖先节点

print(soup.a.parent) #获取a标签的父节点

print(soup.a.parents) #找到a标签所有的祖先节点,父亲的父亲,父亲的父亲的父亲...

soup.a.parents 最终返回的节点是...。也就是说,它直接回到顶层了!

4、兄弟节点

print('=====>')print(soup.a.next_sibling) #下一个兄弟

print(soup.a.previous_sibling) #上一个兄弟

print(list(soup.a.next_siblings)) #下面的兄弟们=>生成器对象

print(soup.a.previous_siblings) #上面的兄弟们=>生成器对象

4. 搜索文档树

BeautifulSoup定义了很多搜索方法,这里着重介绍2个: find() 和 find_all() .其它方法的参数和用法类似

1、五种过滤器

搜索文档树:BeautifulSoup定义了很多搜索方法,这里着重介绍2个: find() 和 find_all() .其它方法的参数和用法类似

html_doc = """

The Dormouse's story

The Dormouse's story

Once upon a time there were three little sisters; and their names were

Elsie,

Lacie and

Tillie;

and they lived at the bottom of a well.

...

"""

from bs4 importBeautifulSoup

soup=BeautifulSoup(html_doc,'lxml')

五种过滤器: 字符串、正则表达式、列表、True、方法

1.1、字符串:即标签名

print(soup.find_all('b'))

执行输出:

[The Dormouse's story]

1.2、正则表达式

importreprint(soup.find_all(re.compile('^b'))) #找出b开头的标签,结果有body和b标签

执行输出:

ContractedBlock.gif

ExpandedBlockStart.gif

[

The Dormouse's story

Once upon a time there were three little sisters; andtheir names wereElsie,Lacie and

Tillie;and they lived at the bottom of a well.

...

, The Dormouse's story]

View Code

1.3、列表

如果传入列表参数,Beautiful Soup会将与列表中任一元素匹配的内容返回.下面代码找到文档中所有标签和标签:

print(soup.find_all(['a','b']))

执行输出:

ContractedBlock.gif

ExpandedBlockStart.gif

[The Dormouse's story, Elsie, Lacie, Tillie]

View Code

1.4、True

可以匹配任何值,下面代码查找到所有的tag,但是不会返回字符串节点

print(soup.find_all(True))for tag insoup.find_all(True):print(tag.name)

这个不怎么用,可以忽略掉!

1.5、方法

如果没有合适过滤器,那么还可以定义一个方法,方法只接受一个元素参数 ,如果这个方法返回 True 表示当前元素匹配并且被找到,如果不是则反回 False

from bs4 importBeautifulSoup

soup=BeautifulSoup(html_doc,'lxml')def has_class_but_no_id(tag): # 有class但是没有id属性的标签

return tag.has_attr('class') and not tag.has_attr('id')

print(soup.find_all(has_class_but_no_id)) # 将每一个标签传给这个方法过滤

执行输出:

ContractedBlock.gif

ExpandedBlockStart.gif

[

Once upon a time there were three little sisters; andtheir names wereElsie,Lacie and

Tillie;and they lived at the bottom of a well.

,

...

]

View Code

最终输出的,都是有class属性,但是没有id属性的标签

2、find_all()

语法

find_all( name , attrs , recursive , text , **kwargs )

2.1、name

搜索name参数的值可以使任一类型的 过滤器 ,字符窜,正则表达式,列表,方法或是 True

from bs4 importBeautifulSoup

soup=BeautifulSoup(html_doc,'lxml')import re

print(soup.find_all(name=re.compile('^t')))

执行输出:

[

The Dormouse's story]

2.2、keyword

key=value的形式,value可以是过滤器:字符串 , 正则表达式 , 列表, True

print(soup.find_all(id=re.compile('my'))) #查找id含有my属性的标签#查找herf含有lacle以及id带有数字的标签。#如果是class属性,需要使用class_,因为在python中,class是关键字!

print(soup.find_all(href=re.compile('lacie'),id=re.compile('\d')))print(soup.find_all(id=True)) #查找有id属性的标签

执行输出:

ContractedBlock.gif

ExpandedBlockStart.gif

[

The Dormouse's story

]

[Lacie]

[

The Dormouse's story

, The Dormouse's story, Elsie, Lacie, Tillie]

View Code

有些tag属性在搜索不能使用,比如HTML5中的 data-* 属性:

data_soup = BeautifulSoup('

foo!
','lxml')

data_soup.find_all(data-foo="value") #报错:SyntaxError: keyword can't be an expression

但是可以通过 find_all() 方法的 attrs 参数定义一个字典参数来搜索包含特殊属性的tag:

from bs4 importBeautifulSoup

soup=BeautifulSoup(html_doc,'lxml')

data_soup= BeautifulSoup('

foo!
','lxml')print(data_soup.find_all(attrs={"data-foo": "value"}))#[
foo!
]

2.3、按照类名查找

注意关键字是class_,class_=value,value可以是五种选择器之一

from bs4 importBeautifulSoup

soup=BeautifulSoup(html_doc,'lxml')importreprint(soup.find_all('a',class_='sister')) #查找类为sister的a标签

print(soup.find_all('a',class_='sister ssss')) #查找类为sister和sss的a标签,顺序错误也匹配不成功

print(soup.find_all(class_=re.compile('^sis'))) #查找类为sister的所有标签

执行输出:

ContractedBlock.gif

ExpandedBlockStart.gif

[Elsie, Lacie, Tillie]

[]

[Elsie, Lacie, Tillie]

View Code

2.4、attrs

如果有多个属性,在attrs里面增加key-value即可

#查询p标签,class属性为story

print(soup.find_all('p',attrs={'class':'story'}))

执行输出:

ContractedBlock.gif

ExpandedBlockStart.gif

[

Once upon a time there were three little sisters; andtheir names wereElsie,Lacie and

Tillie;and they lived at the bottom of a well.

,

...

]

View Code

2.5、text

值可以是:字符,列表,True,正则

print(soup.find_all(text='Elsie'))print(soup.find_all('a',text='Elsie'))

执行输出:

ContractedBlock.gif

ExpandedBlockStart.gif

['Elsie']

[Elsie]

View Code

2.6、limit参数

如果文档树很大那么搜索会很慢.如果我们不需要全部结果,可以使用 limit 参数限制返回结果的数量.效果与SQL中的limit关键字类似,当搜索到的结果数量达到 limit 的限制时,就停止搜索返回结果

print(soup.find_all('a',limit=2))

执行输出:

ContractedBlock.gif

ExpandedBlockStart.gif

[Elsie, Lacie]

View Code

2.7、recursive

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

print(soup.html.find_all('a'))print(soup.html.find_all('a',recursive=False))

执行输出:

ContractedBlock.gif

ExpandedBlockStart.gif

[Elsie, Lacie, Tillie]

[]

View Code

备注

像调用 find_all() 一样调用tag

find_all() 几乎是Beautiful Soup中最常用的搜索方法,所以我们定义了它的简写方法. BeautifulSoup 对象和 tag 对象可以被当作一个方法来使用,

这个方法的执行结果与调用这个对象的 find_all() 方法相同,下面两行代码是等价的:

soup.find_all("a")

soup("a")

这两行代码也是等价的:

soup.title.find_all(text=True)

soup.title(text=True)

3、find()

语法

find( name , attrs , recursive , text , **kwargs )

find_all() 方法将返回文档中符合条件的所有tag,尽管有时候我们只想得到一个结果.比如文档中只有一个

标签,那么使用 find_all() 方法来查找标签就不太合适, 使用 find_all 方法并设置 limit=1 参数不如直接使用 find() 方法.下面两行代码是等价的:

from bs4 importBeautifulSoup

soup=BeautifulSoup(html_doc,'lxml')print(soup.find_all('title', limit=1))#soup.find_all('title', limit=1)

print(soup.find('title'))#

The Dormouse's story

唯一的区别是 find_all() 方法的返回结果是值包含一个元素的列表,而 find() 方法直接返回结果.

find_all() 方法没有找到目标是返回空列表, find() 方法找不到目标时,返回 None .

print(soup.find("nosuchtag"))#None

soup.head.title 是 tag的名字 方法的简写.这个简写的原理就是多次调用当前tag的 find() 方法:

print(soup.head.title)#

The Dormouse's story

print(soup.find("head").find("title"))#

The Dormouse's story

4、其他方法

5、css选择器

我们在写 CSS 时,标签名不加任何修饰,类名前加点,id名前加 #,在这里我们也可以利用类似的方法来筛选元素,用到的方法是 soup.select(),返回类型是 list

1. 通过标签名查找

from bs4 importBeautifulSoup

soup=BeautifulSoup(html_doc,'lxml')print(soup.select("title")) #[

The Dormouse's story]

print(soup.select("b")) #[The Dormouse's story]

2. 通过类名查找

print(soup.select(".sister"))'''[Elsie,

Lacie,

Tillie]'''

3. 通过 id 名查找

print(soup.select("#link1"))#[Elsie]

4. 组合查找

组合查找即和写 class 文件时,标签名与类名、id名进行的组合原理是一样的,例如查找 p 标签中,id 等于 link1的内容,二者需要用空格分开

print(soup.select("p #link2"))#[Lacie]

直接子标签查找

print(soup.select("p > #link2"))#[Lacie]

5. 属性查找

查找时还可以加入属性元素,属性需要用中括号括起来,注意属性和标签属于同一节点,所以中间不能加空格,否则会无法匹配到。

print(soup.select("a[href='http://example.com/tillie']"))#[Tillie]

select 方法返回的结果都是列表形式,可以遍历形式输出,然后用 get_text() 方法来获取它的内容:

for title in soup.select('a'):print(title.get_text())'''Elsie

Lacie

Tillie'''

5. 修改文档树

二、Xpath模块

xpath简介

XPath在Python的爬虫学习中,起着举足轻重的地位,对比正则表达式 re两者可以完成同样的工作,实现的功能也差不多,但XPath明显比re具有优势,在网页分析上使re退居二线。

XPath介绍

是什么? 全称为XML Path Language 一种小型的查询语言

说道XPath是门语言,不得不说它所具备的优点:

可在XML中查找信息

支持HTML的查找

通过元素和属性进行导航

python开发使用XPath条件: 由于XPath属于lxml库模块,所以首先要安装库lxml。

XPath的简单调用方法:

from lxml importetree

selector=etree.HTML(源码) #将源码转化为能被XPath匹配的格式

selector.xpath(表达式)#返回为一列表

Xpath语法

查询

html_doc = """

Title

ALex is dsb

Egon too

baidu

百度

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值