Python下利用BeautifulSoup解析HTML的实现


Posted in Python onJanuary 17, 2020

摘要

Beautiful Soup 是一个可以从 HTML 或 XML 格式文件中提取数据的 Python 库,他可以将HTML 或 XML 数据解析为Python 对象,以方便通过Python代码进行处理。

文档环境

  • Centos7.5
  • Python2.7
  • BeautifulSoup4

Beautifu Soup 使用说明

Beautiful Soup 的基本功能就是对HTML的标签进行查找及编辑。

基本概念-对象类型

Beautiful Soup 将复杂 HTML 文档转换成一个复杂的树形结构,每个节点都被转换成一个Python 对象,Beautiful Soup将这些对象定义了4 种类型: Tag、NavigableString、BeautifulSoup、Comment 。

对象类型 描述
BeautifulSoup 文档的全部内容
Tag HTML的标签
NavigableString 标签包含的文字
Comment 是一种特殊的NavigableString类型,当标签中的NavigableString 被注释时,则定义为该类型

安装及引用

# Beautiful Soup
pip install bs4

# 解析器
pip install lxml
pip install html5lib
# 初始化
from bs4 import BeautifulSoup

# 方法一,直接打开文件
soup = BeautifulSoup(open("index.html"))

# 方法二,指定数据
resp = "<html>data</html>"
soup = BeautifulSoup(resp, 'lxml')

# soup 为 BeautifulSoup 类型对象
print(type(soup))

标签搜索及过滤

基本方法

标签搜索有find_all() 和find() 两个基本的搜索方法,find_all() 方法会返回所有匹配关键字的标签列表,find()方法则只返回一个匹配结果。

soup = BeautifulSoup(resp, 'lxml')

# 返回一个标签名为"a"的Tag
soup.find("a")

# 返回所有tag 列表
soup.find_all("a")

## find_all方法可被简写
soup("a")

#找出所有以b开头的标签
for tag in soup.find_all(re.compile("^b")):
  print(tag.name)

#找出列表中的所有标签
soup.find_all(["a", "p"])

# 查找标签名为p,class属性为"title"
soup.find_all("p", "title")

# 查找属性id为"link2"
soup.find_all(id="link2")

# 查找存在属性id的
soup.find_all(id=True)

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

# 
soup.find_all(attrs={"data-foo": "value"})

#查找标签文字包含"sisters"
soup.find(string=re.compile("sisters"))

# 获取指定数量的结果
soup.find_all("a", limit=2)

# 自定义匹配方法
def has_class_but_no_id(tag):
  return tag.has_attr('class') and not tag.has_attr('id')
soup.find_all(has_class_but_no_id)

# 仅对属性使用自定义匹配方法
def not_lacie(href):
    return href and not re.compile("lacie").search(href)
soup.find_all(href=not_lacie)

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

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

扩展方法

ind_parents() 所有父辈节点
find_parent() 第一个父辈节点
find_next_siblings() 之后的所有兄弟节点
find_next_sibling() 之后的第一个兄弟节点
find_previous_siblings() 之前的所有兄弟节点
find_previous_sibling() 之前的第一个兄弟节点
find_all_next() 之后的所有元素
find_next() 之后的第一个元素
find_all_previous() 之前的所有元素
find_previous() 之前的第一个元素

CSS选择器

Beautiful Soup支持大部分的CSS选择器 http://www.w3.org/TR/CSS2/selector.html, 在 Tag 或 BeautifulSoup 对象的 .select() 方法中传入字符串参数, 即可使用CSS选择器的语法找到tag。

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" rel="external nofollow" class="sister" id="link1">Elsie</a>,
  <a href="http://example.com/lacie" rel="external nofollow" class="sister" id="link2">Lacie</a>
  and
  <a href="http://example.com/tillie" rel="external nofollow" class="sister" id="link3">Tillie</a>;
  and they lived at the bottom of a well.
 </p>

 <p class="story">...</p>
"""

soup = BeautifulSoup(html_doc)

# 所有 a 标签
soup.select("a")

# 逐层查找
soup.select("body a")
soup.select("html head title")

# tag标签下的直接子标签
soup.select("head > title")
soup.select("p > #link1")

# 所有匹配标签之后的兄弟标签
soup.select("#link1 ~ .sister")

# 匹配标签之后的第一个兄弟标签
soup.select("#link1 + .sister")

# 根据calss类名
soup.select(".sister")
soup.select("[class~=sister]")

# 根据ID查找
soup.select("#link1")
soup.select("a#link1")

# 根据多个ID查找
soup.select("#link1,#link2")

# 根据属性查找
soup.select('a[href]')

# 根据属性值查找
soup.select('a[href^="http://example.com/"]')
soup.select('a[href$="tillie"]')
soup.select('a[href*=".com/el"]')

# 只获取一个匹配结果
soup.select(".sister", limit=1)

# 只获取一个匹配结果
soup.select_one(".sister")

标签对象方法

标签属性

soup = BeautifulSoup('<p class="body strikeout" id="1">Extremely bold</p><p class="body strikeout" id="2">Extremely bold2</p>')
# 获取所有的 p标签对象
tags = soup.find_all("p")
# 获取第一个p标签对象
tag = soup.p
# 输出标签类型 
type(tag)
# 标签名
tag.name
# 标签属性
tag.attrs
# 标签属性class 的值
tag['class']
# 标签包含的文字内容,对象NavigableString 的内容
tag.string

# 返回标签内所有的文字内容
for string in tag.strings:
  print(repr(string))

# 返回标签内所有的文字内容, 并去掉空行
for string in tag.stripped_strings:
  print(repr(string))

# 获取到tag中包含的所有及包括子孙tag中的NavigableString内容,并以Unicode字符串格式输出
tag.get_text()
## 以"|"分隔
tag.get_text("|")
## 以"|"分隔,不输出空字符
tag.get_text("|", strip=True)
获取子节点
tag.contents # 返回第一层子节点的列表
tag.children # 返回第一层子节点的listiterator 对象
for child in tag.children:
  print(child)

tag.descendants # 递归返回所有子节点
for child in tag.descendants:
  print(child)

获取父节点

tag.parent # 返回第一层父节点标签
tag.parents # 递归得到元素的所有父辈节点

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

获取兄弟节点

# 下一个兄弟元素
tag.next_sibling 

# 当前标签之后的所有兄弟元素
tag.next_siblings
for sibling in tag.next_siblings:
  print(repr(sibling))

# 上一个兄弟元素
tag.previous_sibling

# 当前标签之前的所有兄弟元素
tag.previous_siblings
for sibling in tag.previous_siblings:
  print(repr(sibling))

元素的遍历

Beautiful Soup中把每个tag定义为一个“element”,每个“element”,被自上而下的在HTML中排列,可以通过遍历命令逐个显示标签

# 当前标签的下一个元素
tag.next_element

# 当前标签之后的所有元素
for element in tag.next_elements:
  print(repr(element))

# 当前标签的前一个元素
tag.previous_element
# 当前标签之前的所有元素
for element in tag.previous_elements:
  print(repr(element))

修改标签属性

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

tag.name = "blockquote"
tag['class'] = 'verybold'
tag['id'] = 1

tag.string = "New link text."
print(tag)

修改标签内容(NavigableString)

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

添加标签内容(NavigableString)

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

# 或者

new_string = NavigableString("Bar")
tag.append(new_string)
print(tag)

添加注释(Comment)

注释是一个特殊的NavigableString 对象,所以同样可以通过append() 方法进行添加。

from bs4 import Comment
soup = BeautifulSoup("<a>Foo</a>")
new_comment = soup.new_string("Nice to see you.", Comment)
tag.append(new_comment)
print(tag)

添加标签(Tag)

添加标签方法有两种,一种是在指定标签的内部添加(append方法),另一种是在指定位置添加(insert、insert_before、insert_after方法)

append方法

soup = BeautifulSoup("<b></b>")
tag = soup.b
new_tag = soup.new_tag("a", href="http://www.example.com" rel="external nofollow" )
new_tag.string = "Link text."
tag.append(new_tag)
print(tag)

* insert方法,是指在当前标签子节点列表的指定位置插入对象(Tag或NavigableString)

html = '<b><a href="http://example.com/" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" >I linked to <i>example.com</i></a></b>'
soup = BeautifulSoup(html)
tag = soup.a
tag.contents
tag.insert(1, "but did not endorse ")
tag.contents

insert_before() 和 insert_after() 方法则在当前标签之前或之后的兄弟节点添加元素

html = '<b><a href="http://example.com/" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" >I linked to <i>example.com</i></a></b>'
soup = BeautifulSoup(html)
tag = soup.new_tag("i")
tag.string = "Don't"
soup.b.insert_before(tag)
soup.b

* wrap() 和 unwrap()可以对指定的tag元素进行包装或解包,并返回包装后的结果。

```python
# 添加包装
soup = BeautifulSoup("<p>I wish I was bold.</p>")
soup.p.string.wrap(soup.new_tag("b"))
#输出 <b>I wish I was bold.</b>

soup.p.wrap(soup.new_tag("div"))
#输出 <div><p><b>I wish I was bold.</b></p></div>

# 拆解包装
markup = '<a href="http://example.com/" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" >I linked to <i>example.com</i></a>'
soup = BeautifulSoup(markup)
a_tag = soup.a

a_tag.i.unwrap()
a_tag
#输出 <a href="http://example.com/" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" >I linked to example.com</a>

删除标签

html = '<b><a href="http://example.com/" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" >I linked to <i>example.com</i></a></b>'
soup = BeautifulSoup(html)
# 清楚当前标签的所有子节点
soup.b.clear()

# 将当前标签及所有子节点从soup 中移除,返回当前标签。
b_tag=soup.b.extract()
b_tag
soup

# 将当前标签及所有子节点从soup 中移除,无返回。
soup.b.decompose()

# 将当前标签替换为指定的元素
tag=soup.i
new_tag = soup.new_tag("p")
new_tag.string = "Don't"
tag.replace_with(new_tag)

其他方法

输出

# 格式化输出
tag.prettify()
tag.prettify("latin-1")
  • 使用Beautiful Soup解析后,文档都被转换成了Unicode,特殊字符也被转换为Unicode,如果将文档转换成字符串,Unicode编码会被编码成UTF-8.这样就无法正确显示HTML特殊字符了
  • 使用Unicode时,Beautiful Soup还会智能的把“引号”转换成HTML或XML中的特殊字符

文档编码

使用Beautiful Soup解析后,文档都被转换成了Unicode,其使用了“编码自动检测”子库来识别当前文档编码并转换成Unicode编码。

soup = BeautifulSoup(html)
soup.original_encoding

# 也可以手动指定文档的编码 
soup = BeautifulSoup(html, from_encoding="iso-8859-8")
soup.original_encoding

# 为提高“编码自动检测”的检测效率,也可以预先排除一些编码
soup = BeautifulSoup(markup, exclude_encodings=["ISO-8859-7"])
通过Beautiful Soup输出文档时,不管输入文档是什么编码方式,默认输出编码均为UTF-8编码
文档解析器
Beautiful Soup目前支持, “lxml”, “html5lib”, 和 “html.parser”

soup=BeautifulSoup("<a><b /></a>")
soup
#输出: <html><body><a><b></b></a></body></html>
soup=BeautifulSoup("<a></p>", "lxml")
soup
#输出: <html><body><a></a></body></html>
soup=BeautifulSoup("<a></p>", "html5lib")
soup
#输出: <html><head></head><body><a><p></p></a></body></html>
soup=BeautifulSoup("<a></p>", "html.parser")
soup
#输出: <a></a>

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

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Python 相关文章推荐
讲解Python中for循环下的索引变量的作用域
Apr 15 Python
Python中用于转换字母为小写的lower()方法使用简介
May 19 Python
Python实现合并字典的方法
Jul 07 Python
python字符串的方法与操作大全
Jan 30 Python
为什么选择python编程语言入门黑客攻防 给你几个理由!
Feb 02 Python
浅谈python中字典append 到list 后值的改变问题
May 04 Python
python numpy 按行归一化的实例
Jan 21 Python
Python之NumPy(axis=0 与axis=1)区分详解
May 27 Python
pandas按行按列遍历Dataframe的几种方式
Oct 23 Python
TensorFlow tf.nn.conv2d实现卷积的方式
Jan 03 Python
scrapy实践之翻页爬取的实现
Jan 05 Python
python自动化调用百度api解决验证码
Apr 13 Python
pytorch forward两个参数实例
Jan 17 #Python
Python实现CNN的多通道输入实例
Jan 17 #Python
Python面向对象编程基础实例分析
Jan 17 #Python
通过python实现windows桌面截图代码实例
Jan 17 #Python
PyTorch加载预训练模型实例(pretrained)
Jan 17 #Python
python 正则表达式参数替换实例详解
Jan 17 #Python
Python函数式编程实例详解
Jan 17 #Python
You might like
php中注册器模式类用法实例分析
2015/11/03 PHP
深入理解PHP之OpCode原理详解
2016/06/01 PHP
详解PHP PDO简单教程
2019/05/28 PHP
在b/s开发中经常用到的javaScript技术
2006/08/23 Javascript
IE6下JS动态设置图片src地址问题
2010/01/08 Javascript
CSS和JS标签style属性对照表(方便js开发的朋友)
2010/11/11 Javascript
实现web打印的各种方法介绍及实现代码
2013/01/09 Javascript
JavaScript核心语法总结(推荐)
2016/06/02 Javascript
把多个JavaScript函数绑定到onload事件处理函数上的方法
2016/09/04 Javascript
让DIV的滚动条自动滚动到最底部的3种方法(推荐)
2016/09/24 Javascript
详解windows下vue-cli及webpack 构建网站(二)导入bootstrap样式
2017/06/17 Javascript
Element Input组件分析小结
2018/10/11 Javascript
[06:40]2014DOTA2西雅图国际邀请赛 DK战队巡礼
2014/07/07 DOTA
[45:50]完美世界DOTA2联赛PWL S3 CPG vs Forest 第二场 12.16
2020/12/17 DOTA
[46:42]DOTA2-DPC中国联赛正赛 Aster vs Magma BO3 第二场 3月5日
2021/03/11 DOTA
python正则表达式修复网站文章字体不统一的解决方法
2013/02/21 Python
Python-基础-入门 简介
2014/08/09 Python
Python中使用第三方库xlrd来读取Excel示例
2015/04/05 Python
Python编程之黑板上排列组合,你舍得解开吗
2017/10/30 Python
python opencv之分水岭算法示例
2018/02/24 Python
python中ASCII码字符与int之间的转换方法
2018/07/09 Python
python3爬虫获取html内容及各属性值的方法
2018/12/17 Python
int在python中的含义以及用法
2019/06/27 Python
python输入错误后删除的方法
2019/10/12 Python
Python collections.defaultdict模块用法详解
2020/06/18 Python
用Python制作mini翻译器的实现示例
2020/08/17 Python
弄清Pytorch显存的分配机制
2020/12/10 Python
系统管理员的职责包括那些?管理的对象是什么?
2013/01/18 面试题
教学器材管理制度
2014/01/26 职场文书
优秀志愿者事迹材料
2014/02/03 职场文书
绩效管理实施方案
2014/03/19 职场文书
信用卡工作证明模板
2014/09/14 职场文书
工作会议通知
2015/04/15 职场文书
产品调价通知函
2015/04/20 职场文书
2015年度对口支援工作总结
2015/07/22 职场文书
幼儿园小班教学反思
2016/03/03 职场文书