python版本回退_Python爬虫之BeautifulSoup解析之路

上一篇分享了正则表达式的使用,相信大家对正则也已经有了一定的了解。它可以针对任意字符串做任何的匹配并提取所需信息。

但是我们爬虫基本上解析的都是html或者xml结构的内容,而非任意字符串。正则表达式虽然很强大灵活,但是对于html这样结构复杂的来说,写pattern的工作量会大大增加,并且有任意一处出错都得不到匹配结果,比较麻烦。

本篇将介绍一款针对html和xml结构,操作简单并容易上手的解析利器—BeautifulSoup。

format,png

BeautifulSoup的介绍

第一次使用BeautifulSoup的时候就在想:这个名字有什么含义吗?美味的汤?于是好信也在网上查了一下。

来看,官方文档是这么解释的:

BeautifulSoup: We called him Tortoise because he taught us”

意思是我们叫他乌龟因为他教了我们,当然这里Tortoise是Taught us的谐音。BeautifulSoup这个词来自于《爱丽丝漫游仙境》,意思是“甲鱼汤”。上面那个官方配图也是来自于《爱丽丝漫游仙境》,看来是没跑了,估计是作者可能很喜欢这部小说吧,因而由此起了这个名字。

好,让我们看看真正的BeautifulSoup是什么?

BeautifulSoup是Python语言中的模块,专门用于解析html/xml,非常适合像爬虫这样的项目。它有如下几个使其强大的特点:

它提供了几个超级简单的方法和Pythonic的语句来实现强大的导航、搜索、修改解析树的功能。

它会自动把将要处理的文档转化为Unicode编码,并输出为utf-8的编码,不需要你再考虑编码的问题。

支持Python标准库中的HTML解析器,还支持第三方的模块,如 lxml解析器 。

BeautifulSoup的安装

目前BeautifulSoup的最新发型版本是BeautifulSoup4,在Python中以bs4模块引入。

博主使用的Python3.x,可以使用 pip3 install bs4 来进行安装,也可以通过官方网站下载来安装,链接:https://www.crummy.com/software/BeautifulSoup/,具体安装步骤不在此叙述了。

以为安装完了吗?还没有呢。

上面介绍BeautifulSoup的特点时说到了,BeautifulSoup支持Python标准库的解析器html5lib,纯Python实现的。除此之外,BeautifulSoup还支持lxml解析器,为了能达到更好的解析效果,建议将这两个解析器也一并安装上。

根据操作系统不同,可以选择下列方法来安装lxml:

$ apt-get install Python-lxml

$ easy_install lxml

$ pip install lxml

另一个可供选择的解析器是纯Python实现的 html5lib , html5lib的解析方式与浏览器相同,可以选择下列方法来安装html5lib:

$ apt-get install Python-html5lib

$ easy_install html5lib

$ pip install html5lib

下面列出上面提到解析器的使用方法。

解析器使用方法

Python标准库BeautifulSoup(markup, "html.parser")

lxml HTML解析器BeautifulSoup(markup, "lxml")

lxml HTML解析器BeautifulSoup(markup, ["lxml",   "xml"])

BeautifulSoup(markup, "xml")

html5libBeautifulSoup(markup, "html5lib")

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

BeautifulSoup的文档对象创建

首先引入bs4库,也就是BeautifulSoup在Python中的模块。

from bs4 import BeautifulSoup

好了,我们来看一下官方提供的例子,这段例子引自《爱丽丝漫游记》。

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

andTillie;

and they lived at the bottom of a well.

...

"""

假设以上html_doc就是我们已经下载的网页,我们需要从中解析并获取感兴趣的内容。

首先的首先,我们需要创建一个BeautifulSoup的文档对象,依据不同需要可以传入“字符串”或者“一个文件句柄”。

传入“字符串”

soup = BeautifulSoup(html_doc)

传入“文件句柄”,打开一个本地文件

soup = BeautifulSoup(open("index.html"))

文档首先被转换为Unicode,如果是解析html文档,直接创建对象就可以了(像上面操作那样),这时候BeautifulSoup会选择一个最合适的解析器对文档进行解析。

但同时,BeautifulSoup也支持手动选择解析器,根据指定解析器进行解析(也就是我们安装上面html5lib和lxml的原因)。

手动指定解析器如下:

soup = BeautifulSoup(html_doc, "lxml")

如果仅是想要解析HTML文档,只要用文档创建 BeautifulSoup 对象就可以了。Beautiful Soup会自动选择一个解析器来解析文档。但是还可以通过参数指定使用那种解析器来解析当前文档。

BeautifulSoup 第一个参数应该是要被解析的文档字符串或是文件句柄,第二个参数用来标识怎样解析文档。如果第二个参数为空,那么Beautiful Soup根据当前系统安装的库自动选择解析器,解析器的优先数序: lxml, html5lib, Python标准库。在下面两种条件下解析器优先顺序会变化:

要解析的文档是什么类型: 目前支持, “html”, “xml”, 和 “html5”

指定使用哪种解析器: 目前支持, “lxml”, “html5lib”, 和 “html.parser”

BeautifulSoup的对象种类

Beautiful Soup将复杂HTML文档转换成一个复杂的树形结构,每个节点都是Python对象,所有对象可以归纳为4种:Tag

NavigableString

BeautifulSoup

Comment

Tag就是html或者xml中的标签,BeautifulSoup会通过一定的方法自动寻找你想要的指定标签。查找标签这部分会在后面“遍历查找树”和“搜索查找树”中介绍,这里仅介绍对象。

soup = BeautifulSoup('Extremely bold')

tag=soup.b

type(tag)

>>>

Tag标签下也有对象,有两个重要的属性对象:name和attributes。

Name

Name就是标签tag的名字,以下简单操作便可获取。

tag.name

>>> u'b'

Attributes

我们都知道一个标签下可能有很多属性,比如上面那个标签b有class属性,属性值为boldest,那么我们如何获取这个属性值呢?

其实标签的属性操作和Python中的字典操作一样的,如下:

tag['class']

>>> u'boldest'

也可以通过“点”来获取,比如:

tag.attrs

>>> {u'class': u'boldest'}

NavigableString是可遍历字符串的意思,其实就是标签内包括的字符串,在爬虫里也是我们主要爬取的对象之一。

在BeautifulSoup中可以非常简单的获取标签内这个字符串。

tag.string

>>> u'Extremely bold'

就这么简单的完成了信息的提取,简单吧。要说明一点,tag中包含的字符串是不能编辑的,但是可以替换。

tag.string.replace_with("No longer bold")

tag

>>>

No longer bold

BeautifulSoup对象表示的是一个文档的全部内容。大部分时候,可以把它当作Tag对象。

soup.name

>>> u'[document]'

BeautifulSoup对象不是一个真正的tag,没有name和attributes,但是却可以查看它的name属性。如上所示,“[document]”为BeautifulSoup文档对象的特殊属性名字。

还有一些对象也是我们需要特殊注意的,就是注释。其实comment对象是一个特殊类型的NavigableString对象,请看下面。

markup = ""

soup = BeautifulSoup(markup)

comment = soup.b.string

type(comment)

>>>

comment

>>> u'Hey, buddy. Want to buy a used parser'

这和NavigableString的使用是一样,同样使用 .string 对标签内字符串进行提取。但是,请看上面comment这个例子,里面字符串是一个comment,有这样的格式,一样使用了 .string对其进行提取,得到的结果是去掉了comment标志的里面的字符串。这样的话,当我们并不知道它是否是comment,如果得到以上的结果很有可能不知道它是个comment。

因此,这可能会让我们得到我们不想要的comment,扰乱我们的解析结果。

为了避免这种问题的发生,可以在使用之前首先通过以下代码进行一个简单的判断,然后再进行其它操作。

iftype(soup.b.string)==bs4.element.Comment:

print(soup.b.string)

BeautifulSoup的遍历文档树

仍然用最开始的《爱丽丝》中的一段话作为例子。

子节点

子节点有 .contents 和 .children 两种用法。

contents

content属性可以将标签所有子节点以列表形式返回。

#

The Dormouse's story

print(soup.head.contents)

>>> [title>The Dormouse's story]

这样就可以返回一个子节点标签了。当然你也可以通过soup.title来实现,但是当文档结构复杂的时候,比如有不止一个title的话,那这样就不如contents使用来的快了。

head下只有一个标签title,那么如果我们查看一下body下的子标签。

print(soup.body.contents)

>>>

['\n',

The Dormouse's story

, '\n',

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

Elsie,

Lacie

andTillie;

and they lived at the bottom of a well.

, '\n',

...

, '\n']

你会发现这些子节点列表中有很多“\n”,这是因为它把空格包括进去了,所以这里需要注意一下。

children

也可以通过 .chidren 得到相同的结果,只不过返回的children是一个生成器(generator),而不是一个列表。

print(soup.body.children)

>>>

看到这是一个生成器,因此我们可以for..in..进行遍历,当然也可以得到以上同样的结果。

forchildinsoup.body.children: print(child)

子孙节点

子孙节点使用.descendants属性。如果子节点可以直接获取标签的直接子节点,那么子孙节点则可以获取所有子孙节点,注意说的是所有,也就是说孙子的孙子都得给我找出来,下用面开一个例子。

for child in head_tag.descendants:    print(child)

>>>

The Dormouse's story

>>> The Dormouse's stor

title是head的子节点,而title中的字符串是title的子节点,title和title所包含的字符串都是head的子孙节点,因此被循环递归的查找出来。.descendants 的用法和 .children 是一样的,会返回一个生成器,需要for..in..进行遍历。

父节点

父节点使用 .parents 属性实现,可以得到父辈的标签。

title_tag = soup.title

title_tag

>>>

The Dormouse's story

title_tag.parent

>>>

The Dormouse's story

title_tag.parent.name

>>> head

获得全部父节点则使用.parents属性实现,可以循环得到所有的父辈的节点。

link = soup.a

for parent in link.parents:    if parent is None:        print(parent)    else:        print(parent.name)

>>>

p

body

html

[document]

None

可以看到a节点的所有父辈标签都被遍历了,包括BeautifulSoup对象本身的[document]。

兄弟节点

兄弟节点使用.next_sibling和.previous_sibling属性。

兄弟嘛,不难理解自然就是同等地位的节点了,其中next_sibling 获取下一个兄弟节点,而previous_sibling 获取前一个兄弟节点。

a_tag = soup.find("a", id="link1")

a_tag.next_sibling

>>> ,

a_tag.previous_element

>>>

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

兄弟节点可以通过 .next_siblings 和 .previous.sibling 获取所有前后兄弟节点,同样需要遍历获取每个元素。

回退和前进

当然还有一些其它用法,如回退和前进.next_element和.previous_element,它是针对所有节点的回退和前进,不分辈分。

a_tag = soup.find("a", id="link1")

a_tag

>>>

Elsie,

a_tag.next_element

>>> Elsie

a_tag.previous_element

>>>

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

因为使用了回退,将会寻找下一个节点对象而不分辈分,那么这个标签的下一个节点就是它的子节点Elsie,而上一个节点就是上一个标签的字符串对象。find用法会在后续搜索文档树里面详细介绍。

回退和前进也可以寻找所有的前后节点,使用.next_elements和.previous_elements。

for elem in last_a_tag.next_elements:

if elem.nameisNone:continue

print(elem.name)

>>>

a

a

p

返回对象同样是生成器,需要遍历获得元素。其中使用了if判断去掉了不需要的None。

节点内容

前面提到过NavigableString对象的 .string 用法,这里在文档遍历再次体会一下其用法。

如果tag只有一个NavigableString 类型子节点,那么这个tag可以使用.string得到子节点,就像之前提到的一样。而如果一个tag里面仅有一个子节点(比如tag里tag的字符串节点),那么这个tag也可以使用.string方法,输出结果与当前唯一子节点的.string结果相同(如上所示)。

title_tag.string

>>> u'The Dormouse's story'

head_tag.contents

>>> [

The Dormouse's story]

head_tag.string

>>> u'The Dormouse's story'

但是如果这个tag里面有多个节点,那就不灵了。因为tag无法确定该调用哪个节点,如下面这种。

print(soup.html.string)

>>> None

如果tag中包含多个字符串,可以使用 .strings 来循环获取,输出的字符串中可能包含了很多空格或空行,使用.stripped_strings可以去除多余空白内容。

上面提介绍的都是如何遍历各个节点,下面我们看看如何搜索我们我们真正想获取的内容,如标签属性等。

BeautifulSoup的搜索文档树

搜索文档树有很多种用法,但使用方法都基本一致。这里只选择介绍一种.find_all。

find_all()

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

find_all() 方法可以搜索当前标签下的子节点,并会经过过滤条件判断是否符合标准,先随便看个例子。

soup.find_all("a")

>>>

[Elsie,

Lacie,

Tillie]

soup.find_all(id="link2")

>>>

[Lacie]

通过以上例子,可以发现,我们只要设定好我们的过滤条件,便可轻松的解析我们想要的内容。这些条件如何设定呢?

就是通过find_all()的这些参数来设置的,让我们来看看。

Name参数

name参数就是标签的名字,如上面的例子寻找所有标签,name参数可以是字符串、True、正则表达式、列表、甚至具体方法。

下面举个正则表达式的例子。

importre

soup =BeautifulSoup(html_doc, 'lxml')fortag insoup.find_all(re.compile("^t")):print(tag.name)

>>> title

可以看到正则表达式的意思是匹配任何以“t”开头的标签名称,就只有title一个。

使用“True”会匹配任何值,使用“列表”会匹配列表中所有的标签项,如果没有合适的过滤条件,还可以自定义一个“方法”。

Keyword参数

就如同Python中的关键字参数一样,我们可以搜索指定的标签属性来定位标签。

soup.find_all(id='link2')

>>>

[Lacie]

找到了id属性为link2的标签。

soup.find_all(href=re.compile("elsie"))

>>>

[Elsie]

找到了href属性里含有“elsie”字样的标签。

也可以同时定义多个关键字条件来过滤匹配结果。

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

>>>

[three]

text参数

通过text参数可以搜索匹配的字符串内容,与name的用法相似,也可以使用字符串、True、正则表达式、列表、或者具体方法。

soup.find_all(text="Elsie")>>> [u'Elsie']

soup.find_all(text=re.compile("Dormouse")) >>>

[u"The Dormouse's story", u"The Dormouse's story"]

limit参数

limit参数可以限制返回匹配结果的数量,看下面这个例子。

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

>>>

[Elsie,

Lacie]

文档中本来有三个标签,但是通过限制只得到了两个。

recursive参数

find_all()会寻找符合匹配条件的所有子孙节点,如果我们只想找直接的子节点,就可以设置recursive参数来进行限制,recursive=False。

soup.html.find_all("title")

>>> [

The Dormouse's story]

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

>>> [ ]

上面是两种使用recursive和没有使用recursive的情况,可以发现它的作用。

以上就是find_all()所有参数的介绍,其它方法如find(),find_parents()等更多方法与find_all()基本一致,可以举一反三。

总结

以上就是BeautifulSoup的使用方法介绍,主要记住三个部分内容:

BeautifulSoup对象种类

BeautifulSoup的遍历文档树

BeautifulSoup的搜索文档树

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/372803.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

WebStorm 运行Rect Native 项目

今天教大家如何直接使用WebStorm这个IDE直接完成编码运行项目工作.这样就可以不用打开Xcode了. 1.首先点击WebStorm右上方的下拉箭头弹出的Edit Configurations.... 2.然后会进入一个配置页面.点击左上方的.在弹出的列表中选中npm.如图. 3.在右边的配置框中,先选择Command为hel…

python编程比赛_用Python编程分析4W场球赛后,2018世界杯冠军竟是…

比赛已经开始,我们不妨用 Python 来对参赛队伍的实力情况进行分析,并大胆的预测下本届世界杯的夺冠热门球队吧!通过数据分析,可以发现很多有趣的结果,比如:找出哪些队伍是首次进入世界杯的黑马队伍找出2018…

JavaFX 2 GameTutorial第2部分

介绍 Ť他的是一系列与一个JavaFX 2游戏教程博客条目的第二批。 如果您尚未阅读第1部分,请参阅JavaFX 2游戏教程的简介部分。 在第1部分中,我提到了游戏的某些方面以及原型飞船的简单演示(原型由简单的形状组成),该飞船…

sqlyog连接mysql教程_如何用SQLyog实现远程连接MySQL

SQLyog客户端,用root用户远程链接MySQL时,提示ldquo;访问被拒绝rdquo;,在网上搜索了一下原因。原来是MySQL没有授权其远程链1,SQLyog客户端,,用root用户远程链接MySQL时,提示“访问被拒绝”&…

JavaME:Google静态地图API

无论您是需要基于位置的应用程序的地图还是只是出于娱乐目的,都可以使用有史以来最简单的方法:Google Static Maps API。 在这篇文章中,我们将看到如何从纬度和经度获得地图作为图像。 可以使用Location API获得纬度和经度,我们将…

深入探讨JS中的数组排序函数sort()和reverse()

最近在研究Javascript发现了其中一些比较灵异的事情。有点让人感到无语比如: alert(typeof( NaN NaN));//结果为假。 alert(typeof( NaN ! NaN));//结果为真。 嘿嘿,当然这个不是这篇文章要讨论的!!开始我们的正文 首先,我们来看一下JS中sor…

带有谓词的Java中的功能样式-第1部分

您一直在听到将要席卷全球的函数式编程,而您仍然坚持使用普通Java? 不用担心,因为您已经可以在日常Java中添加一些功能样式。 此外,它很有趣,可以节省许多代码行并减少错误。 什么是谓词? 实际上&#xff…

centos 6.5下安装文件上传下载服务

centos 6.5下安装文件上传下载服务 由于每次在CentOS中要下载一些配置文件到物理机,和上传一些文件到服务器,导致来回的开启ftp软件有点麻烦,这里我们可以使用文件上传下载服务,来解决上传和下载的问题。 1.登录服务器 2.执行命令…

Jenkins 入门系列--jenkins 介绍

第一章 Jenkins是什么? Jenkins 是一个可扩展的持续集成引擎。 主要用于: l 持续、自动地构建/测试软件项目。 l 监控一些定时执行的任务。Jenkins拥有的特性包括: l 易于安装-只要把jenkins.war部署到servlet容器,不需要数据库支…

20162303《程序设计与数据结构》第一周学习总结

学号 2016-2017-2 《程序设计与数据结构》第1周学习总结 教材学习内容总结 本周学习了基本的JAVA知识,虽然比较基础,但是在实际过程中还是出现了许许多多的问题,代码一遍遍的敲错,又一遍遍的修改,刚开始甚至不会切换模…

面向接口编程详解(三)——模式研究

通过前面两篇,我想各位朋友对“面向接口编程”的思想有了一定认识,并通过第二篇的例子,获得了一定的直观印象。但是,第二篇中的例子旨在展示面向接口编程的实现方法,比较简单,不能体现出面向接口编程的优势…

错误学习:Java + OSGi

最近,我致力于在OSGi环境中使Apache Hive工作。 虽然没有被证明是小菜一碟(软件对吗?。。为什么我不感到惊讶? ),它引导我解决了各种Java和OSGi错误。 在这里,我列出了其中一些让我有些吃力的东…

Business Component(BC)和Business Object(BO)

Siebel应用架构的一个成功的地方就是在应用里引入了BC,BO的概念,从而使得几千张关系数据表能够按照业务的含义组织成业务对象,对于业务人员而言具有了业务上的含义,而不仅仅是从技术人员的观点来对待数据(就是关系表而…

NetBeans可用性提示

的Java IDE都来了,因为在很长的路要走天的JBuilder的 (尽管JBuilder中似乎是一个值得欢迎提前在时间)。 当今的Java IDE(例如NetBeans , Eclipse , IntelliJ IDEA和JDeveloper )是非常先进的工具…

WPF 反编译后错误处理

1. 首先,手动创建一个WPF工程(WpfApplicationReflectorDemo) 2. 把生成的WpfApplicationReflectorDemo.exe 拖到ILSpy里 3.点击 File -> Save Code...: 相应的代码会生成到指定地方。 4. 打开应用程序,并且编译它,此…

JavaFX 2 GameTutorial第1部分

介绍 我相信大多数软件开发人员可能会在年轻人(年轻人)一生中的某一时刻被迫创建游戏来帮助他们学习编程语言(我知道我确实做到了)。 以前,我的第一台计算机实际上是Franklin Ace 1000 ,后来是Apple [] 。 …

虚拟现实-VR-UE4-认识UE4

VR的火热,让每个人都想参与一下, 公司在展会上面搞了一个VR的Demo,关注度超出预期,使得公司高层决定来个VR项目 所以 关于UE4 百度百科地址:http://baike.baidu.com/link?urlmEmbwOcqEuqtkfdu9lNdxVtWAkv0Q6UHZ4VgIHr…

轻松完成Birt报告

这是使用Birt插件在Eclipse中构建报告的完整指南。 Birt或Business Intelligence and Reporting工具是一种无需编写太多Java代码即可生成报告的工具。 如果您使用的是ireport,那么您知道我在说什么:)(晶体报告..毫无意义&#xff…

揭开Python科学计算的面纱

春牛春杖。无限春风来海上。便与春工。染得桃红似肉红。 春幡春胜。一阵春风吹酒醒。不似天涯。卷起杨花似雪花。 标准的Python中用列表保存一组值,可以当做数组使用,但是由于其值类型任意,所以列表中保存的是指针,这样的话保存一…

TeamCity构建依赖项

介绍 构建依存关系的主题既不重要也不是次要的。 各种构建工具从不同的角度处理此主题,从而提供各种解决方案,每种解决方案都有其优点和缺点。 熟悉发行版和快照依赖项的Maven和Gradle用户可能不了解TeamCity快照依赖项,或者认为他们与Maven…