Python爬虫html解析工具 —— BeautifulSoup官方文档简略版

BeautifulSoup官方文档地址

入门

Soup的生成

# 导包
from bs4 import BeautifulSoup

# 传入文件句柄
with open("index.html") as fp:
    soup = BeautifulSoup(fp, 'html.parser')

# 字符串
soup = BeautifulSoup("<html>a web page</html>", 'html.parser')

对象树中的四种对象

BeautifulSoup将复杂的HTML文档转换为复杂的Python对象树
但只有四种对象我们关心。

Tag

这里的标签指的就是 html 或者 xml 里面的标签,如 body、a、h 等等。
我们关心的是名称属性

# tag指某一具体标签
soup.tag
# 链式访问
soup.tag1.tag2.tag3
# 不过只能获取第一个 a 标签(可以使用后面的 find_all获取所有的a标签)
soup.a

名称

每个标签都有一个名称,可以通过.name以下方式访问:

# 名称
soup.tag.name

属性

标签可以具有任意数量的属性。
你可以通过将标签视为字典来访问标签的属性。

tag = BeautifulSoup('<b id="boldest">bold</b>', 'html.parser').b
# python的字典访问
tag['id']
# 'boldest'

# .attrs返回字典
tag.attrs
# {'id': 'boldest'}

# 判断是否拥有某个属性
tag.has_attr("id")

其实还可以修改它,但这里就不说了。

多值属性

有些属性可以有多个值。如class。
Beautiful Soup将多值属性的值显示为列表。

css_soup = BeautifulSoup('<p class="body strikeout"></p>', 'html.parser')
css_soup.p['class']
# ['body', 'strikeout']

NavigableString

其实就是在标签里面写的,我们能看到的字符串。

soup = BeautifulSoup('<b class="boldest">Extremely bold</b>', 'html.parser')
tag = soup.b
tag.string
# 'Extremely bold'
type(tag.string)
# <class 'bs4.element.NavigableString'>

BeautifulSoup

该BeautifulSoup对象代表了整个解析后的文档。对于大多数目的,您可以将其视为Tag 对象。
如 soup 就是对象树的根部tag。

Comment

似乎就是那些注释?maybe
总之,它不重要。

文档对象树的遍历

必须要说的,只有tag才能迭代遍历,string不可以,因为string没有“孩子”。

向下走

使用标签名称导航

就这么简单,直接访问就好。

# tag指某一具体标签
soup.tag
# 链式访问
soup.tag1.tag2.tag3

.contents 和 .children

一个标签的孩子列表的获取方法。
两种方法区别不大。

print(type(soup.html.children))
print(type(soup.html.contents))

# <class 'list_iterator'>
# <class 'list'>

字符串没有.contents。

.descendants

递归地获取孩子、孩子的孩子。
自己测试一下,就知道它的解析逻辑了。

.string 和 .strings、stripped_strings

  • 如果标签只有一个孩子,而该孩子是一个NavigableString,则该孩子可以使用.string。

  • 使用.strings生成器获取文档中所有的可见字符串。

  • 使用stripped_strings去掉生成的很多的换行。

向上走

.parent

您可以使用.parent属性访问元素的父级。

.parents

您可以使用遍历元素的所有父对象 .parents。此示例用于.parents从埋在文档深处的标记移动到文档的最顶部。

link = soup.a
link
# <a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>
for parent in link.parents:
    print(parent.name)
# p
# body
# html
# [document]

向一边走

同一标记的直接子代。我们称他们为兄弟姐妹。

.next_sibling和.previous_sibling

要小心,在实际文档中,标签的.next_sibling或.previous_sibling通常通常是包含空格的字符串。

# <a href="http://example.com/elsie" class="sister" id="link1">Elsie</a>
# <a href="http://example.com/lacie" class="sister" id="link2">Lacie</a>
# <a href="http://example.com/tillie" class="sister" id="link3">Tillie</a>
link = soup.a
link.next_sibling.next_sibling
# <a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>

.next_siblings和.previous_siblings

您可以使用.next_siblings或.previous_siblings遍历标签的同级元素。

文档对象树的搜索

对于爬虫者来说,搜索应该让人更感兴趣。

演示用的html

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

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

过滤器

过滤器其实就是你指定如何去搜索指定的东西。

在详细讨论find_all()和类似方法之前,我想展示可以传入这些方法的不同过滤器的示例。这些过滤器会在整个搜索API中一次又一次地显示。您可以使用它们根据标签的名称,属性,字符串文本或它们的某种组合进行过滤。

字符串

最简单的过滤器是字符串。将字符串传递给搜索方法,Beautiful Soup将对该字符串进行匹配。此代码查找文档中的所有的b标签

soup.find_all('b')
#[<b>The Dormouse's story</b>]

正则表达式

如果传入正则表达式对象,Beautiful Soup将使用其search()方法针对该正则表达式进行过滤。此代码查找名称以字母“ b”开头的所有标签;在这种情况下,标签和标签:

import re
for tag in soup.find_all(re.compile("^b")):
    print(tag.name)
# body
# b

此代码查找名称中包含字母“ t”的所有标签:

for tag in soup.find_all(re.compile("t")):
    print(tag.name)
# html
# title

列表

如果您传递列表,Beautiful Soup将允许对该列表中的任何项目进行字符串匹配(或的关系)。此代码查找所有a标签 和所有b标签。

soup.find_all(["a", "b"])
# [<b>The Dormouse's story</b>,
#  <a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>,
#  <a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>,
#  <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>]

True

该值True匹配所有可能的值。此代码查找文档中的所有标签,忽略到任何文本字符串。

函数

你可以传入一个函数,就像一个谓词一样。

  • 函数的参数是Tag
    默认情况下,谓词函数的参数是 tag 对象
# 只要那些有class属性,而没有id属性的标签,
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)
# [<p class="title"><b>The Dormouse's story</b></p>,
#  <p class="story">Once upon a time there were…bottom of a well.</p>,
#  <p class="story">...</p>]
  • 函数的参数是Tag的属性
    如果您传入一个函数以过滤特定属性(例如) href,则传递给该函数的参数将是属性值,而不是整个标记。
import re
def not_lacie(href):
    return href and not re.compile("lacie").search(href)

soup.find_all(href=not_lacie)
# [<a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>,
#  <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>]

find_all

函数原型,返回值

find_all(name, attrs, recursive, string, limit, **kwargs)

该find_all()方法浏览标签的后代,并检索与过滤器匹配的所有后代。

举例

soup.find_all("title")
# [<title>The Dormouse's story</title>]

soup.find_all("p", "title")
# [<p class="title"><b>The Dormouse's story</b></p>]

soup.find_all("a")
# [<a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>,
#  <a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>,
#  <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>]

soup.find_all(id="link2")
# [<a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>]

import re
soup.find(string=re.compile("sisters"))
# 'Once upon a time there were three little sisters; and their names were\n'

下面函数的参数做出解释。

因为它find_all()是Beautiful Soup搜索API中最受欢迎的方法,所以可以为其使用快捷方式。如果将 BeautifulSoup对象或Tag对象视为函数,则与调用find_all()该对象相同。这两行代码是等效的:

# 这也是等效的
soup.find_all("a")
soup("a")
# 这也是等效的
soup.title.find_all(string=True)
soup.title(string=True)

name

只考虑标签的名字进行搜索,忽略属性、可见字符串

soup.find_all("title")
# [<title>The Dormouse's story</title>]

attrs

# 可以直接指定
soup.find_all(id='xxx')
# 同时指定多个:
soup.find_all(id='xxx',href=href_filter);
# 通过字典传入
soup.find_all(attrs = {'id' : 'xxx', 'name':'form_name'})
  • CSS class
    如果是对于class属性的话,即可以使用attrs传入。
soup.find_all("a", attrs={"class": "sister"})
# [<a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>,
#  <a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>,
#  <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>]

不过还有更简洁的做法:
使用class_

css_soup = BeautifulSoup('<p class="body strikeout"></p>', 'html.parser')
css_soup.find_all("p", class_="strikeout")
# [<p class="body strikeout"></p>]

css_soup.find_all("p", class_="body")
# [<p class="body strikeout"></p>]

string

根据 可见字符串 进行匹配。
与 name和参数关键字一样,您可以传入字符串,正则表达式,列表,函数或值True。

soup.find_all(string = "Elsie")
# ['Elsie']

注意这时返回的是匹配的字符串列表
如果你要根据string匹配并且返回tag列表,你必须显式加上name

soup.find_all(name = True,string = "Elsie")
# [<a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>]

limit

find_all()返回与您的过滤器匹配的所有标记和字符串。如果文档较大,则可能需要一段时间。如果您不需要所有结果,可以输入的数字limit。就像SQL中的LIMIT关键字一样工作。它告诉Beautiful Soup在找到一定数量后停止收集结果。

soup.find_all("a", limit=2)
# [<a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>,
#  <a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>]

recursive

您只希望soup考虑直接的孩子,则可以传递recursive=False
默认是True。

find家族

除了前面长篇大论说的 find_all,
还有其他很实用的find方法。

find()

相当于设置find_all的limit为1 ;
唯一的区别是,find_all()返回包含单个结果的列表,而find()仅返回结果。
如果find_all()找不到任何内容,则返回一个空列表。如果 find()找不到任何内容,则返回None。

find_parents()和find_parent()

前面的 find find_all都是往”后代找“。

find_parents()和find_parent()是往树上面找。
看函数的名字就知道,一个一直往上搜索,另一个仅仅直系parent。

find_next_siblings()和find_next_sibling()

这些方法使用.next_siblings遍历树中元素的其余同级。该 find_next_siblings()方法返回所有匹配的兄弟姐妹,并且find_next_sibling()仅返回第一个。

find_previous_siblings()和find_previous_sibling()

这些方法使用.previous_siblings遍历树中位于其之前的元素的同级元素。该find_previous_siblings() 方法返回所有匹配的兄弟姐妹,并且 find_previous_sibling()仅返回第一个。

find_all_next()和find_next()

这些方法使用.next_elements遍历文档中紧随其后的所有标签和字符串。该find_all_next()方法返回所有匹配项,并且 find_next()仅返回第一个匹配项。

find_all_previous()和find_previous()

这些方法使用.previous_elements遍历文档中位于其之前的标签和字符串。该find_all_previous()方法返回所有匹配项,并且 find_previous()仅返回第一个匹配项。

CSS选择器

css选择器介绍

  • 通过CSS类查找标签
soup.select(".sister")
# [<a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>,
#  <a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>,
#  <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>]

soup.select("[class~=sister]")
# [<a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>,
#  <a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>,
#  <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>]
  • 按ID查找标签
soup.select("#link1")
# [<a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>]

soup.select("a#link2")
# [<a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>]
  • 测试属性的存在
soup.select('a[href]')
# [<a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>,
#  <a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>,
#  <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>]
python爬虫与项目实战,网络爬虫是一个自动提取网页的程序,它为搜索引擎从万维网上下载网页,是搜索引擎的重要组成。 随着网络的迅速发展,万维网成为大量信息的载体,如何有效地提取并利用这些信息成为一个巨大的挑战。搜索引擎(Search Engine),例如传统的通用搜索引擎AltaVista,Yahoo!和Google等,作为一个辅助人们检索信息的工具成为用户访问万维网的入口和指南。但是,这些通用性搜索引擎也存在着一定的局限性,如: (1)不同领域、不同背景的用户往往具有不同的检索目的和需求,通用搜索引擎所返回的结果包含大量用户不关心的网页。 (2)通用搜索引擎的目标是尽可能大的网络覆盖率,有限的搜索引擎服务器资源与无限的网络数据资源之间的矛盾将进一步加深。 (3)万维网数据形式的丰富和网络技术的不断发展,图片、数据库、音频、视频多媒体等不同数据大量出现,通用搜索引擎往往对这些信息含量密集且具有一定结构的数据无能为力,不能很好地发现和获取。 (4)通用搜索引擎大多提供基于关键字的检索,难以支持根据语义信息提出的查询。 网络爬虫 为了解决上述问题,定向抓取相关网页资源的聚焦爬虫应运而生。聚焦爬虫是一个自动下载网页的程序,它根据既定的抓取目标,有选择的访问万维网上的网页与相关的链接,获取所需要的信息。与通用爬虫(general purpose web crawler)不同,聚焦爬虫并不追求大的覆盖,而将目标定为抓取与某一特定主题内容相关的网页,为面向主题的用户查询准备数据资源。 传统爬虫从一个或若干初始网页的URL开始,获得初始网页上的URL,在抓取网页的过程中,不断从当前页面上抽取新的URL放入队列,直到满足系统的一定停止条件。聚焦爬虫的工作流程较为复杂,需要根据一定的网页分析算法过滤与主题无关的链接,保留有用的链接并将其放入等待抓取的URL队列。然后,它将根据一定的搜索策略从队列中选择下一步要抓取的网页URL,并重复上述过程,直到达到系统的某一条件时停止。另外,所有被爬虫抓取的网页将会被系统存贮,进行一定的分析、过滤,并建立索引,以便之后的查询和检索;对于聚焦爬虫来说,这一过程所得到的分析结果还可能对以后的抓取过程给出反馈和指导。
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值