ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

python_爬虫 06 BeautifulSoup4库

2021-06-03 18:58:17  阅读:202  来源: 互联网

标签:06 获取 python 标签 BeautifulSoup soup print BeautifulSoup4 find


目录

一、BeautifulSoup4库

二、安装和文档

三、几大解析工具对比

四、简单使用

五、四个常用的对象(了解)

    1、Tag

    2、NavigableString

    3、BeautifulSoup

    4、Comment

总结 

六、遍历文档树

    1、contents 和 children

    2、strings 和 stripped_strings

七、搜索文档树

1. find和find_all方法

 例子

总结

2. select方法

例子

总结

 八、中国天气网爬虫


一、BeautifulSoup4库

和 lxml 一样,Beautiful Soup 也是一个HTML/XML的解析器,主要的功能也是如何解析和提取 HTML/XML 数据。
lxml 只会局部遍历,而Beautiful Soup 是基于HTML DOM(Document Object Model)的,会载入整个文档,解析整个DOM树,因此时间和内存开销都会大很多,所以性能要低于lxml。
BeautifulSoup 用来解析 HTML 比较简单,API非常人性化,支持CSS选择器、Python标准库中的HTML解析器,也支持 lxml 的 XML解析器。
Beautiful Soup 3 目前已经停止开发,推荐现在的项目使用Beautiful Soup 4。
 

二、安装和文档

安装:pip install bs4 , python3 使用 pip3 install bs4
中文文档:https://www.crummy.com/software/BeautifulSoup/bs4/doc/index.zh.html

 

三、几大解析工具对比

解析工具解析速度使用难度
BeautifulSoup最慢最简单
lxml简单
正则最快最难

 

四、简单使用

from bs4 import BeautifulSoup

html = """
<html>
    <head><title>The Dormouse's story</title></head>
    <body>
        <div class="div1_class">
            <p class="story" id="pid1" >python</p>
            <p class="story">广州</p>
            <p class="story2">python研发工程师</p>
        </div>
        <div>
            <p class="even">java</p>
            <p class="even">深圳</p>
            <p class="even">java研发工程师</p>
            <a id="test" class="test" herf="http://www.baidu.com">http://www.baidu.com</a>
        </div>
    </body>
</html>
"""
# 这里使用 lxml, 需要先安装 lxml模块
soup = BeautifulSoup(html, "lxml")
print(soup.prettify())

 

五、四个常用的对象(了解)

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

    Tag
    NavigatableString
    BeautifulSoup
    Comment

下面四个类的代码基于如下代码进行测试:

from bs4 import BeautifulSoup

html = """
<html>
    <head><title>The Dormouse's story</title></head>
    <body>
        <div class="div_class">
            <p class="story" id="pid1" >python</p>
            <p class="story">广州</p>
            <p class="story2">python研发工程师</p>
            <p id="p1">
            <!-- 这是一个注释 -->
            </p>
            <p id="p2"><!-- 这是一个注释 --></p>
        </div>
    </body>
</html>
"""
soup = BeautifulSoup(html, "lxml")

    1、Tag

    Tag 通俗点讲就是 HTML 中的一个个标签。我们可以利用 soup 加标签名轻松地获取这些标签的内容,这些对象的类型是bs4.element.Tag。

# Tag
p = soup.select("p")[0]
print(type(p))  # <class 'bs4.element.Tag'>

    2、NavigableString

如果拿到标签后,还想获取标签中的内容。那么可以通过tag.string获取标签中的文字。示例代码如下:

# NavigableString
p = soup.select("p")[0]
print(type(p.string))  # <class 'bs4.element.NavigableString'>

    3、BeautifulSoup

BeautifulSoup 对象表示的是一个文档的全部内容.大部分时候,可以把它当作 Tag 对象,它支持 遍历文档树 和 搜索文档树 中描述的大部分的方法.
因为 BeautifulSoup 对象并不是真正的HTML或XML的tag,所以它没有name和attribute属性.但有时查看它的 .name 属性是很方便的,所以 BeautifulSoup 对象包含了一个值为 “[document]” 的特殊属性 .name

# BeautifulSoup
soup = BeautifulSoup(html, "lxml")
print(type(soup))  # <class 'bs4.BeautifulSoup'>
print(soup.name)  # [document]

    4、Comment

Tag , NavigableString , BeautifulSoup 几乎覆盖了html和xml中的所有内容,但是还有一些特殊对象.容易让人担心的内容是文档的注释部分:

Comment 对象是一个特殊类型的 NavigableString 对象:

# Comment
p1 = soup.select("p#p2")[0]
print(p1.string)  #  这是一个注释
print(type(p1.string))  # <class 'bs4.element.Comment'>

总结 

常见的四种对象
    1、Tag:BeautifulSoup 中所有的标签都是 Tag 类型, 并且 BeautifulSoup 的对象本质上也是一个 Tag 类型。所有一些方法比如 find、find_all 并不是 BeautifulSoup de ,而是 Tag 的。
    2、NavigableString :继承自 python 的 str, 用起来和python str 一样。
    3、BeautifulSoup : 继承自 Tag, 用来生成 BeautifulSoup 树。
    4、Comment : 继承自NavigableString

contents 和 children:
    返回某个标签下的直接子元素,其中也包括字符串。
    contents: 返回的是一个列表
    children:返回的是一个迭代器

 

六、遍历文档树

    1、contents 和 children

from bs4 import BeautifulSoup

html = """
<html>
    <head><title>The Dormouse's story</title></head>
    <body>
        <div class="div_class">
            <p class="story" id="pid1" >python</p>
            <p class="story">广州</p>
            <p class="story2">python研发工程师</p>
            <p id="p1">
            <!-- 这是一个注释 -->
            </p>
            <p id="p2"><!-- 这是一个注释 --></p>
        </div>
    </body>
</html>
"""
soup = BeautifulSoup(html, "lxml")


print("===============================")
div = soup.find("div")
# div.contents 返回所有子节点列表
for p in div.contents:
    print(p)

print("===============================")
div = soup.find("div")
# div.contents 返回所有子节点的生成器
for p in div.children:
    print(p)

    2、strings 和 stripped_strings

如果tag中包含多个字符串 ,可以使用 .strings 来循环获取:

for string in soup.strings:
    print(repr(string))
    # u"The Dormouse's story"
    # u'\n\n'
    # u"The Dormouse's story"
    # u'\n\n'
    # u'Once upon a time there were three little sisters; and their names were\n'
    # u'Elsie'
    # u',\n'
    # u'Lacie'
    # u' and\n'
    # u'Tillie'
    # u';\nand they lived at the bottom of a well.'
    # u'\n\n'
    # u'...'
    # u'\n'

输出的字符串中可能包含了很多空格或空行,使用 .stripped_strings 可以去除多余空白内容:

for string in soup.stripped_strings:
    print(repr(string))
    # u"The Dormouse's story"
    # u"The Dormouse's story"
    # u'Once upon a time there were three little sisters; and their names were'
    # u'Elsie'
    # u','
    # u'Lacie'
    # u'and'
    # u'Tillie'
    # u';\nand they lived at the bottom of a well.'
    # u'...

 

七、搜索文档树

1. find和find_all方法

搜索文档树,一般用得比较多的就是两个方法,一个是find,一个是find_all

find方法是找到第一个满足条件的标签后就立即返回,只返回一个元素。

find_all方法是把所有满足条件的标签都选到,然后返回回去。使用这两个方法,最常用的用法是出入name以及attr参数找出符合要求的标签。

soup.find_all("a",attrs={"id":"link2"})

或者是直接传入属性的的名字作为关键字参数:

soup.find_all("a",id='link2')

 例子

html = """
<html>
    <head><title>The Dormouse's story</title></head>
    <body>
        <div class="div_class">
            <p class="story" id="pid1" >python</p>
            <p class="story">广州</p>
            <p class="story2">python研发工程师</p>
        </div>
        <div  class="div_class">
            <p class="even">java</p>
            <p class="even">深圳</p>
            <p class="even">java研发工程师</p>
            <a id="test" class="test even" href="http://www.baidu.com">http://www.baidu.com</a>
        </div>
    </body>
</html>
"""

# 1、获取所有 p 标签
# 2、获取第二个 p 标签
# 3、获取所有 class 等于 even 的 p 标签
# 4、获取所有 id 等于 test, class 等于 test 的 a 标签
# 5、获取所有 a 标签的 herf 属性
# 6、获取所有的职位信息(纯文本)

soup = BeautifulSoup(html, "lxml")
# print(soup.prettify())

# 1、获取所有 p 标签
ps = soup.find_all("p")
# print(type(ps[0]))  # <class 'bs4.element.Tag'>
for p in ps:
    print(p)

# 2、获取第二个 p 标签
p = soup.find_all("p", limit=2)[1]
p = soup.find_all("p")[1]
print(p)

# 3、获取所有 class 等于 even 的 p 标签
ps = soup.find_all("p", class_="even")
ps = soup.find_all("p", attrs={"class": "even"})
for p in ps:
    print(p)

# 4、获取所有 id 等于 test, class 等于 test 的 a 标签
aList = soup.find_all("a", class_="test", id="test")
aList = soup.find_all("a", attrs={"class": "test", "id": "test"})
for a in aList:
    print(a)

# 5、获取所有 a 标签的 href 属性
aList = soup.find_all("a")
for a in aList:
    # 1、通过下标操作的方式
    print(a["href"])
    # 2、通过 attrs 属性的方式
    print(a.attrs["href"])

# 6、获取所有的职位信息(纯文本)
divs = soup.find_all("div")
positions = []
for div in divs:
    # 通过 tag.string 获取内容
    position = {}
    ps = div.find_all("p")
    language = ps[0].string
    address = ps[1].string
    name = ps[2].string
    position['language'] = language
    position['address'] = address
    position['name'] = name
    positions.append(position)

    # 通过 strings 获取内容
    gen = div.strings # 返回的是一个生成器
    # print(type(gen))  # <class 'generator'>
    infos = list(div.strings)  # 生成的列表带有空白数据
    positions.append(infos)
    # [['\n', 'python', '\n', '广州', '\n', 'python研发工程师', '\n'],
    #  ['\n', 'java', '\n', '深圳', '\n', 'java研发工程师', '\n', 'http://www.baidu.com', '\n']]

    # 通过 stripped_strings 获取内容
    gen = div.stripped_strings  # 返回的是一个生成器
    # print(type(gen))  # <class 'generator'>
    infos = list(div.stripped_strings)  # 生成的列表没有有空白数据
    positions.append(infos)
    # [['python', '广州', 'python研发工程师'],
    # ['java', '深圳', 'java研发工程师', 'http://www.baidu.com']]

    # 通过 get_text 获取内容
    print(type(div.get_text()))  # <class 'str'>
    print(div.get_text())

print(positions)

总结

find_all 的作用
    1、在提取标签的时候,第一个参数是标签的名字。然后如果在提取标签的时候想要使用标签的属性进行过滤,那么可以在这个地方通过关键字参数的形式,将属性的名字以及对应的值传进去。或者使用 atrs 属性, 将所有的属性以及对应的值放进一个字典传给 attrs 属性。
    2、有些时候,在提取标签的时候,不像提取那么多,那么可以使用 limit 参数,限制提取多少个。

find 与 find_all 的区别
    1、find:找到第一个满足条件的标签就返回。只会返回一个元素
    2、find_all:将所有满足条件的标签都返回
    
使用 find 和 find_all 的过滤条件
    1、关键字参数:将属性的名字作为关键字参数的 key ,属性的值作为关键字参数的 value。
    2、attrs 参数:将属性条件放到一个字典中,传给 attrs 参数
    
获取标签的属性:
    1、通过下标获取:通过标签的下标方式:
        href = a["href"]
    2、通过 attrs 属性获取
        href = a.attrs["href"]

string、strings、stripped_strings 和 get_text 方法:
    1、string:获取某个标签下的非标签字符串,返回的是一个字符串。如果这个标签下有多行字符,那么就不能获取到。
    2、strings:获取某个标签下的子孙非标签字符串,返回的是一个生成器
    3、stripped_strings:获取某个标签下的子孙非标签字符串,去掉空白字符串。返回的是一个生成器
    4、get_text:获取某个标签下的子孙非标签字符串。以普通字符串返回

2. select方法

使用以上方法可以方便的找出元素。但有时候使用css选择器的方式可以更加的方便。使用css选择器的语法,应该使用select方法。以下列出几种常用的css选择器方法:

(1)通过标签名查找:

print(soup.select('a'))

(2)通过类名查找:

通过类名,则应该在类的前面加一个.。比如要查找class=sister的标签。示例代码如下:

print(soup.select('.sister'))

(3)通过id查找:

通过id查找,应该在id的名字前面加一个#号。示例代码如下:

print(soup.select("#link1"))

(4)组合查找:

组合查找即和写 class 文件时,标签名与类名、id名进行的组合原理是一样的,例如查找 p 标签中,id 等于 link1的内容,二者需要用空格分开:

print(soup.select("p #link1"))

直接子标签查找,则使用 > 分隔:

print(soup.select("head > title"))

(5)通过属性查找:

查找时还可以加入属性元素,属性需要用中括号括起来,注意属性和标签属于同一节点,所以中间不能加空格,否则会无法匹配到。示例代码如下:

print(soup.select('a[href="http://example.com/elsie"]'))

(6)获取内容

以上的 select 方法返回的结果都是列表形式,可以遍历形式输出,然后用 get_text() 方法来获取它的内容。

soup = BeautifulSoup(html, 'lxml')
print type(soup.select('title'))
print soup.select('title')[0].get_text()

for title in soup.select('title'):
    print title.get_text()

例子

from bs4 import BeautifulSoup

html = """
<html>
    <head><title>The Dormouse's story</title></head>
    <body>
        <div class="div_class">
            <p class="story" id="pid1" >python</p>
            <p class="story">广州</p>
            <p class="story2">python研发工程师</p>
        </div>
        <div  class="div_class">
            <p class="even">java</p>
            <p class="even">深圳</p>
            <p class="even">java研发工程师</p>
            <a id="test" class="test even" href="http://www.baidu.com">http://www.baidu.com</a>
        </div>
    </body>
</html>
"""

# 1、获取所有 p 标签
# 2、获取第二个 p 标签
# 3、获取所有 class 等于 even 的 p 标签
# 4、获取所有 a 标签的 herf 属性
# 5、获取所有的职位信息(纯文本)

soup = BeautifulSoup(html, "lxml")
# print(soup.prettify())

# 1、获取所有 p 标签
ps = soup.select("p")
# print(type(ps[0]))  # <class 'bs4.element.Tag'>
for p in ps:
    print(p)

# 2、获取第二个 p 标签
pList = soup.select("p")
print(type(pList))  # <class 'bs4.element.ResultSet'>
print(type(pList[0]))  # <class 'bs4.element.Tag'>
p2 = pList[1]
print(p2)  # <p class="story">广州</p>

# 3、获取所有 class 等于 even 的 p 标签
# plist = soup.select("p.even")
plist = soup.select("p[class='even']")
for p in plist:
    print(p)

# 4、获取所有 a 标签的 href 属性
aList = soup.select("a")
for a in aList:
    print(a["href"])  # http://www.baidu.com

# 5、获取所有的职位信息(纯文本)
# 可以使用 string、strings、stripped_strings、get_text 方法
divs = soup.select("div")
for div in divs:
    print(list(div.stripped_strings))
"""
['python', '广州', 'python研发工程师']
['java', '深圳', 'java研发工程师', 'http://www.baidu.com']
"""

总结

在 BeautifulSoup 中,要使用 css 选择器,那么应该使用 soup.select() 方法。应该传递一个 css 选择器的字符串给 select 方法。

 八、中国天气网爬虫

爬取中国天气网全国所有城市当前最低温度;将数据排序后,将温度最高的10个城市通过图标显示出来。

代码

import requests
from bs4 import BeautifulSoup
from pyecharts import options as opts
from pyecharts.charts import Bar
from pyecharts.faker import Faker

HEADERS = {
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.77 Safari/537.36",
    "Referer": "http://www.weather.com.cn/textFC/db.shtml",
}

def parse_page(url):
    # 获取天气预报页面
    response = requests.get(url=url, headers=HEADERS)
    text = response.content.decode("utf-8")
    # print(text)

    # 通过 BeautifulSoup 解析html 页面,并获取到面所有 table 标签内容
    # 使用 html5lib 代替 lxml: 由于 gat.html 页面的天气数据,只有 <table>, 没有  </table>,
    # lxml 解析不了,会报错,但是通过 浏览器解析后我们看到的html , 浏览器已经帮我们补全所以看出是少了闭合标签
    # 如果通过 html5lib解析器 解析, 返回的 table 会帮程序补全闭合标签
    # 安装命令: pip3 install html5lib
    soup = BeautifulSoup(text, "html5lib")
    # 页面会有7天的数据(默认使用第一个(今天))
    conMidtab = soup.find("div", class_="conMidtab")
    tables = conMidtab.find_all("table")

    # 解析每个表格的数据
    cities = []
    for table in tables:
        # print(table)
        trs = table.find_all("tr")[2:]  # 第一二行为表头
        # 解析每一行的数据
        for index, tr in enumerate(trs):
            tds = tr.find_all("td")
            td = tds[0]
            if index == 0: td = tds[1]  # 数据的第一行的第一列是省份,第二列才是城市
            city = list(tds[0].stripped_strings)[0]
            min_temp = list(tds[-2].stripped_strings)[0]
            cities.append({"city": city, "min_temp": int(min_temp)})
    return cities

def creat_html(xdata, ydata):
    bar = Bar()
    bar.add_xaxis(xdata)
    bar.add_yaxis("城市", ydata)
    bar.set_global_opts(
        title_opts=opts.TitleOpts(title="城市最低温度排序后10名", subtitle="我是副标题"),
        # brush_opts=opts.BrushOpts(),
    )
    # 渲染数据并保存到html 页面
    bar.render("bar_with_brush.html")

def main():
    all_data = []
    url_list = [
        "http://www.weather.com.cn/textFC/hb.shtml",
        "http://www.weather.com.cn/textFC/db.shtml",
        "http://www.weather.com.cn/textFC/hd.shtml",
        "http://www.weather.com.cn/textFC/hz.shtml",
        "http://www.weather.com.cn/textFC/hn.shtml",
        "http://www.weather.com.cn/textFC/xb.shtml",
        "http://www.weather.com.cn/textFC/xn.shtml",
        "http://www.weather.com.cn/textFC/gat.shtml",
    ]
    for url in url_list:
        datas = parse_page(url)
        all_data.extend(datas)
    # 通过匿名函数设置排序的key
    all_data.sort(key=lambda data: data["min_temp"])

    # url = "http://www.weather.com.cn/textFC/gat.shtml"
    # datas = parse_page(url)
    # all_data.extend(datas)
    # all_data.sort(key=lambda data: data["min_temp"])

    # 通过 pyecharts 将 城市最低温度 排序 最高 的10个城市显示成 html
    show_data = all_data[-10:]
    cities = list(map(lambda data: data["city"], show_data))
    min_temps = list(map(lambda data: data["min_temp"], show_data))
    creat_html(cities, min_temps)

    print("all_data: ", all_data)


if __name__ == '__main__':
    main()

结果

标签:06,获取,python,标签,BeautifulSoup,soup,print,BeautifulSoup4,find
来源: https://blog.csdn.net/qq_30346413/article/details/117475114

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有