1. Beautiful Soup介绍

Beautiful Soup是一个可以从HTML或XML文件中提取数据的Python库。能将即将要进行解析的源码加载到bs对象,调用bs对象中相关的方法或属性进行源码中的相关标签的定位,并获取定位到的标签之间存在的文本或者属性值。

它能够通过你喜欢的转换器实现惯用的文档导航、查找、修改文档的方式。Beautiful Soup会帮你节省数小时甚至数天的工作时间。

1.1 安装bs4

pip install 包名 -i http://pypi.douban.com/simple/ --trusted-host pypi.douban.com

1.2 初始化

from bs4 import BeautifulSoup

soup = BeautifulSoup("<html>A Html Text</html>", "html.parser")

两个参数:第一个参数是要解析的html文本,第二个参数是使用那种解析器,对于HTML来讲就是html.parser,这个是bs4自带的解析器。

如果一段HTML或XML文档格式不正确的话,那么在不同的解析器中返回的结果可能是不一样的。

格式化输出:

soup.prettify()

1.3 对象

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

1.3.1 tag

Tag对象与xml或html原生文档中的tag相同。

from bs4 import BeautifulSoup

soup = BeautifulSoup('<b class="boldest">Extremely bold</b>','xml')
tag = soup.b
print(type(tag))# <class 'bs4.element.Tag'>

如果不存在,则返回 None,如果存在多个,则返回第一个。

每个tag都有自己的名字。

tag = soup.b
print(tag.name)# b

tag的属性是一个字典。

tag = soup.b
print(tag['class'])# boldest
print(tag.attrs)# {'class': 'boldest'}
print(type(tag.attrs))# <class 'dict'>

多值属性,最常见的多值属性是class,多值属性的返回list。

soup = BeautifulSoup('<p class="body strikeout"></p>','xml') 

print(soup.p['class']) # ['body', 'strikeout'] print(soup.p.attrs) # {'class': ['body', 'strikeout']}

如果某个属性看起来好像有多个值,但在任何版本的HTML定义中都没有被定义为多值属性,那么Beautiful Soup会将这个属性作为字符串返回。

soup = BeautifulSoup('<p id="my id"></p>', 'html.parser')
print(soup.p['id']) # 'my id'

text属性返回tag的所有字符串连成的字符串。

1.3.2 NavigableString

字符串常被包含在tag内,BeautifulSoup用NavigableString类来包装tag中的字符串。但是字符串中不能包含其他tag。

soup = BeautifulSoup('<b class="boldest">Extremely bold</b>','xml')

s = soup.b.string

print(s)        # Extremely bold

print(type(s))  # <class 'bs4.element.NavigableString'>

1.3.3 BeautifulSoup

BeautifulSoup 对象表示的是一个文档的全部内容。大部分时候,可以把它当作Tag对象。但是BeautifulSoup对象并不是真正的HTML或XML的 tag,它没有attribute属性,name属性是一个值为“[document]”的特殊属性。

1.3.4 Comment

Comment一般表示文档的注释部分。

oup = BeautifulSoup("<b><!--This is a comment--></b>",'xml')

comment = soup.b.string

print(comment)          # This is a comment

print(type(comment))    # <class 'bs4.element.Comment'>

1.4 遍历

1.4.1 子节点

contents属性返回所有子节点的列表,包括NavigableString类型节点。如果节点当中有换行符,会被当做是NavigableString类型节点而作为一个子节点。

NavigableString 类型节点没有contents属性,因为没有子节点。

soup = BeautifulSoup("""<div>
<span>test</span>
</div>
""",'xml') element = soup.div.contents print(element) # ['\n', <span>test</span>, '\n']

children属性跟contents属性基本一样,只不过返回的不是子节点列表,而是子节点的可迭代对象。

descendants属性返回tag的所有子孙节点。

如果tag只有一个NavigableString类型子节点,那么这个tag可以使用.string得到子节点。

如果一个tag仅有一个子节点,那么这个tag也可以使用.string方法,输出结果与当前唯一子节点的.string结果相同。

如果tag包含了多个子节点,tag就无法确定.string方法应该调用哪个子节点的内容,.string 的输出结果是None。

soup = BeautifulSoup("""<div>
<p><span><b>test</b></span></p>
</div>
""",'xml') element = soup.p.string print(element) # test print(type(element)) # <class 'bs4.element.NavigableString'>

特别注意,为了清楚显示,一般我们会将html节点换行缩进显示,而在BeautifulSoup中会被认为是一个NavigableString类型子节点,导致出错。上例中,如果改成element = soup.div.string就会出错。

如果tag中包含多个字符串,可以用strings属性来获取。如果返回结果中要去除空行,则可以用stripped_strings属性。

soup = BeautifulSoup("""<div>
<p> </p>
<p>test 1</p>
<p>test 2</p>
</div>
""", 'html.parser') element = soup.div.stripped_strings print(list(element)) # ['test 1', 'test 2']

1.4.2 父节点

parent属性返回某个元素(tag、NavigableString)的父节点,文档的顶层节点的父节点是BeautifulSoup对象,BeautifulSoup对象的父节点是None。

parents属性递归得到元素的所有父辈节点,包括BeautifulSoup对象。

1.4.3 兄弟节点

next_sibling返回后一个兄弟节点,previous_sibling返回前一个兄弟节点。直接看个例子,注意别被换行缩进搅了局。

soup = BeautifulSoup("""<div>
<p>test 1</p><b>test 2</b><h>test 3</h></div>
""", 'html.parser') print(soup.b.next_sibling) # <h>test 3</h> print(soup.b.previous_sibling) # <p>test 1</p> print(soup.h.next_sibling) # None

next_siblings返回后面的兄弟节点

previous_siblings返回前面的兄弟节点

1.4.4 回退和前进

把html解析看成依次解析标签的一连串事件,BeautifulSoup提供了重现解析器初始化过程的方法。

next_element属性指向解析过程中下一个被解析的对象(tag或NavigableString)。

previous_element属性指向解析过程中前一个被解析的对象。

另外还有next_elements和previous_elements属性,不赘述了。

1.5 搜索

1.5.1 过滤器

介绍find_all()方法前,先介绍一下过滤器的类型,这些过滤器贯穿整个搜索的API。过滤器可以被用在tag的name中,节点的属性中,字符串中或他们的混合中。

html = """
<div>
<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></p>
</div>
""" soup = BeautifulSoup(html, 'html.parser')

查找所有的<b>标签

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

传入正则表达式作为参数,返回满足正则表达式的标签。下面例子中找出所有以b开头的标签。

print(soup.find_all(re.compile("^b")))  # [<b>The Dormouse's story</b>]

传入列表参数,将返回与列表中任一元素匹配的内容。下面例子中找出所有<a>标签和<b>标签。

print(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可以匹配任何值,下面的代码查找到所有的tag,但是不会返回字符串节点。

print(soup.find_all(True))

如果没有合适的过滤器,还可以自定义一个方法,方法只接收一个元素参数,如果这个方法返回True表示当前元素匹配被找到。

例子,返回所有包含class属性但不包含id属性的标签:

def has_class_but_no_id(tag):
return tag.has_attr('class') and not tag.has_attr('id') print(soup.find_all(has_class_but_no_id))

结果:

结果怎么不对呢,<a>标签含有id属性。其实返回的list中只有2个元素,都是<p>标签,<a>标签是<p>标签的子节点。

1.5.2 find和find_all

搜索当前tag的所有tag子节点,并判断是否符合过滤器的条件。

语法:

find(name=None, attrs={}, recursive=True, text=None, **kwargs)

find_all(name=None, attrs={}, recursive=True, text=None, limit=None, **kwargs)

参数:

name:查找所有名字为name的tag,字符串对象会被自动忽略掉。上面过滤器示例中的参数都是name参数。当然,其他参数中也可以使用过滤器。

attrs:按属性名和值查找。传入字典,key为属性名,value为属性值。

recursive:是否递归遍历所有子孙节点,默认True。

text:用于搜索字符串,会找到 .string方法与text参数值相符的tag,通常配合正则表达式使用。也就是说,虽然参数名是 text,但实际上搜索的是string属性。

limit:限定返回列表的最大个数。

kwargs:如果一个指定名字的参数不是搜索内置的参数名,搜索时会把该参数当作tag的属性来搜索。这里注意,如果要按class属性搜索,因为class是python的保留字,需要写作class_。

Tag的有些属性在搜索中不能作为kwargs参数使用,比如H5中的data-*属性。

data_soup = BeautifulSoup('<div data-foo="value">foo!</div>')

print(data_soup.find_all(data-foo="value"))

# SyntaxError: keyword can't be an expression

但是可以通过attrs参数传递

data_soup = BeautifulSoup('<div data-foo="value">foo!</div>','xml')

print(data_soup.find_all(attrs={"data-foo": "value"}))

# [<div data-foo="value">foo!</div>]

1.6 CSS选择器

BeautifulSoup支持大部分的CSS选择器,这里直接用代码来演示。

from bs4 import BeautifulSoup

html = """
<html>
<head><title>标题</title></head>
<body>
<p class="title" name="dromouse"><b>标题</b></p>
<div name="divlink">
<p>
<a href="http://example.com/1" class="sister" id="link1">链接1</a>
<a href="http://example.com/2" class="sister" id="link2">链接2</a>
<a href="http://example.com/3" class="sister" id="link3">链接3</a>
</p>
</div>
<p></p>
<div name='dv2'></div>
</body>
</html>
""" soup = BeautifulSoup(html, 'lxml') # 通过tag查找
print(soup.select('title')) # [<title>标题</title>] # 通过tag逐层查找
print(soup.select("html head title")) # [<title>标题</title>] # 通过class查找
print(soup.select('.sister'))
# [<a class="sister" href="http://example.com/1" id="link1">链接1</a>,
# <a class="sister" href="http://example.com/2" id="link2">链接2</a>,
# <a class="sister" href="http://example.com/3" id="link3">链接3</a>] # 通过id查找
print(soup.select('#link1, #link2'))
# [<a class="sister" href="http://example.com/1" id="link1">链接1</a>,
# <a class="sister" href="http://example.com/2" id="link2">链接2</a>] # 组合查找
print(soup.select('p #link1'))# [<a class="sister" href="http://example.com/1" id="link1">链接1</a>] # 查找直接子标签
print(soup.select("head > title"))# [<title>标题</title>] print(soup.select("p > #link1"))# [<a class="sister" href="http://example.com/1" id="link1">链接1</a>] print(soup.select("p > a:nth-of-type(2)")) # [<a class="sister" href="http://example.com/2" id="link2">链接2</a>]
# nth-of-type 是CSS选择器 # 查找兄弟节点(向后查找)
print(soup.select("#link1 ~ .sister"))
# [<a class="sister" href="http://example.com/2" id="link2">链接2</a>,
# <a class="sister" href="http://example.com/3" id="link3">链接3</a>] print(soup.select("#link1 + .sister"))
# [<a class="sister" href="http://example.com/2" id="link2">链接2</a>] # 通过属性查找
print(soup.select('a[href="http://example.com/1"]')) # ^ 以XX开头
print(soup.select('a[href^="http://example.com/"]')) # * 包含
print(soup.select('a[href*=".com/"]')) # 查找包含指定属性的标签
print(soup.select('[name]')) # 查找第一个元素
print(soup.select_one(".sister"))

1.7 实例

我们来爬取三国演义的内容,我甚至有个想法,去那些小说网里爬小说去。

import requests
from bs4 import BeautifulSoup url = 'http://www.shicimingju.com/book/sanguoyanyi.html'
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36'
}
page_text = requests.get(url=url,headers=headers).text soup = BeautifulSoup(page_text,'lxml') a_list = soup.select('.book-mulu > ul > li > a') fp = open('sanguo.txt','w',encoding='utf-8')
for a in a_list:
title = a.string
detail_url = 'http://www.shicimingju.com'+a['href']
detail_page_text = requests.get(url=detail_url,headers=headers).text soup = BeautifulSoup(detail_page_text,'lxml')
content = soup.find('div',class_='chapter_content').text fp.write(title+'\n'+content)
print(title,'下载完毕')
print('over')

爬虫(七):BeatifulSoup模块的更多相关文章

  1. 爬虫 BeatifulSoup 模块

    BeatifulSoup 模块 介绍 Beautiful Soup 是一个可以从HTML或XML文件中提取数据的Python库 安装 pip install beautifulsoup4  解析器下载 ...

  2. python爬虫主要就是五个模块:爬虫启动入口模块,URL管理器存放已经爬虫的URL和待爬虫URL列表,html下载器,html解析器,html输出器 同时可以掌握到urllib2的使用、bs4(BeautifulSoup)页面解析器、re正则表达式、urlparse、python基础知识回顾(set集合操作)等相关内容。

    本次python爬虫百步百科,里面详细分析了爬虫的步骤,对每一步代码都有详细的注释说明,可通过本案例掌握python爬虫的特点: 1.爬虫调度入口(crawler_main.py) # coding: ...

  3. Python爬虫之urllib模块2

    Python爬虫之urllib模块2 本文来自网友投稿 作者:PG-55,一个待毕业待就业的二流大学生. 看了一下上一节的反馈,有些同学认为这个没什么意义,也有的同学觉得太简单,关于Beautiful ...

  4. Python爬虫之urllib模块1

    Python爬虫之urllib模块1 本文来自网友投稿.作者PG,一个待毕业待就业二流大学生.玄魂工作室未对该文章内容做任何改变. 因为本人一直对推理悬疑比较感兴趣,所以这次爬取的网站也是平时看一些悬 ...

  5. asynicio模块以及爬虫应用asynicio模块(高性能爬虫)

    一.背景知识 爬虫的本质就是一个socket客户端与服务端的通信过程,如果我们有多个url待爬取,只用一个线程且采用串行的方式执行,那只能等待爬取一个结束后才能继续下一个,效率会非常低. 需要强调的是 ...

  6. 第三百二十六节,web爬虫,scrapy模块,解决重复ur——自动递归url

    第三百二十六节,web爬虫,scrapy模块,解决重复url——自动递归url 一般抓取过的url不重复抓取,那么就需要记录url,判断当前URL如果在记录里说明已经抓取过了,如果不存在说明没抓取过 ...

  7. 第三百二十五节,web爬虫,scrapy模块标签选择器下载图片,以及正则匹配标签

    第三百二十五节,web爬虫,scrapy模块标签选择器下载图片,以及正则匹配标签 标签选择器对象 HtmlXPathSelector()创建标签选择器对象,参数接收response回调的html对象需 ...

  8. 第三百二十四节,web爬虫,scrapy模块介绍与使用

    第三百二十四节,web爬虫,scrapy模块介绍与使用 Scrapy是一个为了爬取网站数据,提取结构性数据而编写的应用框架. 其可以应用在数据挖掘,信息处理或存储历史数据等一系列的程序中.其最初是为了 ...

  9. 第三百二十三节,web爬虫,scrapy模块以及相关依赖模块安装

    第三百二十三节,web爬虫,scrapy模块以及相关依赖模块安装 当前环境python3.5 ,windows10系统 Linux系统安装 在线安装,会自动安装scrapy模块以及相关依赖模块 pip ...

  10. Python爬虫教程-09-error 模块

    Python爬虫教程-09-error模块 今天的主角是error,爬取的时候,很容易出现错,所以我们要在代码里做一些,常见错误的处,关于urllib.error URLError URLError ...

随机推荐

  1. oracle表结构

    表管理 新建表 语法 create table 表名 ( 列名1 类型(长度), 列名2 类型(长度), 列名3 类型(长度) ); create table:关键字,建表 后跟新建表的表名,表名长度 ...

  2. 探索 IPv6 网络

    目录 0x00 前言 0x01 探索 服务器配置 IPv6 地址 服务器部署网络代理 客户端配置网络代理 测试访问 IPv6 地址 给博客添加 IPv6 地址 0x00 前言 IPv4 地址枯竭的事情 ...

  3. 【集训Day1 测试】装饰

    装饰(decorate) [题目描述] 一个图有 N 个结点,编号 1 至 N,有 M 条无向边,第 i 条边连接的两个结点是 Ai 和Bi,其中 Ai 和 Bi 是不同的结点.可能有多条边连接的是同 ...

  4. Altium Designer 20.0.9

    Altium Designer 20.0.9 Download: http://dl3.downloadly.ir/Files/Software/Altium_Designer_20.0.9_Buil ...

  5. 动态规划算法(java)

    一.动态规划算法 众所周知,递归算法时间复杂度很高为(2^n),而动态规划算法也能够解决此类问题,动态规划的算法的时间复杂度为(n^2).动态规划算法是以空间置换时间的解决方式,一开始理解起来可能比较 ...

  6. Chapter 05—Advanced data management(Part 2)

    二. 控制流 statement:一个单独的R语句或者是一个复合的R语句: cond:条件表达式,为TRUE或FALSE: expr:数字或字符表达式: seq:数字或字符串的顺序. 1.循环语句:f ...

  7. Python的特有的参数传递(*和**)

    目录 值传递 引用传递 python的传递方式具有两种值传递和引用传递.除此之外,python中还允许包裹方式的参数传递,这未不确定参数个数和参数类型的函数调用提供了基础: 值传递 int.float ...

  8. MyEclispe启动Tomcat7时出现错误The servlets named [LoginServlet] and [com.liu.control.LoginServlet] are both

    刚开始尝试写Servlet代码,第一天就碰到这个错误,在网上找了很多资料才找到解决办法,在此记录一下. org.apache.catalina.LifecycleException: Failed t ...

  9. 拿 C# 搞函数式编程 - 2

    前一阵子在写 CPU,导致一直没有什么时间去做其他的事情,现在好不容易做完闲下来了,我又可以水文章了哈哈哈哈哈.顺便成立了自己的专栏:hez2010 的编程日常,欢迎大家关注(逃 有关 FP 的类型部 ...

  10. 程序员修神之路--打通Docker镜像发布容器运行流程

    菜菜哥,我看了一下docker相关的内容,但是还是有点迷糊 还有哪不明白呢? 如果我想用docker实现所谓的云原生,我的项目该怎么发布呢? 这还是要详细介绍一下docker了 Docker 是一个开 ...