文章目录
BeautifulSoup是一个用于解析HTML和XML文档的Python库,它能够方便地从网页中提取数据。
一、基本介绍
1.BeautifulSoup类源码:
def __init__(self, markup="", features=None, builder=None,
parse_only=None, from_encoding=None, exclude_encodings=None,
element_classes=None, **kwargs):
其中传入的参数说明:
-
1:param markup: 一个字符串或类似文件的对象,表示要解析的标记内容。
-
2:param features: 解析器应具备的理想特性。这可以是特定解析器的名称(“lxml”、“lxml-xml”、“html.parser"或"html5lib”)或者是要使用的标记类型(“html”、“html5”、“xml”)。建议指定一个具体的解析器名称,以便Beautiful Soup在不同平台和虚拟环境中给出一致的结果。
-
3:param builder: 用于实例化的TreeBuilder子类(或直接使用的实例),而不是根据
features
自动查找。只有当你实现了自定义的TreeBuilder时,才需要使用此参数。 -
4:param parse_only: 一个SoupStrainer对象。仅文档中与SoupStrainer匹配的部分会被考虑。当解析的文档部分过大以至于无法一次性载入内存时,这一点尤为有用。
-
5:param from_encoding: 表示待解析文档编码的字符串。如果Beautiful Soup错误地猜测了文档的编码,可以传递此参数。
-
6:param exclude_encodings: 一个字符串列表,指示已知错误的编码。如果你不知道文档的确切编码,但确定Beautiful Soup的猜测是错误的,可以传递此参数。
-
7:param element_classes: 一个字典,映射BeautifulSoup类(如Tag和NavigableString)到你在构建解析树时希望替代实例化的其他类。这对于通过子类化Tag或NavigableString来修改默认行为非常有用。
-
8:param kwargs: 为了向后兼容,构造函数接受某些Beautiful Soup 3中使用的关键字参数。在Beautiful Soup 4中,这些参数不做任何事情;它们将导致警告并随后被忽略。
其中不同的解析器如 “lxml”、“lxml-xml”、“html.parser” 和 “html5lib” 提供了不同的特性和性能,适用于不同场景的HTML和XML解析任务。
- lxml:
基于C库libxml2和libxslt,提供了非常高的解析速度和效率。
支持XPath,这使得通过路径表达式快速定位和提取文档中的元素变得容易。
既可以用于解析HTML也可以解析XML,当指定为 “lxml” 时,它通常会自动适应文档类型。
相对于纯Python实现的解析器,lxml需要安装额外的依赖,但在性能上通常有显著优势。 - lxml-xml:
与 lxml 类似,但明确指定用于XML解析,意味着它将严格遵守XML的语法规则,对于不规范的HTML可能不如"lxml"宽容。
适合那些需要精确XML解析的场景,比如处理RSS feeds或XML API响应。 - html.parser (Python标准库解析器):
Python自带的HTML解析器,不需要额外安装。
性能和内存消耗方面通常不如 lxml,但对于简单和中等复杂度的任务来说足够使用。
不支持XPath,但提供了基于方法和属性的简单API来遍历和搜索文档。
对于不严格的HTML(即现实世界中的网页代码)有较好的容错性。 - html5lib:
力求实现与现代浏览器相同的解析算法,这意味着它能非常宽容地解析不规范的HTML,产生与浏览器渲染相同的DOM树。
不像其他解析器那样高效,特别是在处理大型文档时可能会很慢,但它提供了最好的兼容性和一致性。
支持类似于jQuery的CSS选择器,使得选择元素更加直观。
需要额外安装。
二、解析html
from bs4 import BeautifulSoup
import requests
url = 'https://www.baidu.com/'
page = requests.get(url)
html_content = page.text
soup = BeautifulSoup(html_content, 'html.parser')
soup就返回了一个使用html.parser解析的对象, 可以使用提供的方法提取需要的信息
三、BeautifulSoup方法提取html信息
1. find()
用于在文档中查找第一个匹配指定条件的 Tag 或 NavigableString 对象, find_all 方法的单个匹配版本.
def find(self, name=None, attrs={
}, recursive=True, string=None,
**kwargs):
find_xxx都存在通用参数:
- param name: 标签名的过滤器。
- param attrs: 一个字典,用于对属性值进行过滤,例如,{‘class’: ‘myClass’} 会查找具有 class=“myClass” 属性的标签。
- param recursive: 如果为True,find_all()将对这个PageElement的子元素进行递归搜索。否则,仅考虑直接子元素。
- param limit: 找到这么多结果后停止搜索。
- kwargs: 一个字典,用于对属性值进行过滤(与attrs参数类似,但此处允许使用其他关键字参数)。
- return: 一个PageElements的ResultSet。
- rtype: bs4.element.ResultSet
comments = soup.find_all('div', class_='comment_items')
for comment in comments:
describe_detail = comment.find('div', class_='starline').find('span')
describe = describe_detail.text
2. findall()
用于查找文档中满足指定条件的所有标签。
def find_all(self, name=None, attrs={
}, recursive=True, string=None,
limit=None, **kwargs):
查询标签
atitles = soup.find_all('a') # 查找所有<a>标签
for title in atitles:
print(title.text) # 打印标题文本
dtitles = soup.find_all('div') # 查找所有<div>标签
for title in dtitles:
print(title.text) # 打印标题文本
返回的是页面元素PageElement类, 可以使用其提供的方法解析出需要的内容
四、PageElement方法
1. find_parent
用途: 返回元素的父元素。
用法示例:
parent_tag = tag.find_parent
2. find_children
用途: 提供一个迭代器,可以遍历该元素的所有直接子节点(包括文本和标签)。
用法示例:
for child in tag.find_children:
print(child)
3. next_sibling 和 .previous_sibling
用途: 分别返回当前元素之后和之前的兄弟节点(可以是文本或标签)。
用法示例:
next_element = tag.next_sibling
prev_element = tag.previous_sibling
4. next_siblings 和 .previous_siblings
用途: 提供迭代器,分别遍历当前元素之后和之前的全部兄弟节点。
用法示例:
for sibling in tag.next_siblings:
print(sibling)
5. find_all, .find, .select 等
用途: 虽然这些方法主要通过BeautifulSoup类直接调用,但它们也可以通过任何PageElement实例调用来在该元素的后代中进行搜索。
用法示例:
sub_tags = tag.find_all_next('span')
五、案例:
从当当网获取某书的书评,并提取出需要的信息
url = f'https://product.dangdang.com/index.php?xxx'
web = requests.get(url, headers=headers)
json_dd = web.text
parse_json = json.loads(json_dd)
html = list['html']
# 使用BeautifulSoup解析HTML
soup = BeautifulSoup(html, 'html.parser')
# 提取所需信息
comments = soup.find_all('div', class_='comment_items')
for comment in comments:
# 提取class=star_box内的10分
star_box = comment.find('span', class_='star_box').find_next_sibling('em')
score = star_box.text
print(f"评分: {
star_box.text}")
# 提取class=describe_detail的评论信息
describe_detail = comment.find('div', class_='describe_detail').find('span')
describe = describe_detail.text
print(f"评论: {
describe_detail.text}")
# 提取class=starline的时间
starline_time = comment.find('div', class_='starline').find('span')
starline = starline_time.text
print(f"时间: {
starline_time.text}")
# 提取class=name的title内容
name_title = comment.find('div', class_='items_left_pic').find('span', class_='name')
print(f"用户名: {
name_title.get('title')} ({
name_title.text})")
name = name_title.get('title')
# 提取class=level的内容
user_level = comment.find('div', class_='items_left_pic').find('span', class_='level')
level = user_level.text
print(f"等级: {
user_level.text}\n")
结果:
# 评分: 10分
# 评论: 啊啊绝对是值得一看的书。这句“为你 千千万万遍”一开始就很喜欢 看完以后觉得虐心 这辈子一定要看看追风筝的人
# 时间: 2018-12-09 21:57:13
# 用户名: xxx
# 等级: 普通会员
#
# 评分: 10分
# 评论: 书很好,值得一读,他让我看到了战时阿富汗的残酷,无数阿富汗的人民、孩子在战争、压迫之下无家可归,同时,书中情节跌宕起伏……多年后,“我”再次回到故土,才知道一个惊天谎言,“我”又是如何面对现实呢……正如电话中拉辛说——这是一次重新变成好人的机会,是一次弥补爸爸和“我”过错的机会,是一次救赎……
# 时间: 2022-07-25 00:04:53
# 用户名: xxx
# 等级: 钻石会员
#
# 评分: 10分
# 评论: 没有虚娇赘文,没有无病呻吟,只有精炼短文,阿米尔与他父亲仆人儿子哈桑的亲密友谊成为贯穿全书脉络,。这不仅是一部政治史诗,也是一个关于童年选择如何影响我们成年生活极度贴近人性故事。让人魂牵梦萦。
# 时间: 2018-10-01 20:26:34
# 用户名: xxx
# 等级: 普通会员
文章评论