Beautiful Soup库简介 Beautiful Soup库是一个可以从HTML或XML文件中提取数据的Python库.它能够通过你喜欢的转换器实现惯用的文档导航,查找,修改文档的方式. Beautiful Soup库会帮你节省数小时甚至数天的工作时间.
安装Beautiful Soup库 安装Beautiful Soup库的最快也是最简单的方法是在shell上使用以下命令:pip install beautifulsoup4
一般使用时直接import即可from bs import BeautifulSoup
安装解析器 Beautiful Soup支持Python标准库中的HTML解析器, 还支持一些第三方的解析器, 其中一个是 lxml .可以使用pip install lxml
来安装lxml.
另一个可供选择的解析器是纯Python实现的 html5lib , html5lib的解析方式与浏览器相同, 可以使用pip install html5lib
来安装html5lib.
下表列出了主要的解析器,以及它们的优缺点:
解析器
使用方法
优势
劣势
Python标准库
BeautifulSoup(markup, “html.parser”)
Python的内置标准库 执行速度适中 文档容错能力强
Python 2.7.3 或 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作为解析器, 因为效率更高.
Beautiful Suop库的理解 使用以下代码可以获取HTML文档的BeautifulSuop类.
1 2 from bs4 import BeautifulSoupsoup = BeautifulSoup(open ("index.html" ), 'lxml' )
Beautiful Soup将复杂HTML文档转换成一个复杂的树形结构,每个节点都是Python对象,所有对象可以归纳为4种: BeautifulSoup, Tag, NavigableString, Comment.
BeautifulSoup BeautifulSoup 对象表示的是一个文档的全部内容.大部分时候,可以把它当作 Tag 对象,它支持 遍历文档树 和 搜索文档树 中描述的大部分的方法. BeautifulSoup 对象表示的是一个文档的全部内容.大部分时候,可以把它当作 Tag 对象,它支持 遍历文档树 和 搜索文档树 中描述的大部分的方法.
因为 BeautifulSoup 对象并不是真正的HTML或XML的tag,所以它没有name和attribute属性.但有时查看它的 .name 属性是很方便的,所以 BeautifulSoup 对象包含了一个值为 “[document]” 的特殊属性 .namesoup.name # '[document]'
Tag Tag对象与XML或HTML原生文档中的tag相同, 是最基本的信息组织单元. Tag对象有两个重要的属性: name和attributes
Name 每个tag都有自己的名字,通过 .name 来获取:
1 2 3 4 5 6 7 >>> from bs4 import BeautifulSoup>>> soup = BeautifulSoup('<b class="boldest">Extremely bold</b>' )>>> tag = soup.b>>> tag<b class ="boldest" >Extremely bold</b> >>> type (tag)<class 'bs4.element.Tag' >
如果改变了tag的name,那将影响所有通过当前Beautiful Soup对象生成的HTML文档:
1 2 3 >>>tag.name = "blockquote" >>>tag <blockquote class ="boldest" >Extremely bold</blockquote>
Attributes 一个tag可能有很多个属性. tag <b class="boldest">
有一个 “class” 的属性,值为 “boldest” . tag的属性的操作方法与字典相同:tag['class'] # 'boldest'
也可以直接用’.’取属性, 比如: .attrs:tag.attrs # {'class': ['boldest']}
tag的属性可以被添加,删除或修改.
1 2 3 4 5 6 7 8 9 10 11 12 >>>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' ] KeyError: 'class' >>>print (tag.get('class' )) None
NavigableString 字符串常被包含在tag内.Beautiful Soup用 NavigableString 类来包装tag中的字符串:
1 2 3 4 >>> tag.string'Extremely bold' >>> type (tag.string)<class 'bs4.element.NavigableString' >
tag中包含的字符串不能编辑,但是可以被替换成其它的字符串,用 replace_with() 方法:
1 2 3 4 >>> tag.string.replace_with("No longer bold" )'Extremely bold' >>> tag<blockquote class ="boldest" >No longer bold</blockquote>
NavigableString 对象支持 遍历文档树 和 搜索文档树 中定义的大部分属性, 并非全部.尤其是,一个字符串不能包含其它内容(tag能够包含字符串或是其它tag),字符串不支持 .contents 或 .string 属性或 find() 方法.
BeautifulSoup, Tag, NavigableString几乎覆盖了html和xml中的所有内容, 但是还有一些特殊对象. 容易让人担心的内容是文档的注释部分:
1 2 3 4 5 >>>markup = "<b><!--Hey, buddy. Want to buy a used parser?--></b>" >>>soup = BeautifulSoup(markup) >>>comment = soup.b.string >>>type (comment) <class 'bs4.element.Comment' >
Comment 对象是一个特殊类型的 NavigableString 对象: 但是当它出现在HTML文档中时, Comment 对象会使用特殊的格式输出:
1 2 3 4 >>>print (soup.b.prettify()) <b> <!--Hey, buddy. Want to buy a used parser?--> </b>
Beautiful Soup中定义的其它类型都可能会出现在XML的文档中: CData, ProcessingInstruction, Declaration, Doctype. 与 Comment 对象类似, 这些类都是 NavigableString 的子类, 只是添加了一些额外的方法的字符串独享. 下面是用CDATA来替代注释的例子:
1 2 3 4 5 6 7 >>>from bs4 import CData >>>cdata = CData("A CDATA block" ) >>>comment.replace_with(cdata) >>>print (soup.b.prettify()) <b> <![CDATA[A CDATA block]]> </b>
基于bs4库的HTML内容遍历方法 每一个HTML都是一颗完整的标签树, 同样也就有了下行遍历, 上行遍历和平行遍历.
下行遍历 标签数的下行遍历属性:
属性
描述
.contens
子节点的列表, 将\所有儿子节点存入列表
.children
子节点的迭代类型, 与.contens相似, 用于遍历儿子节点
.descnedants
子孙节点的迭代类型, 包含所有子孙节点, 用于循环遍历
例如:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 >>> import requests>>> from bs4 import BeautifulSoup>>> r = requests.request('GET' , 'https://zzz5.xyz' )>>> soup = BeautifulSoup(r.text)>>> tag = soup.head>>> tag.contents[<meta content="Hexo 3.8.0" name="generator" />, '\n' , <meta charset="utf-8" />, '\n' , <meta content="webkit" name="renderer" />, '\n' , <meta content="IE=edge" http-equiv="X-UA-Compatible" />, '\n' , <link href="http://www.zzz5.xyz" rel="dns-prefetch" />, '\n' , <title>zzZ5的个人博客</title>, '\n' , <meta content="width=device-width, initial-scale=1, maximum-scale=1" name="viewport" />, '\n' , <meta content="个人博客" name="description" />, '\n' , <meta content="日记" name="keywords" />, '\n' , <meta content="website" property ="og:type" />, '\n' , <meta content="zzZ5的个人博客" property ="og:title" />, '\n' , <meta content="http://www.zzz5.xyz/index.html" property ="og:url" />, '\n' , <meta content="zzZ5的个人博客" property ="og:site_name" />, '\n' , <meta content="个人博客" property ="og:description" />, '\n' , <meta content="zh-CN" property ="og:locale" />, '\n' , <meta content="summary" name="twitter:card" />, '\n' , <meta content="zzZ5的个人博客" name="twitter:title" />, '\n' , <meta content="个人博客" name="twitter:description" />, '\n' , <link href="/atom.xml" rel="alternative" title="zzZ5的个人博客" type ="application/atom+xml" />, '\n' , <link href="/assets/img/zzz5.ico" rel="icon" />, '\n' , <link href="/./main.0cf68a.css" rel="stylesheet" type ="text/css" />, '\n' , <style type ="text/css" > background: linear-gradient(200deg, }] >>> for i in tag.children: print (i) <meta content="Hexo 3.8.0" name="generator" /> <meta charset="utf-8" /> <meta content="webkit" name="renderer" /> <meta content="IE=edge" http-equiv="X-UA-Compatible" /> <link href="http://www.zzz5.xyz" rel="dns-prefetch" /> <title>zzZ5的个人博客</title> <meta content="width=device-width, initial-scale=1, maximum-scale=1" name="viewport" /> <meta content="个人博客" name="description" /> <meta content="日记" name="keywords" /> <meta content="website" property ="og:type" /> <meta content="zzZ5的个人博客" property ="og:title" /> <meta content="http://www.zzz5.xyz/index.html" property ="og:url" /> <meta content="zzZ5的个人博客" property ="og:site_name" /> <meta content="个人博客" property ="og:description" /> <meta content="zh-CN" property ="og:locale" /> <meta content="summary" name="twitter:card" /> <meta content="zzZ5的个人博客" name="twitter:title" /> <meta content="个人博客" name="twitter:description" /> <link href="/atom.xml" rel="alternative" title="zzZ5的个人博客" type ="application/atom+xml" /> <link href="/assets/img/zzz5.ico" rel="icon" /> <link href="/./main.0cf68a.css" rel="stylesheet" type ="text/css" /> <style type ="text/css" > background: linear-gradient(200deg, } </style>
上行遍历 标签数的上行遍历属性:
属性
描述
.parent
节点的父亲标签
.parents
节点的先辈的迭代标签, 用于循环遍历先辈节点
方法和下行遍历相同, 此处就不举例子了. 需要注意一点, soup 的 parent 为空.
平行遍历 标签数的平行遍历属性:
属性
描述
.next_sibling
返回按照HTML文本顺序的下一个平行节点标签
.previous_sibling
返回按照HTML文本顺序的上一个平行节点标签
.next_siblings
迭代类型, 返回按照HTML文本顺序的后续所有平行节点标签
.previous_sibling
迭代类型, 返回按照HTML文本顺序的前序所有平行节点标签
使用方法和上行下行遍历相同, 此处就不举例子了.
基于bs4库的HTML格式化 使用 pretiffy() 方法将HTML格式化. 例如:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 >>> soup.prettify<bound method Tag.prettify of <!DOCTYPE html> <html> <head><meta content="Hexo 3.8.0" name="generator" /> <meta charset="utf-8" /> <meta content="webkit" name="renderer" /> <meta content="IE=edge" http-equiv="X-UA-Compatible" /> <link href="http://www.zzz5.xyz" rel="dns-prefetch" /> <title>zzZ5的个人博客</title> <meta content="width=device-width, initial-scale=1, maximum-scale=1" name="viewport" /> <meta content="个人博客" name="description" /> <meta content="日记" name="keywords" /> <meta content="website" property ="og:type" /> <meta content="zzZ5的个人博客" property ="og:title" /> <meta content="http://www.zzz5.xyz/index.html" property ="og:url" /> <meta content="zzZ5的个人博客" property ="og:site_name" /> <meta content="个人博客" property ="og:description" /> <meta content="zh-CN" property ="og:locale" /> <meta content="summary" name="twitter:card" /> <meta content="zzZ5的个人博客" name="twitter:title" /> <meta content="个人博客" name="twitter:description" /> <link href="/atom.xml" rel="alternative" title="zzZ5的个人博客" type ="application/atom+xml" /> <link href="/assets/img/zzz5.ico" rel="icon" /> <link href="/./main.0cf68a.css" rel="stylesheet" type ="text/css" /> <style type ="text/css" > background: linear-gradient(200deg, } </style> </head></html> >
基于bs4库的HTML内容查找方法
find_all(name, attrs, recursive, text, **kwargs)
name : 查找所有名字为 name 的tag,
keyword : 如果一个指定名字的参数不是搜索内置的参数名,搜索时会把该参数当作指定名字tag的属性来搜索,如果包含一个名字为 id 的参数,Beautiful Soup会搜索每个tag的’id’属性.
recursive : 调用tag的 find_all() 方法时, Beautiful Soup会检索当前tag的所有子孙节点,如果只想搜索tag的直接子节点, 可以使用参数 recursive=False.
attrs : 参数定义一个字典参数来搜索包含特殊属性的tag: findall(attrs={“data-foo”: “value”})
text : 参数可以搜索文档中的字符串内容.与 name 参数的可选值一样, text 参数接受字符串, 正则表达式, 列表, True.
**kwargs : 可选参数.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 >>> for tag in soup.find_all(True ): print (tag.name) html head meta meta meta meta link title meta meta meta meta meta meta meta meta meta meta meta meta link link link style >>> soup.find_all('title' )[<title>zzZ5的个人博客</title>] >>> soup.find_all(content='个人博客' )[<meta content="个人博客" name="description" />, <meta content="个人博客" property ="og:description" />, <meta content="个人博客" name="twitter:description" />] >>>import re >>> soup.find_all(content=re.compile ('个人' ))[<meta content="个人博客" name="description" />, <meta content="zzZ5的个人博客" property ="og:title" />, <meta content="zzZ5的个人博客" property ="og:site_name" />, <meta content="个人博客" property ="og:description" />, <meta content="zzZ5的个人博客" name="twitter:title" />, <meta content="个人博客" name="twitter:description" />] >>> soup.find_all(text=re.compile ('zzZ5' ))['zzZ5的个人博客' ]
值得一提的是: <tag>(…) 等价于 <tag>.find_all(…) soup(…) 等价于 soup.find_all(…)
按照CSS类名搜索tag的功能非常实用, 但标识CSS类名的关键字 class 在Python中是保留字,使用 class 做参数会导致语法错误.从Beautiful Soup的4.1.1版本开始,可以通过 class_ 参数搜索有指定CSS类名的tag:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 >>>oup.find_all("p" , class_="etitle" ) >>>soup.find_all(class_=re.compile ("eti" )) >>>def has_six_characters (css_class ): >>> return css_class is not None and len (css_class) == 6 >>>soup.find_all(class_=has_six_characters) >>>css_soup = BeautifulSoup('<p class="body strikeout"></p>' ) >>>css_soup.find_all("p" , class_="strikeout" ) [<p class ="body strikeout" ></p>] >>>css_soup.find_all("p" , class_="body" ) [<p class ="body strikeout" ></p>] >>>css_soup.find_all("p" , class_="body strikeout" ) [<p class ="body strikeout" ></p>] soup.find_all("P" , attrs={"class" : "body strikeout" }) [<p class ="body strikeout" ></p>]
find_all() 方法还有七个扩展方法:
方法
描述
find()
搜索且只返回一个结果, 字符串类型, 同find_all()参数
find_parents()
在先辈节点中搜索, 返回列表类型, 同find_all()参数
find_parent()
在先辈节点中搜索且只返回一个结果, 字符串类型, 同find_all()参数
find_next_siblings()
在后续平行节点中搜索, 返回列表类型, 同find_all()参数
find_next_sibling()
在后续平行节点中搜索且只返回一个结果, 字符串类型, 同find_all()参数
find_previous_siblings()
在前序平行节点中搜索, 返回列表类型, 同find_all()参数
find_previous_sibling()
在前续平行节点中搜索且只返回一个结果, 字符串类型, 同find_all()参数