(17)python Beautiful Soup 4.6

一、安装

1.登陆官网:https://www.crummy.com/software/BeautifulSoup/  

2.下载

 

3.解压

4.安装

cmd找到文件路径,运行  

setup.py build

然后输入

python  setup.py  install

 

5.测试

打开python 导入bs4 模块看看是否报错

import bs4

没报错就看安装成功了

 二、安装解析器

soup=BeautifulSoup(html文档字符串,html解析器,html文档编码)

例如:

soup=BeautifulSoup(str,"lxml",from_encoding='utf8')

 

安装lxml和html5lib

 

 
解析器使用方法优势劣势
Python标准库 BeautifulSoup(markup, “html.parser”)
  • Python的内置标准库
  • 执行速度适中
  • 文档容错能力强
  • Python 2.7.3 or 3.2.2)前 的版本中文档容错能力差
lxml HTML 解析器 BeautifulSoup(markup, “lxml”)
  • 速度快
  • 文档容错能力强
  • 需要安装C语言库
lxml XML 解析器 BeautifulSoup(markup, [“lxml”, “xml”])BeautifulSoup(markup, “xml”)
  • 速度快
  • 唯一支持XML的解析器
  • 需要安装C语言库
html5lib BeautifulSoup(markup, “html5lib”)
  • 最好的容错性
  • 以浏览器的方式解析文档
  • 生成HTML5格式的文档
  • 速度慢

 推荐使用lxml作为解析器,因为效率更高. 在Python2.7.3之前的版本和Python3中3.2.2之前的版本,必须安装lxml或html5lib, 因为那些Python版本的标准库中内置的HTML解析方法不够稳定

三、 查找文档树

标签:例如<body>,<head>,<title>,<a>,<p>,<b>

 

 获得第一个 title 标签

print soup.title

结果:<title>百度一下,你就知道</title>

 

获得第一个p标签(可以多个点,但下面这个例子实际body没什么意义)

print soup.body.p

结果:<p><b>手机百度</b></p>

 

标签的两个属性

name

得到标签本身的名称

print soup.title.name

结果:title

Attributes

通过属性,返回一个字典

print soup.a.attrs

源代码

<a href="/" id="result_logo" onmousedown="return c({'fm':'tab','tab':'logo'})"><img src="//www.baidu.com/img/baidu_jgylogo3.gif" alt="到百度首页" title="到百度首页"></a>

取值后

{'href': '/', 'id': 'result_logo', 'onmousedown': "return c({'fm':'tab','tab':'logo'})"}

获得attrs属性的值

print soup.a['id']

结果:result_logo

还可以这样,利用get方法,传入属性的名称,二者是等价的

print soup.a.get('id')

 

一个属性有多个值时,lxml做解析器时,会返回list

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

结果:['body', 'strikeout']

如果没有定义多值属性会返回一个字符串

 

 

import  urllib2
from bs4 import BeautifulSoup

#response =urllib2.urlopen("http://www.baidu.com")
request = urllib2.Request("http://www.baidu.com")
response = urllib2.urlopen(request)
str=response.read()
soup=BeautifulSoup(str,"lxml")

 

优化格式

print soup.prettify()

 

获得标签内容

print soup.title.string

结果:

百度一下,你就知道

 

第一个 p 标签的 class 属性内容

print soup.p['class']

 

输出第一个 a 标签的  href 属性内容

print soup.a['href']

 

循环a标签

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

 

重点介绍下find_all()方法

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

可以只写一个name参数

find_all()的第一个参数可以是标签、正则表达式、列表、Ture、方法

返回 <class 'bs4.element.ResultSet'> 类型

标签

用于查找所有完全匹配的标签

soup.find_all('b')

会找出所有<b>的标签

 

正则表达式

import re
for tag in soup.find_all(re.compile("^b")):
    print(tag.name)

会找出所有b开头的标签

 

列表

如果传入列表参数,Beautiful Soup会将与列表中任一元素匹配的内容返回

soup.find_all(["a", "b"])

找出所有的a标签和b标签

 

Ture

Ture找出所有标签,配合name属性和循环,列出所有的标签名

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

 

方法

如果找不到合适的过滤器,自定义

待写

 

关键字参数

soup.find_all(id='link2')
soup.find_all(href=re.compile("elsie"))
soup.find_all(id=True)

 

同时过滤多属性

soup.find_all(href=re.compile("elsie"), id='link1')

 

按class搜索

参数不能用class,这是python的关键字,要用class_参数用法一样

soup.find_all("a", class_="sister")

 

通过内容查找

string参数

soup.find_all("a", string="Elsie")

 

限制返回结果的数量

limit参数

soup.find_all("a", limit=2)

 

recursive 参数

参数 recursive=False 表示只能查他的下一层节点,没有的话返回[]

<html>
 <head>
  <title>
   The Dormouse's story
  </title>
 </head>

 

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

soup.html.find_all("title", recursive=False)
# []

 

find_all()方法简写

因为find_all()是最常用的方法,所以程序上可以把他省略

soup.find_all("a")相当于soup("a")

 

find()方法

soup.find_all('title', limit=1)相当于soup.find('title')

区别是find_all返回值是一个元素列表, find直接返回结果

find_all找不到目标时返回空列表,find返回None

soup.head.title的原理就是soup.find("head").find("title")

 

 

find_parents() 和 find_parent()

用来搜索当前节点的父辈节点,搜索方法与普通tag的搜索方法相同,搜索文档搜索文档包含的内容. 我们从一个文档中的一个叶子节点开始

 

find_next_siblings()和find_next_sibling()

属性对当前tag的后面解析

find_next_siblings():方法返回所有符合条件的后面的兄弟节点

find_next_sibling():只返回符合条件的后面的第一个tag节点

find_previous_siblings和 find_previous_sibling()

属性对当前tag的前面解析

find_previous_siblings:方法返回所有符合条件的前面的兄弟节点

 find_previous_sibling():方法返回第一个符合条件的前面的兄弟节点

find_all_next() 和 find_next()

对当前tag的之后的 tag和字符串进行迭代

find_all_next()方法返回所有符合条件的节点, find_next() 方法返回第一个符合条件的节点

find_all_previous() 和 find_previous()

对当前节点前面的tag和字符串进行迭代

find_all_previous() 方法返回所有符合条件的节点, find_previous() 方法返回第一个符合条件的节点

 

三、CSS选择器

BeautifulSoup 对象的 .select() 方法中传入字符串参数, 即可使用CSS选择器的语法找到tag

代写

 

 

四、修改文档树

 

soup = BeautifulSoup('<b class="boldest">Extremely bold</b>')
tag = soup.b

tag.name = "blockquote"
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>

 

修改 .string

给tag的 .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."
tag
# <a href="http://example.com/">New link text.</a>

注意: 如果当前的tag包含了其它tag,那么给它的 .string 属性赋值会覆盖掉原有的所有内容包括子tag

 

append()

Tag.append() 方法想tag中添加内容,就好像Python的列表的 .append() 方法:

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

soup
# <html><head></head><body><a>FooBar</a></body></html>
soup.a.contents
# [u'Foo', u'Bar']

 

clear()

Tag.clear() 方法移除当前tag的内容:

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

tag.clear()
tag
# <a href="http://example.com/"></a>

 

五、输出

格式化输出

markup = '<a href="http://example.com/">I linked to <i>example.com</i></a>'
soup = BeautifulSoup(markup)
soup.prettify()
# '<html>\n <head>\n </head>\n <body>\n  <a href="http://example.com/">\n...'

print(soup.prettify())
# <html>
#  <head>
#  </head>
#  <body>
#   <a href="http://example.com/">
#    I linked to
#    <i>
#     example.com
#    </i>
#   </a>
#  </body>
# </html>

 

BeautifulSoup 对象和它的tag节点都可以调用 prettify() 方法:

print(soup.a.prettify())
# <a href="http://example.com/">
#  I linked to
#  <i>
#   example.com
#  </i>
# </a>

 

压缩输出

如果只想得到结果字符串,不重视格式,那么可以对一个 BeautifulSoup 对象或 Tag 对象使用Python的 unicode() 或 str() 方法:

str(soup)
# '<html><head></head><body><a href="http://example.com/">I linked to <i>example.com</i></a></body></html>'

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

str() 方法返回UTF-8编码的字符串,可以指定 编码 的设置.

还可以调用 encode() 方法获得字节码或调用 decode() 方法获得Unicode.

 

输出格式

Beautiful Soup输出是会将HTML中的特殊字符转换成Unicode,比如“&lquot;”:

soup = BeautifulSoup("&ldquo;Dammit!&rdquo; he said.")
unicode(soup)
# u'<html><head></head><body>\u201cDammit!\u201d he said.</body></html>'

如果将文档转换成字符串,Unicode编码会被编码成UTF-8.这样就无法正确显示HTML特殊字符了:

str(soup)
# '<html><head></head><body>\xe2\x80\x9cDammit!\xe2\x80\x9d he said.</body></html>'

 

get_text()

如果只想得到tag中包含的文本内容,那么可以用 get_text() 方法,这个方法获取到tag中包含的所有文版内容包括子孙tag中的内容,并将结果作为Unicode字符串返回:

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

soup.get_text()
u'\nI linked to example.com\n'
soup.i.get_text()
u'example.com'

 

可以通过参数指定tag的文本内容的分隔符:

# soup.get_text("|")
u'\nI linked to |example.com|\n'

 

还可以去除获得文本内容的前后空白:

# soup.get_text("|", strip=True)
u'I linked to|example.com'

 

或者使用 .stripped_strings 生成器,获得文本列表后手动处理列表:

[text for text in soup.stripped_strings]
# [u'I linked to', u'example.com']

 

六、指定文档解析器

解析器之间的区别

Beautiful Soup为不同的解析器提供了相同的接口,但解析器本身时有区别的.同一篇文档被不同的解析器解析后可能会生成不同结构的树型文档.区别最大的是HTML解析器和XML解析器,看下面片段被解析成HTML结构:

BeautifulSoup("<a><b /></a>")
# <html><head></head><body><a><b></b></a></body></html>

因为空标签<b />不符合HTML标准,所以解析器把它解析成<b></b>

同样的文档使用XML解析如下(解析XML需要安装lxml库).注意,空标签<b />依然被保留,并且文档前添加了XML头,而不是被包含在<html>标签内:

BeautifulSoup("<a><b /></a>", "xml")
# <?xml version="1.0" encoding="utf-8"?>
# <a><b/></a>

 

HTML解析器之间也有区别,如果被解析的HTML文档是标准格式,那么解析器之间没有任何差别,只是解析速度不同,结果都会返回正确的文档树.

但是如果被解析文档不是标准格式,那么不同的解析器返回结果可能不同.下面例子中,使用lxml解析错误格式的文档,结果</p>标签被直接忽略掉了:

BeautifulSoup("<a></p>", "lxml")
# <html><body><a></a></body></html>

 

使用html5lib库解析相同文档会得到不同的结果:

BeautifulSoup("<a></p>", "html5lib")
# <html><head></head><body><a><p></p></a></body></html>

 

html5lib库没有忽略掉</p>标签,而是自动补全了标签,还给文档树添加了<head>标签.

使用pyhton内置库解析结果如下:

BeautifulSoup("<a></p>", "html.parser")
# <a></a>

 

与lxml库类似的,Python内置库忽略掉了</p>标签,与html5lib库不同的是标准库没有尝试创建符合标准的文档格式或将文档片段包含在<body>标签内,与lxml不同的是标准库甚至连<html>标签都没有尝试去添加.

因为文档片段“<a></p>”是错误格式,所以以上解析方式都能算作”正确”,html5lib库使用的是HTML5的部分标准,所以最接近”正确”.不过所有解析器的结构都能够被认为是”正常”的.

不同的解析器可能影响代码执行结果,如果在分发给别人的代码中使用了 BeautifulSoup ,那么最好注明使用了哪种解析器,以减少不必要的麻烦.

 

七、编码

任何HTML或XML文档都有自己的编码方式,比如ASCII 或 UTF-8,但是使用Beautiful Soup解析后,文档都被转换成了Unicode:

markup = "<h1>Sacr\xc3\xa9 bleu!</h1>"
soup = BeautifulSoup(markup)
soup.h1
# <h1>Sacré bleu!</h1>
soup.h1.string
# u'Sacr\xe9 bleu!'

这不是魔术(但很神奇),Beautiful Soup用了 `编码自动检测`_ 子库来识别当前文档编码并转换成Unicode编码. BeautifulSoup 对象的 .original_encoding 属性记录了自动识别编码的结果:

soup.original_encoding
'utf-8'

`编码自动检测`_ 功能大部分时候都能猜对编码格式,但有时候也会出错.有时候即使猜测正确,也是在逐个字节的遍历整个文档后才猜对的,这样很慢.如果预先知道文档编码,可以设置编码参数来减少自动检查编码出错的概率并且提高文档解析速度.在创建 BeautifulSoup 对象的时候设置 from_encoding参数.

下面一段文档用了ISO-8859-8编码方式,这段文档太短,结果Beautiful Soup以为文档是用ISO-8859-7编码:

markup = b"<h1>\xed\xe5\xec\xf9</h1>"
soup = BeautifulSoup(markup)
soup.h1
<h1>νεμω</h1>
soup.original_encoding
'ISO-8859-7'

通过传入 from_encoding 参数来指定编码方式:

soup = BeautifulSoup(markup, from_encoding="iso-8859-8")
soup.h1
<h1>םולש</h1>
soup.original_encoding
'iso8859-8'

如果仅知道文档采用了Unicode编码, 但不知道具体编码. 可以先自己猜测, 猜测错误(依旧是乱码)时, 可以把错误编码作为 exclude_encodings 参数, 这样文档就不会尝试使用这种编码了解码了. 译者备注: 在没有指定编码的情况下, BS会自己猜测编码, 把不正确的编码排除掉, BS就更容易猜到正确编码.

soup = BeautifulSoup(markup, exclude_encodings=["ISO-8859-7"])
soup.h1
<h1>םולש</h1>
soup.original_encoding
'WINDOWS-1255'

少数情况下(通常是UTF-8编码的文档中包含了其它编码格式的文件),想获得正确的Unicode编码就不得不将文档中少数特殊编码字符替换成特殊Unicode编码,“REPLACEMENT CHARACTER” (U+FFFD, �) .如果Beautifu Soup猜测文档编码时作了特殊字符的替换,那么Beautiful Soup会把UnicodeDammit 或 BeautifulSoup 对象的 .contains_replacement_characters 属性标记为 True .这样就可以知道当前文档进行Unicode编码后丢失了一部分特殊内容字符.如果文档中包含�而.contains_replacement_characters 属性是 False ,则表示�就是文档中原来的字符,不是转码失败.

 

posted @ 2017-07-11 10:57  富坚老贼  阅读(455)  评论(0编辑  收藏  举报