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切片用法实例教程
Sep 08 Python
Python 抓取动态网页内容方案详解
Dec 25 Python
在Python中处理字符串之isdigit()方法的使用
May 18 Python
python链接Oracle数据库的方法
Jun 28 Python
Python下Fabric的简单部署方法
Jul 14 Python
分析python切片原理和方法
Dec 19 Python
python list元素为tuple时的排序方法
Apr 18 Python
python3.6.3转化为win-exe文件发布的方法
Oct 31 Python
PyQt 实现使窗口中的元素跟随窗口大小的变化而变化
Jun 18 Python
python监控进程状态,记录重启时间及进程号的实例
Jul 15 Python
Django 导出项目依赖库到 requirements.txt过程解析
Aug 23 Python
python操作toml文件的示例代码
Nov 27 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中jsonp的跨域实例
2013/06/21 PHP
一个php短网址的生成代码(仿微博短网址)
2014/05/07 PHP
使用新浪微博API的OAuth认证发布微博实例
2015/03/27 PHP
一段利用WSH获取登录时间的jscript代码
2008/05/11 Javascript
jquery选择器大全 全面详解jquery选择器
2014/03/06 Javascript
jquery队列queue与原生模仿其实现方法分享
2014/03/25 Javascript
jQuery实现定时读取分析xml文件的方法
2015/07/16 Javascript
原生JS实现平滑回到顶部组件
2016/03/16 Javascript
分分钟玩转Vue.js组件
2016/10/25 Javascript
详解js中==与===的区别
2017/01/08 Javascript
MUI 上拉刷新/下拉加载功能实例代码
2017/04/13 Javascript
微信小程序request出现400的问题解决办法
2017/05/23 Javascript
微信小程序实现图片上传功能
2018/05/28 Javascript
JQuery中queue方法用法示例
2019/01/31 jQuery
mpvue实现微信小程序快递单号查询代码
2020/04/03 Javascript
vue 根据选择的月份动态展示日期对应的星期几
2021/02/06 Vue.js
[09:37]2018DOTA2国际邀请赛寻真——不懈追梦的Team Serenity
2018/08/13 DOTA
[00:18]天涯墨客三技能展示
2018/08/25 DOTA
python教程之用py2exe将PY文件转成EXE文件
2014/06/12 Python
跟老齐学Python之网站的结构
2014/10/24 Python
Python操作RabbitMQ服务器实现消息队列的路由功能
2016/06/29 Python
分享Python开发中要注意的十个小贴士
2016/08/30 Python
Python配置mysql的教程(推荐)
2017/10/13 Python
pandas apply 函数 实现多进程的示例讲解
2018/04/20 Python
如何在django中实现分页功能
2020/04/22 Python
使用keras实现Precise, Recall, F1-socre方式
2020/06/15 Python
CSS改变网页中鼠标选中文字背景颜色例子
2014/04/23 HTML / CSS
财务主管岗位职责
2014/02/28 职场文书
公司庆典欢迎词
2015/01/26 职场文书
2015年社区精神文明工作总结
2015/05/26 职场文书
在校学生证明格式
2015/06/24 职场文书
酒吧七夕情人节宣传语
2015/11/24 职场文书
解决Tkinter中button按钮未按却主动执行command函数的问题
2021/05/23 Python
pandas中DataFrame重置索引的几种方法
2021/05/24 Python
Java实现注册登录跳转
2022/06/16 Java/Android
Go微服务项目配置文件的定义和读取示例详解
2022/06/21 Golang