当前位置:

首页 > 编程开发 > Python中如何使用Beautiful Soup(BS4)库来解析HTML和XML?

Python中如何使用Beautiful Soup(BS4)库来解析HTML和XML?

一、BeautifulSoup概述:BeautifulSoup支持从HTML或XML文件中提取数据的Python库;它支持Python标准库中的HTML解析器,还支持一些第三方的解析器lxml。BeautifulSoup自动将输入文档转换为Unicode编码,输出文档转换为utf-8编码。安装:pipinstallbeautifulsoup4可选择安装解析器pipinstalllxmlpipinstallhtml5lib二、BeautifulSoup4简单使用假设有这样一个Html,具体内容如下:百度一下

一、Beautiful Soup概述:

Beautiful Soup支持从HTML或XML文件中提取数据的Python库;

它支持Python标准库中的HTML解析器,还支持一些第三方的解析器lxml。

Beautiful Soup自动将输入文档转换为Unicode编码,输出文档转换为utf-8编码。

安装:

pip install beautifulsoup4

可选择安装解析器

  • pip install lxml

  • pip install html5lib

二、BeautifulSoup4简单使用

假设有这样一个Html,具体内容如下:




    
    
    
    
    百度一下,你就知道 


  
    
        
          
            新闻 
            hao123 
            地图 
            视频 
            贴吧 
            更多产品 
          
        
    
  

创建beautifulsoup4对象:

from bs4 import BeautifulSoup

file = open('./aa.html', 'rb')
html = file.read()
bs = BeautifulSoup(html, "html.parser")  # 缩进格式

print(bs.prettify())  # 格式化html结构
print(bs.title)  # 
print(bs.title.name)  # 获取title标签的名称 :title
print(bs.title.string)  # 获取title标签的文本内容 :   百度一下,你就知道
print(bs.head)  # 获取head标签的所有内容 :
print(bs.div)  # 获取第一个div标签中的所有内容   :
print(bs.div["id"])  # 获取第一个div标签的id的值      :    wrapper
print(bs.a)  # 获取第一个a标签中的所有内容    :       新闻 
print(bs.find_all("a"))  # 获取所有的a标签中的所有内容     :   [....]
print(bs.find(id="u1"))  # 获取id="u1"的所有内容 :
for item in bs.find_all("a"):  # 获取所有的a标签,并遍历打印a标签中的href的值    :
    print(item.get("href"))
for item in bs.find_all("a"):  # 获取所有的a标签,并遍历打印a标签的文本值:
    print(item.get_text())

三、BeautifulSoup4四大对象种类

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

1、Tag:标签

Tag通俗点讲就是HTML中的一个个标签,例如:

print(bs.title) # 获取title标签的所有内容
print(bs.head) # 获取head标签的所有内容
print(bs.a) # 获取第一个a标签的所有内容
print(type(bs.a))# 类型

我们可以利用 soup 加标签名轻松地获取这些标签的内容,这些对象的类型是bs4.element.Tag。但是注意,它查找的是在所有内容中的第一个符合要求的标签。

对于 Tag,它有两个重要的属性,是 name 和 attrs:

print(bs.name) # [document] #bs 对象本身比较特殊,它的 name 即为 [document]
print(bs.head.name) # head #对于其他内部标签,输出的值便为标签本身的名称
print(bs.a.attrs) # 在这里,我们把 a 标签的所有属性打印输出了出来,得到的类型是一个字典。
print(bs.a['class']) ##还可以利用get方法,传入属性的名称,二者是等价的,等价 bs.a.get('class')

bs.a['class'] = "newClass"# 可以对这些属性和内容等等进行修改
print(bs.a) 

del bs.a['class'] # 还可以对这个属性进行删除
print(bs.a)

2、NavigableString:标签内部的文字

既然我们已经得到了标签的内容,那么问题来了,我们要想获取标签内部的文字怎么办呢?很简单,用 .string 即可,例如:

print(bs.title.string)  # 百度一下,你就知道 
print(type(bs.title.string))  #

3、BeautifulSoup:文档的内容

BeautifulSoup对象表示的是一个文档的内容。大部分时候,可以把它当作 Tag 对象,是一个特殊的 Tag,我们可以分别获取它的类型,名称,以及属性,例如:

print(type(bs.name))  # 
print(bs.name)  # [document]
print(bs.attrs)  # {}

4、Comment:注释

Comment 对象是一个特殊类型的 NavigableString 对象,其输出的内容不包括注释符号。

print(bs.a)
# 此时不能出现空格和换行符,a标签如下:
# 
print(bs.a.string) # 新闻
print(type(bs.a.string)) #

四、遍历文档树所用属性

print(bs.head.contents)     # tag的.contents属性可以将tag的子节点以列表的方式输出:[...]
print(bs.head.contents[1])  # 用列表索引来获取它的某一个元素:
for child in bs.body.children:
    print(child)

五、搜索文档树

1、find_all():过滤器

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

find_all过滤器可以被用在tag的name中,节点的属性等。

(1)name参数:

字符串过滤:会查找与字符串完全匹配的内容

a_list = bs.find_all("a")
print(a_list)

正则表达式过滤:如果传入的是正则表达式,那么BeautifulSoup4会通过search()来匹配内容

import re 

t_list = bs.find_all(re.compile("a")) 
for item in t_list: 
   print(item)

列表:如果传入一个列表,BeautifulSoup4将会与列表中的任一元素匹配到的节点返回

t_list = bs.find_all(["meta","link"])
for item in t_list:
    print(item)

方法:传入一个方法,根据方法来匹配

def name_is_exists(tag): 
    return tag.has_attr("name") 
t_list = bs.find_all(name_is_exists) 
for item in t_list: 
    print(item)
(2)kwargs参数:
t_list = bs.find_all(id="head")  # 查询id=head的Tag
t_list = bs.find_all(href=re.compile(http://news.baidu.com))  # 查询href属性包含ss1.bdstatic.com的Tag
t_list = bs.find_all(class_=True) # 查询所有包含class的Tag(注意:class在Python中属于关键字,所以加_以示区别)
for item in t_list: 
    print(item)
(3)attrs参数:

并不是所有的属性都可以使用上面这种方式进行搜索,比如HTML的data-*属性:

t_list = bs.find_all(data-foo="value")

如果执行这段代码,将会报错。我们可以使用attrs参数,定义一个字典来搜索包含特殊属性的tag:

t_list = bs.find_all(attrs={"data-foo":"value"})
for item in t_list:
    print(item)
(4)text参数:

通过text参数可以搜索文档中的字符串内容,与name参数的可选值一样,text参数接受 字符串,正则表达式,列表

t_list = bs.find_all(text="hao123") 
t_list = bs.find_all(text=["hao123", "地图", "贴吧"]) 
t_list = bs.find_all(text=re.compile("\d"))

当我们搜索text中的一些特殊属性时,同样也可以传入一个方法来达到我们的目的:

def length_is_two(text):
    return text and len(text) == 2
t_list = bs.find_all(text=length_is_two)
(5)limit参数

可以传入一个limit参数来限制返回的数量,当搜索出的数据量为5,而设置了limit=2时,此时只会返回前2个数据

t_list = bs.find_all("a",limit=2)

find_all除了上面一些常规的写法,还可以对其进行一些简写:

# 下面两者是相等的
t_list = bs.find_all("a") 
t_list = bs("a") 

# 下面两者是相等的
t_list = bs.a.find_all(text="新闻") 
t_list = bs.a(text="新闻")

2、find()

find()将返回符合条件的第一个Tag,有时我们只需要或一个Tag时,我们就可以用到find()方法了。当然了,也可以使用find_all()方法,传入一个limit=1,然后再取出第一个值也是可以的,不过未免繁琐。

t_list = bs.find_all("title",limit=1) # 返回只有一个结果的列表
t = bs.find("title") # 返回唯一值
t = bs.find("abc") # 如果没有找到,则返回None

从结果可以看出find_all,尽管传入了limit=1,但是返回值仍然为一个列表,当我们只需要取一个值时,远不如find方法方便。但是如果未搜索到值时,将返回一个None。

在上面介绍BeautifulSoup4的时候,我们知道可以通过bs.div来获取第一个div标签,如果我们需要获取第一个div下的第一个div,我们可以这样:

t = bs.div.div
# 等价于
t = bs.find("div").find("div")

六、CSS选择器:select()方法

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

print(bs.select('title'))  # 1、通过标签名查找               
print(bs.select('a'))                                
print(bs.select('.mnav'))  # 2、通过类名查找                
print(bs.select('#u1'))  # 3、通过id查找                  
print(bs.select('div .bri'))  # 4、组合查找               
print(bs.select('a[class="bri"]'))  # 5、属性查找         
print(bs.select('a[href="http://tieba.baidu.com" rel="external nofollow"  rel="external nofollow" ]')) 
print(bs.select("head > title"))  # 6、直接子标签查找        
print(bs.select(".mnav ~ .bri"))  # 7、兄弟节点标签查找       
print(bs.select('title')[0].get_text())  # 8、获取内容

七、综合实例:

from bs4 import BeautifulSoup
import requests,re
req_obj = requests.get('https://www.baidu.com')
soup = BeautifulSoup(req_obj.text,'lxml')

'''标签查找'''
print(soup.title)              #只是查找出第一个
print(soup.find('title'))      #效果和上面一样
print(soup.find_all('div'))    #查出所有的div标签

'''获取标签里的属性'''
tag = soup.div
print(tag['class'])   #多属性的话,会返回一个列表
print(tag['id'])      #查找标签的id属性
print(tag.attrs)      #查找标签所有的属性,返回一个字典(属性名:属性值)

'''标签包的字符串'''
tag = soup.title
print(tag.string)                 #获取标签里的字符串
tag.string.replace_with("哈哈")    #字符串不能直接编辑,可以替换

'''子节点的操作'''
tag = soup.head
print(tag.title)     #获取head标签后再获取它包含的子标签

'''contents 和 .children'''
tag = soup.body
print(tag.contents)        #将标签的子节点以列表返回
print([child for child in tag.children])      #输出和上面一样


'''descendants'''
tag = soup.body
[print(child_tag) for child_tag in tag.descendants]    #获取所有子节点和子子节点

'''strings和.stripped_strings'''
tag = soup.body
[print(str) for str in tag.strings]             #输出所有所有文本内容
[print(str) for str in tag.stripped_strings]    #输出所有所有文本内容,去除空格或空行

'''.parent和.parents'''
tag = soup.title
print(tag.parent)                 #输出便签的父标签
[print(parent) for parent in tag.parents]  #输出所有的父标签

'''.next_siblings 和 .previous_siblings
    查出所有的兄弟节点
'''

'''.next_element 和 .previous_element
    下一个兄弟节点
'''

'''find_all的keyword 参数'''
soup.find_all(id='link2')                   #查找所有包含 id 属性的标签
soup.find_all(href=re.compile("elsie"))     #href 参数,Beautiful Soup会搜索每个标签的href属性:
soup.find_all(id=True)                       #找出所有的有id属性的标签
soup.find_all(href=re.compile("elsie"), id='link1')         #也可以组合查找
soup.find_all(attrs={"属性名": "属性值"})  #也可以通过字典的方式查找

八、BeautifulSoup 和lxml(Xpath)对比

# test.py
# -*- coding: utf-8 -*-
import requests
from bs4 import BeautifulSoup, SoupStrainer
import traceback
import json
from lxml import etree
import re
import time

def getHtmlText(url):
    try:
        r = requests.get(url, headers=headers)
        r.raise_for_status()
        if r.encoding == 'ISO-8859-1':
            r.encoding = r.apparent_encoding
        return r.text
    except:
        traceback.print_exc()

# ----------使用BeautifulSoup解析------------------------
def parseWithBeautifulSoup(html_text):
    soup = BeautifulSoup(html_text, 'lxml') 
    content = []
    for mulu in soup.find_all(class_='mulu'): # 先找到所有的 div class=mulu 标记
        # 找到div_h3 标记
        h3 = mulu.find('h3')
        if h3 != None:
            h3_title = h3.string # 获取标题
            lst = []
            for a in mulu.select('div.box a'):
                href = a.get('href') # 找到 href 属性
                box_title = a.get('title')  # 找到 title 属性
                pattern = re.compile(r'\s*\[(.*)\]\s+(.*)') # (re) 匹配括号内的表达式,也表示一个组
                match = pattern.search(box_title)
                if match != None:
                    date = match.group(1)
                    real_title = match.group(2)
                    lst.append({'href':href,'title':real_title,'date':date})
            content.append({'title':h3_title,'content':lst})
    with open('dmbj_bs.json', 'w') as fp:
        json.dump(content, fp=fp, indent=4)

# ----------使用Xpath解析------------------------
def parseWithXpath(html_text):
    html = etree.HTML(html_text)
    content = []
    for div_mulu in html.xpath('.//*[@class="mulu"]'): # 先找到所有的 div class=mulu 标记
        # 找到所有的 div_h3 标记
        div_h3 = div_mulu.xpath('./div[@class="mulu-title"]/center/h3/text()')
        if len(div_h3) > 0:
            h3_title = div_h3[0] # 获取标题
            a_s = div_mulu.xpath('./div[@class="box"]/ul/li/a')
            lst = []
            for a in a_s:
                href = a.xpath('./@href')[0] # 找到 href 属性
                box_title = a.xpath('./@title')[0] # 找到 title 属性
                pattern = re.compile(r'\s*\[(.*)\]\s+(.*)') # (re) 匹配括号内的表达式,也表示一个组
                match = pattern.search(box_title)
                if match != None:
                    date = match.group(1)
                    real_title = match.group(2)
                    lst.append({'href':href,'title':real_title,'date':date})
            content.append({'title':h3_title,'content':lst})
    with open('dmbj_xp.json', 'w') as fp:
        json.dump(content, fp=fp, indent=4)

def main():
    html_text = getHtmlText('http://www.seputu.com')
    print(len(html_text))
    start = time.clock()
    parseWithBeautifulSoup(html_text)
    print('BSoup cost:', time.clock()-start)
    start = time.clock()
    parseWithXpath(html_text)
    print('Xpath cost:', time.clock()-start)

if __name__ == '__main__':
    user_agent = 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.117 Safari/537.36'
    headers={'User-Agent': user_agent}
    main()
本文内容来源于互联网,如有侵权请联系删除。
作者最新文章
编程开发 Python
上一篇: 如何使用Redis实现接口幂等性拦截器在Spring Boot中
下一篇: Photoshop快捷键技巧教程 常用命令一览表及图解
相关文章 更多
Python安装后怎么打开:使用IDLE或命令行启动解释器
Python安装后怎么打开:使用IDLE或命令行启动解释器

刚在Windows安装好Python却不知道如何启动?本文详细演示如何通过开始菜单找到并打开IDLE集成开发环境,以及如何在PowerShell或命令提示符中使用python和py命令启动交互式解释器、运行.py脚本文件。包含退出解释器的方法及常见启动问题排查,帮助初学者快速验证安装成功并开始编写代码。

Windows系统Python安装教程:下载、勾选PATH及环境变量配置
Windows系统Python安装教程:下载、勾选PATH及环境变量配置

针对Windows初学者的Python安装实战指南。详细讲解如何从Python官网下载匹配架构的安装包,重点演示安装首屏勾选“Add python.exe to PATH”的关键操作,并提供使用python --version和py命令验证环境变量的具体步骤,帮助新手快速搭建开发环境并排查路径问题。

麒麟OS如何查看Python进程的运行状态
麒麟OS如何查看Python进程的运行状态

要想确认麒麟OS中Python程序的运行状态以及资源占用情况,我们可以这样做:用ps -ef | grep python来筛选进程;通过top命令,按P键排序查看实时负载;使用pgrep -f "script.py"精准获取PID;借助lsof -p PID验证文件打开状态。另外,还可以结合syst

Python在Debian上如何配置SSL证书
Python在Debian上如何配置SSL证书

在Debian系统上配置SSL证书通常涉及以下几个步骤:安装Web服务器:首先,你需要一个Web服务器,比如Apache或Nginx。这里以Apache为例。sudo apt updatesudo apt install apache2获取SSL证书:你可以从Let’s Encrypt免费获取SSL

统信UOS怎么安装Python开发环境
统信UOS怎么安装Python开发环境

要想让Python项目在统信UOS上正常运行,得先安装python3、python3-pip、python3-venv、python3-dev以及build-essential等组件。具体操作就是执行sudo apt install命令来一步到位完成安装,同时别忘了配置清华镜像源来给pip加速哦。在

纯Python方案实现中英文全文搜索
纯Python方案实现中英文全文搜索

在互联网上的各类网站中,无论大小,基本上都会有一个搜索框,用来给用户对内容进行搜索,小到站点搜索,大到搜索引擎搜索。从简单的来说,搜索功能确实很简单,一个简单的select语句就可以实现数据的搜索。而从复杂的来看,无论是搜索的精度还是搜索的效率,都是有很深的研究范围的。对于简单的搜索功能来说,一个s

Mac如何取消通过Python脚本运行的关机程序
Mac如何取消通过Python脚本运行的关机程序

立即在终端输入sudo shutdown -c取消倒计时关机,成功后显示“Shutdown cancelled”;若存在pmset重复任务,需再执行sudo pmset repeat cancel清除。Mac因Python脚本执行了os.system("sudo shutdown -h +10")或

PS网页版直接使用
PS网页版直接使用

PS网页版免费官方入口为https://www.adobe.com/products/photoshop/web.html,支持PSD编辑、实时协作、多色彩空间、智能抠图、AI修复、跨端同步、中文引导及SVG/PSD兼容等核心功能。 对于很多设计新手,或者只是偶尔需要处理图片的朋友来说,直接在线、免

淘宝网页版入口查找教程
淘宝网页版入口查找教程

淘宝官方网页登录入口 对于如何找到淘宝网页版的入口,很多朋友都感到有点摸不着头脑。别急,这篇文章就来为你拆解清楚整个登录流程。官方的登录入口很明确,就在官网首页的左上角。 淘宝网页版入口位于官网首页左上角,点击“亲,请登录”即可跳转至统一的验证页面。登录支持密码、短信验证码和手机APP扫码三种方式,

怎样在无忧小说网中查看阅读进度
怎样在无忧小说网中查看阅读进度

在无忧小说网,如何精准掌握你的阅读进度? 在无忧小说网追更小说,最怕的就是忘了上次读到哪儿。如果感觉界面没有清晰显示阅读进度,别急,这通常不是数据丢失,而是相关视图功能没有开启或找到。掌握下面这四条路径,你就能对阅读进度了如指掌。 一、通过阅读页面顶部状态栏查看 最简单直接的方法,其实就藏在眼皮底下

查看更多
精品专题 更多
装机必备
装机必备

正软商城装机必备专区,精选办公、浏览器、安全防护、影音播放、压缩解压、设计创作和系统工具等电脑常用正版软件,帮助用户快速完成新电脑软件配置。

Windows
Windows

正软商城Windows软件专区,汇集适用于Windows电脑的办公、设计、安全防护、影音播放、开发工具和系统优化软件,提供软件介绍、系统要求、正版授权及购买下载服务。

macOS软件
macOS软件

正软商城macOS软件专区,精选适用于Mac电脑的办公、设计、影音、效率、开发和系统工具,提供软件功能介绍、macOS兼容版本、正版授权及购买下载服务。

Mac软件 更多
灵活计算器
灵活计算器
macOS/iOS/Android

灵活计算器是一款笔记式算数应用,支持实时计算、动态关联和云端同步功能。记录、整理和输出之间的过渡会更自然,适合长期写作、做笔记或持续沉淀个人内容。

赤友清理大师
赤友清理大师
macOS

赤友清理大师是一款为 Mac 设计的智能清理优化工具,可精准扫描垃圾、大文件、重复文件等,释放磁盘空间。做扫描整理、文字提取和表格转换时,它能把识别后的处理步骤接得更顺,资料录入这类场景会省下不少时间。

极度公式
极度公式
Windows/macOS/Linux

极度公式是一款跨平台专业LaTeX公式识别编辑软件,支持OCR公式识别和多平台编辑。和使用说明,避免使用,享受完整功能与稳定支持。做扫描整理、文字提取和表格转换时,它能把识别后的处理步骤接得更顺,资料录入这类场景会省下不少时间。

WINDOWS 更多
Windows 10
Windows 10
Windows

Windows 10 是一款微软推出的经典操作系统,拥有硬件兼容性与多任务处理能力。它更偏向把系统状态查看和常用调节动作放在一起,适合需要持续观察和微调设备状态的场景。

极度公式
极度公式
Windows/macOS/Linux

极度公式是一款跨平台专业LaTeX公式识别编辑软件,支持OCR公式识别和多平台编辑。和使用说明,避免使用,享受完整功能与稳定支持。做扫描整理、文字提取和表格转换时,它能把识别后的处理步骤接得更顺,资料录入这类场景会省下不少时间。

密码键盘
密码键盘
Windows/macOS/iOS/Android

密码键盘是一款兼具安全性与便捷性的高效密码管理器。日常使用里的持续防护和信息管理会更突出,适合把安全控制放进长期使用流程中的场景。