python3解析库BeautifulSoup4

Beautiful Soup是python的一个HTML或XML的解析库,我们可以用它来方便的从网页中提取数据,它拥有强大的API和多样的解析方式。

Beautiful Soup的三个特点:

Beautiful Soup提供一些简单的方法和python式函数,用于浏览,搜索和修改解析树,它是一个工具箱,通过解析文档为用户提供需要抓取的数据

Beautiful Soup自动将转入稳定转换为Unicode编码,输出文档转换为UTF-8编码,不需要考虑编码,除非文档没有指定编码方式,这时只需要指定原始编码即可

Beautiful Soup位于流行的Python解析器(如lxml和html5lib)之上,允许您尝试不同的解析策略或交易速度以获得灵活性。


1、Beautiful Soup4的安装配置

Beautiful Soup4通过PyPi发布,所以可以通过系统管理包工具安装,包名字为beautifulsoup4

$easy_install beautifulsoup4

或者

$pip install beautifulsoup4

也可用通过下载源码包来安装:

#wget  https://www.crummy.com/software/BeautifulSoup/bs4/download/4.0/beautifulsoup4-4.1.0.tar.gz#tar xf beautifulsoup4-4.1.0.tar.gz#cd beautifulsoup4#python setup.py install

Beautiful Soup在解析时实际上是依赖解析器的,它除了支持python标准库中的HTML解析器外还支持第三方解析器如lxml

Beautiful Soup支持的解析器,以及它们的优缺点:

解析器使用方法优势劣势

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语言库

html5libBeautifulSoup(markup,"html5lib")最好的容错性

以浏览器的方式解析文档

生成HTML5格式的文档

速度慢

不依赖外部扩展

安装解析器:

$pip install lxml

$pip install html5lib

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

2、BeautifulSoup的基本用法

通过传入一段字符或一个文件句柄,BeautifulSoup的构造方法就能得到一个文档的对象,选择合适的解析器来解析文档,如手动指定将选择指定的解析器来解析文档,Beautiful Soup将复杂的HTML文档转换成一个复杂的树形结构,每个节点都是python对象,所有对象可以归纳为4种:Tag、NavigableString、BeautifulSoup、Comment

注意:BeautifulSoup版本4的包是在bs4中引入的

frombs4importBeautifulSoup#下面代码示例都是用此文档测试html_doc="""<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 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>"""markup="<b><!--Hey, buddy. Want to buy a used parser?--></b>"soup=BeautifulSoup(html_doc,"lxml")

soup1=BeautifulSoup(markup,"lxml")

tag=soup.a

navstr=tag.string

comment=soup1.b.stringprint(type(tag))#Tag标签对象print(type(comment))#Comment对象包含文档注释内容print(type(navstr))#NavigableString对象包装字符串内容print(type(soup))#BeautifulSoup对象为文档的全部内容#

(1)节点选择器(tag)

直接调用节点的名称就可以选择节点元素,节点可以嵌套选择返回的类型都是bs4.element.Tag对象

soup=BeautifulSoup(html_doc,'lxml')print(soup.head)#获取head标签print(soup.p.b)#获取p节点下的b节点print(soup.a.string)#获取a标签下的文本,只获取第一个

name属性获取节点名称:

soup.body.name

attrs属性获取节点属性,也可以字典的形式直接获取,返回的结果可能是列表或字符串类型,取决于节点类型

soup.p.attrs#获取p节点所有属性soup.p.attrs['class']#获取p节点class属性soup.p['class']#直接获取p节点class属性

string属性获取节点元素包含的文本内容:

soup.p.string#获取第一个p节点下的文本内容

contents属性获取节点的直接子节点,以列表的形式返回内容

soup.body.contents#是直接子节点,不包括子孙节点

children属性获取的也是节点的直接子节点,只是以生成器的类型返回

soup.body.children

descendants属性获取子孙节点,返回生成器

soup.body.descendants

parent属性获取父节点,parents获取祖先节点,返回生成器

soup.b.parent

soup.b.parents

next_sibling属性返回下一个兄弟节点,previous_sibling返回上一个兄弟节点,注意换行符也是一个节点,所以有时候在获取兄弟节点是通常是字符串或者空白

soup.a.next_sibling

soup.a.previous_sibling

next_siblings和previous_sibling分别返回前面和后面的所有兄弟节点,返回生成器

soup.a.next_siblings

soup.a.previous_siblings

next_element和previous_element属性获取下一个被解析的对象,或者上一个

soup.a.next_element

soup.a.previous_element

next_elements和previous_elements迭代器向前或者后访问文档解析内容

soup.a.next_elements

soup.a.previous_elements

(2)方法选择器

前面使用的都是通过节点属性来选择的,这种方法非常快,但在进行比较复杂的选择时就不够灵活,幸好Beautiful Soup还为我们提供了一些查询方法,如fang_all()和find()等

find_all(name,attrs,recursive,text,**kwargs):查询所有符合条件的元素,其中的参数

name表示可以查找所有名字为name的标签(tag),也可以是过滤器,正则表达式,列表或者是True

attrs表示传入的属性,可以通过attrs参数以字典的形式指定如常用属性id,attrs={'id':'123'},由于class属性是python中的关键字,所有在查询时需要在class后面加上下划线即class_='element',返回的结果是tag类型的列表

text参数用来匹配节点的文本,传入的形式可以是字符串也可以是正则表达式对象

recursive表示,如果只想搜索直接子节点可以将参数设为false:recursive=Flase

limit参数,可以用来限制返回结果的数量,与SQL中的limit关键字类似

import refrombs4import BeautifulSoup

html_doc ="""#下面示例都是用此文本内容测试<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 href="http://example.com/elsie" class="sister" id="link1">Elsie</a>

    ddd

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

<span>中文</span>"""soup=BeautifulSoup(html_doc,'lxml')print(type(soup))print(soup.find_all('span'))#标签查找print(soup.find_all('a',id='link1'))#属性加标签过滤print(soup.find_all('a',attrs={'class':'sister','id':'link3'}))#多属性print(soup.find_all('p',class_='title'))#class特殊性,此次传入的参数是**kwargsprint(soup.find_all(text=re.compile('Tillie')))#文本过滤print(soup.find_all('a',limit=2))#限制输出数量#[中文]

[Elsie]

[Tillie]

[The Dormouse's story</b></p>]['Tillie']

[Elsie, Lacie]

find( name , attrs , recursive , text , **kwargs ):它返回的是单个元素,也就是第一个匹配的元素,类型依然是tag类型

参数同find_all()一样

另外还有许多查询方法,其用法和前面介绍的find_all()方法完全相同,只不过查询范围不同,参数也一样

find_parents(name , attrs , recursive , text , **kwargs )和find_parent(name , attrs , recursive , text , **kwargs ):前者返回所有祖先节点,后者返回直接父节点

find_next_siblings(name , attrs , recursive , text , **kwargs )和find_next_sibling(name , attrs , recursive , text , **kwargs ):对当前tag后面的节点进行迭代,前者返回后面的所有兄弟节点,后者返回后面第一个兄弟节点

find_previous_siblings(name , attrs , recursive , text , **kwargs )和find_previous_sibling(name , attrs , recursive , text , **kwargs ):对当前tag前面的节点进行迭代,前者返回前面的所有兄弟节点,后者返回前面的第一个兄弟节点

find_all_next(name , attrs , recursive , text , **kwargs )和find_next(name , attrs , recursive , text , **kwargs ):对当前tag之后的tag和字符串进行迭代,前者返回所有符合条件的节点,后者返回第一个符合条件的节点

find_all_previous()和find_previous():对当前tag之前的tag和字符串进行迭代,前者返回节点后所有符合条件的节点,后者返回第一个符合条件的节点

(3)CSS选择器

Beautiful Soup还提供了CSS选择器,如果多CSS选择器不熟悉可以参考下http://www.w3school.com.cn/cssref/css_selectors.asp

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

In [10]: soup.select('title')

Out[10]: [The Dormouse's story</title>]

通过tag标签逐层查找:

In [12]: soup.select('body a')

Out[12]:

[Elsie,

Lacie,

Tillie]

查找某个tag标签下的直接子标签:

In [13]: soup.select('head > title')

Out[13]: [The Dormouse's story</title>]

查找兄弟节点标签:

In [14]: soup.select('#link1 ~ .sister')

Out[14]:

[Lacie,

Tillie]

通过CSS类名查找:

In [15]: soup.select('.title')

Out[15]: [The Dormouse's story</b></p>]In [16]: soup.select('[class~=title]')

Out[16]: [The Dormouse's story</b></p>]

通过tag的id查找:

In [17]: soup.select('#link1')

Out[17]: [Elsie]

In [18]: soup.select('a#link2')

Out[18]: [Lacie]

通过是否存在某个属性来查找:

In [20]: soup.select('a[href]')

Out[20]:

[Elsie,

Lacie,

Tillie]

通过属性的值来查找匹配:

In [22]: soup.select('a[href="http://example.com/elsie"]')

Out[22]: [Elsie]

In [23]: soup.select('a[href^="http://example.com/"]')#匹配值的开头Out[23]:

[Elsie,

Lacie,

Tillie]

In [24]: soup.select('a[href$="tillie"]')#匹配值的结尾Out[24]: [Tillie]

In [25]: soup.select('a[href*=".com/el"]')#模糊匹配Out[25]: [Elsie]

tag节点查找,方法选择器查找和CSS选择器查找三种方法的实现方式基本相似,tag相对于其他两种所有最快速的查找方式,但方法选择器提供更便利更复杂的查找方式,使用更如有上手。

(4)tag修改方法

Beautiful Soup的强项是文档的搜索功能,修改功能使用场景不是很多只做简单介绍,要了解更多修改方法请前往Beautiful Soup官方文档查看。

Beautiful Soup可以实现改变tag标志的属性的值,添加或删除属性和内容,下面介绍一些常用的方法

In [26]: markup='<a href="http://www.baidu.com/">baidu</a>'In [28]: soup=BeautifulSoup(markup,'lxml')

In [29]: soup.a.string='百度'In [30]: soup.a

Out[30]: 百度#如果a节点下包括子也将被覆盖掉

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

In [30]: soup.a

Out[30]: 百度In [31]: soup.a.append('一下')

In [32]: soup.a

Out[32]: 百度一下

new_tag()方法用于创建一个tag标签

In [33]: soup=BeautifulSoup('<b></b>','lxml')

In [34]: new_tag=soup.new_tag('a',href="http://www.python.org")#创建tag,第一个参数必须为tag的名称In [35]: soup.b.append(new_tag)#添加到b节点下In [36]: new_tag.string='python'#为tag设置值In [37]: soup.b

Out[37]: python

其他方法:

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

inert_before()在当前tag或文本节点前插入内容

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

clear()移除当前tag的内容

extract()将当前tag移除文档数,并作为方法结果返回

prettify()将Beautiful Soup的文档数格式化后以Unicode编码输出,tag节点也可以调用

get_text()输出tag中包含的文本内容,包括子孙tag中的内容

soup.original_encoding 属性记录了自动识别的编码结果

from_encoding:参数在创建BeautifulSoup对象是可以用来指定编码,减少猜测编码的运行速度

#解析部分文档,可以使用SoupStrainer类来创建一个内容过滤器,它接受同搜索方法相同的参数

frombs4import BeautifulSoup,SoupStrainer

html_doc ="""<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 href="http://example.com/elsie" class="sister" id="link1">Elsie</a>

    ddd

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

<span>中文</span>"""only_a_tags = SoupStrainer('a')#顾虑器soup=BeautifulSoup(html_doc,'lxml',parse_only=only_a_tags)print(soup.prettify())# Elsie Lacie Tillie

#Beautiful Soup异常处理:

HTMLParser.HTMLParseError:malformed     start    tag  

HTMLParser.HTMLParseError:bad   end   tag 这个两个异常都是解析器引起的,解决方法是安装lxml或者html5lib

官方文档:https://www.crummy.com/software/BeautifulSoup/bs4/doc/

中文文档:https://www.crummy.com/software/BeautifulSoup/bs4/doc.zh

PyPI:https://pypi.org/project/beautifulsoup4/

参考文献:https://www.cnblogs.com/zhangxinqi/p/9218395.html

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,457评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,837评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,696评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,183评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,057评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,105评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,520评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,211评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,482评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,574评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,353评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,213评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,576评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,897评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,174评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,489评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,683评论 2 335