当前位置: 首页 > news >正文

python-爬虫之beautifulsoup

HTML,全称为 HyperText Markup Language,即超文本标记语言,是用于创建网页和用户界面的标准标记语言。HTML 通过使用标记(即标签)来定义网页的内容和结构。一个典型的 HTML 文档包括元素、属性和内容。每个网站都是由 HTML 代码构成的,并且它能够决定网页的结构和内容。

一、网页的基本结构

HTML文档由4个主要标记组成,包括<html>、<head>、<body>.这3个标记构成了HTML页面最基本的元素。

1.1、网页常见要素

  1. <!DOCTYPE html>
    • 作用:声明文档类型,告诉浏览器这是一个 HTML5 文档。
    • 必须放在文档的最顶部
  2. <html>标签
    • 是整个 HTML 文档的根元素,所有的HTML文件都以标记开头,以标记结束,即所有其他内容都包含在 <html>标签中。标记虽然没有实质性的功能,但却是HTML必不可少的部分。
    • 属性 lang="zh-CN"表示页面语言为中文(中国大陆),有助于搜索引擎和辅助技术理解页面内容语言。
  3. <head>部分
    • 标记是HTML文件的头标记,用于存放HTML文件的信息。
    • 常见内容:
      • <meta charset="UTF-8">:设置字符编码为 UTF-8,确保中文等特殊字符正常显示。
      • <meta name="viewport" ...>:用于响应式设计,让网页在手机等设备上显示良好。
      • <title>:定义浏览器标签页上显示的标题,也是搜索引擎结果中显示的标题。建议必须设置
      • <link>:引入外部资源,如 CSS 文件。
      • <script>:引入 JavaScript 脚本。
      • <style>:内联 CSS 样式(一般推荐将样式写在独立的 CSS 文件中)。
  4. <body>部分
    • 是网页的主体内容区域,用户在浏览器中看到的所有内容(文字、图片、链接、表单等)都放在这里。
    • 包括标题、段落、图片、链接、列表、表格、表单、视频等元素。

1.2、HTML 常用结构标签

html的标签预定义了一部分,下面一些常用的 HTML 标签,它们用于构建网页的基本结构与内容:

标签用途示例
<h1><h6>定义标题,<h1>最重要,<h6>最不重要<h1>主标题</h1>
<p>定义段落<p>这是一个段落。</p>
<a>定义链接<a href="https://www.example.com">访问示例</a>
<img>插入图片<img src="image.jpg" alt="描述文字">
<ul>, <ol>, <li>定义无序和有序列表<ul><li>项目1</li><li>项目2</li></ul>
<div>定义一个块级容器,用于布局或分组内容<div>这是一个容器</div>
<span>定义行内容器,用于对文本的一部分进行样式化或操作<span>高亮文字</span>
<form>创建表单,用于用户输入<form action="/submit" method="POST">...</form>
<input>输入框(文本、密码、按钮等)<input type="text" placeholder="请输入">
<button>按钮<button>点击我</button>

1.3、HTML 语义化标签(HTML5 新增)

HTML5 引入了一系列语义化标签,它们不仅起到容器的作用,还明确表达了内容的含义,有助于 SEO 和可访问性。

标签语义/用途
<header>页面或区块的页眉,通常包含标题、导航等
<nav>导航栏,包含链接集合
<main>页面的主要内容,每个页面应该只有一个
<section>一个主题性的内容区块
<article>独立的内容块,如一篇博客文章、新闻
<aside>侧边栏或与主内容相关但独立的内容(如广告、相关链接)
<footer>页脚,通常包含版权信息、联系方式等
<figure><figcaption>用于展示媒体(如图片、图表)及其标题说明
<!DOCTYPE html>
<html lang="zh-CN">
<head><meta charset="UTF-8" /><meta name="viewport" content="width=device-width, initial-scale=1.0" /><title>我的第一个网页</title><link rel="stylesheet" href="styles.css" />
</head>
<body><header><h1>欢迎来到我的网站</h1><nav><ul><li><a href="#">首页</a></li><li><a href="#">博客</a></li></ul></nav></header><main><section><h2>关于我们</h2><p>这是关于我们的介绍。</p></section><article><h2>最新文章</h2><p>这是一篇新文章的内容。</p></article></main><footer><p>Copyright © 2024 我的公司</p></footer>
</body>
</html>

html打开后的显示效果
在这里插入图片描述

二、python解析html

python解析html可以lxml或者beautifulsoup。下面主要介绍beautifulsoup模块

2.1、Beautiful Soup的简介

Beautiful Soup是python的一个 HTML/XML 解析库,提供了简单易用的 API,适合快速提取和操作网页内容。不是解析器本身,而是一个解析工具,需要搭配解析器使用(如 html.parserlxmlhtml5lib),其中只要html.parser是官方内置的标准库,其他库都需要额外安装。下面是不同解析器的对比:

解析器使用方法优势劣势
Python标准库BeautifulSoup(markup, “html.parser”)Python的内置标准库、执行速度适中、文档容错能力强Python 2.7.3 or 3.2.2)前 的版本中文档容错能力差
lxml HTML 解析器BeautifulSoup(markup, “lxml”)速度快、文档容错能力强需要安装C语言库
lxml XML 解析器BeautifulSoup(markup, [“lxml”, “xml”])或BeautifulSoup(markup, “xml”)速度快、唯一支持XML的解析器需要安装C语言库
html5libBeautifulSoup(markup, “html5lib”)最好的容错性、以浏览器的方式解析文档、生成HTML5格式的文档、不依赖外部扩展速度慢

不同场景下使用建议:

场景推荐工具
快速上手、代码简洁、功能够用BeautifulSoup + html.parser 或 lxml
追求解析速度,处理大量数据BeautifulSoup + lxml 或直接使用 lxml
处理混乱、不规范的 HTMLBeautifulSoup + html5lib
不想安装第三方库,轻量级使用BeautifulSoup + html.parser
需要使用 XPath 进行高级查询lxml(推荐)
需要严格按 XML 标准解析lxml-xml

2.2、BeautifulSoup用法

soup = BeautifulSoup(markup, features=None, builder=None, parse_only=None, from_encoding=None, exclude_encodings=None, element_classes=None, **kwargs)
  • 功能:原始的 HTML/XML 字符串或文件内容,转换成一个可遍历、可搜索的 DOM 树结构,从而让你能够方便地提取、修改和操作其中的内容

  • 常用参数:

    • markup:必选, 要解析的 HTML 或 XML 内容,可以是HTML 字符串、XML 字符串,本地 HTML 文件内容

    • features:可选,指定使用的解析器,强烈建议指定,因为不同环境存在的解析器不同。如果没有显式指定解析器,BeautifulSoup 会使用当前环境下 “可用的、优先级最高的解析器” 作为默认解析器,通常lxml>html.parser>html5lib

  • 返回值:

    • bs4.BeautifulSoup类的实例(对象),它是对整个 HTML/XML 文档的封装,表示一个可遍历、可搜索的树状结构。

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

对象名描述
BeautifulSoup文档自身:表示的是一个文档的全部内容
Tag标签对:Tag对象与XML或HTML原生文档中的tag相同,即标签对
NavigableString标签值:标签对中的字符串
Comment注释:文档的注释部分

2.2.1、tag对象

下面是tag对象的一下属性和方法

功能类别说明示例方法/属性
访问标签直接通过标签名获取第一个匹配的标签对象soup.p, soup.a, soup.div,soup.head, soup.body,soup.title
获取标签名称获取某个标签名soup.p.name
获取标签内容获取某个标签的文本(会递归地提取该标签及其所有子孙节点中的文本内容,并将它们拼接成一个完整的字符串).text, .get_text()
获取标签内容获取某个标签文本(没有嵌套其他标签返回字符串内容,有嵌套其他标签返回None)Navigable对象(并不好用).string
获取标签属性获取标签的属性,返回的是一个字典对象。soup.p.attrs
获取标签某个属性获取某个标签的属性值,如 classhrefid,返回的是一个list对象soup.a['href'], soup.p['class']
查找标签查找第一个符合条件的标签(一般不使用)soup.find('p'), soup.find(class_='intro')
查找所有标签查找所有符合条件的tag,返回一个列表soup.find_all('p')
遍历文档结构可以遍历子节点、父节点、兄弟节点等.children, .parent, .next_sibling
搜索与过滤支持按标签名、属性、CSS 类名、文本内容等查找find_all(), select()(CSS选择器)
修改文档可以修改标签、属性、文本内容(增删改查).string = "新文本", .append()
from bs4 import BeautifulSouphtml_text = """
<html>
<head><title>示例页面</title></head>
<body><h1>欢迎来到我的网站</h1><p class="intro aaa" test=123>这是一个段落。</p><p class="content bbb">这是另一个段落。</p><a href="https://example.com">链接</a>
</body>
</html>
"""soup = BeautifulSoup(html_text, "lxml")print("h1标签的tag对象为", soup.h1, type(soup.h1))
# p标签的tag对象为 <h1>欢迎来到我的网站</h1> <class 'bs4.element.Tag'>
print("h1标签的文本为", soup.h1.text, type(soup.h1))print("p标签的tag对象的属性为",soup.p.attrs)  # 返回一个字典对象。标签属性的值只有class解析为列表:['intro', 'aaa'],其他的解析为string;lxml将其解析为字符串:'intro aaa'
# p标签的tag对象的属性为 {'class': ['intro', 'aaa'], 'test': '123'}# 获取标签某个属性具体的值有以下四种方式
print("p标签的test属性值为", soup.p.attrs["test"])  # 使用字典的索引
# p标签的test属性值为 123
print("p标签的class属性值为", soup.p.attrs.get("class"))  # 使用字典的get方法
# p标签的class属性值为 ['intro', 'aaa']
print("p标签的class属性值为", soup.p.get("class"))  # 使用soup对象.标签名.get(属性名)
# p标签的class属性值为 ['intro', 'aaa']
print("p标签的class属性值为", soup.p["class"])
# p标签的class属性值为 ['intro', 'aaa']print("soup的所有p标签的class属性值为", soup.find_all("p"))
# soup的所有p标签的class属性值为 [<p class="intro aaa" test="123">这是一个段落。</p>, <p class="content bbb">这是另一个段落。</p>]
  • HTML 规范本身允许 class属性包含多个值,所以除了class属性的值返回为list,其他的属性(id、src等通用属性)返回为string。
  • 标签属性的值只有class解析为列表:[‘intro’, ‘aaa’],其他的解析为string;lxml将其解析为字符串:‘intro aaa’

2.2.2、NavigableString对象

BeautifulSoup 中,当你访问某个标签(Tag)内部的 纯文本 时(无论是在父子标签之间,还是兄弟标签之间,只要是在tag内部的的存文本),都会被表示为一个 NavigableString类型的对象。格式化的html中换行符也会被当做一个NavigableString类型。

from bs4 import BeautifulSouphtml_text = """
<html>
<head><title>示例页面</title></head>
<body><h1>欢迎来到我的网站</h1><p class="intro aaa" test=123>这是一个段落。<test>这是一个子节点</test>兄弟节点间的文本<test1>test的兄弟节点test1</test1></p><p class="content bbb">这是另一个段落。</p><a href="https://example.com">链接</a>
</body>
</html>
"""soup = BeautifulSoup(html_text, "lxml")for child in soup.p.children:print("-"*30+"\r\n",child,type(child))------------------------------这是一个段落。<class 'bs4.element.NavigableString'>
------------------------------<test>这是一个子节点</test> <class 'bs4.element.Tag'>
------------------------------兄弟节点间的文本<class 'bs4.element.NavigableString'>
------------------------------<test1>test的兄弟节点test1</test1> <class 'bs4.element.Tag'>
------------------------------<class 'bs4.element.NavigableString'>

NavigableString是 BeautifulSoup 提供的一个类,用来表示 HTML 或 XML 标签中包含的 “纯文本” 的对象。它是 str(字符串)的一个子类,可以像操作字符串一样操作它,但它还 “可导航(Navigable)”,即可以访问其父节点、兄弟节点等,这就是 “Navigable” 的含义。通过 soup.p.string获取,.string只有在当前标签下只有一个字符串节点(没有嵌套其它标签)时才有值,且返回的是 NavigableString类型,否则返回None

from bs4 import BeautifulSouphtml_text = """
<html>
<head><title>示例页面</title></head>
<body><h1>欢迎来到我的网站</h1><p class="intro aaa" test=123>这是一个段落。</p><p class="content bbb">这是另一个段落。</p><a href="https://example.com">链接</a>
</body>
</html>
"""soup = BeautifulSoup(html_text, "lxml")print(soup.body.string)
print(soup.body.string.parsend)
print(soup.title.string)
返回结果为:
None
示例页面

在正常html数据中是需要将换行符生成的NavigableString类型剔除掉的,可以使用以下几种方式:

2.2.2.1、if child.name
for child in p.children:if child.name:  # 如果 child 是一个标签,它会有 name 属性,如 'span'print("找到子标签:", child)else:# child 是字符串(可能是换行/空格/文本)if child.strip():  # 如果文本非空(去掉首尾空格后不为空)print("文本内容:", child.strip())# else: 是纯换行或空格,可忽略
2.2.2.2、isinstance()
for child in p.children:if isinstance(child, Tag):print("子标签:", child)else:# 处理文本节点text = child.strip()if text:print("文本:", text)
2.2.2.3、find_all()–最常用
for child in p.find_all(recursive=False):# 只查找 <p>的直接子标签,不会递归查找子标签的子标签,而且不会返回文本节点,只返回标签print("直接子标签:", child)

2.2.3、BeautifulSoup对象

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

from bs4 import BeautifulSouphtml_text = """
<html>
<head><title>示例页面</title></head>
<body><h1>欢迎来到我的网站</h1><p class="intro aaa" test=123>这是一个段落。</p><p class="content bbb">这是另一个段落。</p><a href="https://example.com">链接</a>
</body>
</html>
"""soup = BeautifulSoup(html_text, "lxml")print(soup.name,type(soup.name))
print(soup.attrs)返回结果:
[document] <class 'str'>
{}

2.2.4、Comment对象

Comment是 BeautifulSoup 提供的一个对象类型,用于表示 HTML 或 XML 文档中的注释内容,例如 <!-- 这是一个注释 -->。它是 bs4.element.Comment类型,同时也是 字符串(str)的子类,所以你可以像操作字符串一样操作注释内容,但同时它也保留了“可导航”的特性(比如可以访问父节点、兄弟节点等)。

from bs4 import BeautifulSoup, Commenthtml = """
<div>这是正常内容<!-- 这是一个注释 --><p>这是一个段落</p><!-- 另一个注释 -->
</div>
"""soup = BeautifulSoup(html, 'html.parser')# 遍历 div 的所有子节点
for child in soup.div.children:print(child)print(type(child))print("---")
输出结果为:    
这是正常内容<class 'bs4.element.NavigableString'>
---
<!-- 这是一个注释 -->
<class 'bs4.element.Comment'>
---
<p>这是一个段落</p>
<class 'bs4.element.Tag'>
---
<!-- 另一个注释 -->
<class 'bs4.element.Comment'>
---
  • <!-- 这是一个注释 -->被解析成了一个 Comment类型 的节点。
  • 它和 NavigableString(文本)、Tag(标签)一样,都是 BeautifulSoup 文档树的 节点类型之一
  • 你可以通过 .children.contents遍历到它,也可以通过条件判断筛选出注释节点。
2.2.4.1、遍历html中的所有注释
for child in soup.body.children:if isinstance(child, Comment):print("发现注释节点:", child)
2.2.4.2、删除注释
from bs4 import BeautifulSoup, Commenthtml = """
<div>Hello<!-- 注释1 --><p>内容</p><!-- 注释2 -->
</div>
"""
soup = BeautifulSoup(html, 'html.parser')# 找到所有注释节点并删除
for comment in soup.find_all(string=lambda text: isinstance(text, Comment)):comment.extract()  # 从文档树中移除该节点print(soup.prettify())

2.2.5、搜索文档树

在BeautifulSoup中最常用的是find()和find_all(),当然还有其他的。比如find_parent() 和 find_parents()、 find_next_sibling() 和 find_next_siblings() 、find_all_next() 和 find_next()、find_all_previous() 和 find_previous() 等等。

find_all( name , attrs , recursive , **string** , **kwargs )
  • 功能:搜索当前tag的所有tag子节点,并判断是否符合过滤器的条件。返回值类型是bs4.element.ResultSet

  • 参数说明

    • name 参数:可以查找所有名字为 name 的tag。

    • attr 参数:就是tag里的属性。

    • string 参数:搜索文档中字符串的内容。

    • recursive 参数: 调用tag的 find_all() 方法时,Beautiful Soup会检索当前tag的所有子孙节点。如果只想搜索tag的直接子节点,可以使用参数 recursive=False 。

find( name , attrs , recursive , string , **kwargs )
  • 功能:与find_all()类似,只不过只返回找到的第一个值。返回值类型是bs4.element.Tag。
  • 参数说明
    • name 参数:可以查找所有名字为 name 的tag。
    • attr 参数:就是tag里的属性。
    • string 参数:搜索文档中字符串的内容。
    • recursive 参数: 调用tag的 find_all() 方法时,Beautiful Soup会检索当前tag的所有子孙节点。如果只想搜索tag的直接子节点,可以使用参数 recursive=False 。

三、爬虫脚本

3.1、爬取图片

import requests
from bs4 import BeautifulSoup
import os
import timeclass BeautifulPicture():def __init__(self, web_url, path):  # 类的初始化操作self.headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.1 (KHTML, like Gecko) Chrome/22.0.1207.1 Safari/537.1'}  # 给请求指定一个请求头来模拟chrome浏览器self.web_url = web_url  # 要访问的网页地址self.folder_path = path  # 设置图片要存放的文件目录def get_pic(self,pic_url_data):print('开始网页get请求')r = self.request(self.web_url)print('开始获取所有a标签')all_a = BeautifulSoup(r.text, 'lxml').find_all('img')  # 获取网页中的所有a标签,一般图片都存放在img标签下print('开始创建文件夹')self.mkdir(self.folder_path)  # 创建文件夹print('开始切换文件夹')os.chdir(self.folder_path)  # 切换路径至上面创建的文件夹i = 0print(all_a)for a in all_a:  # 循环每个标签,获取标签中图片的url并且进行网络请求,最后保存图片try:img_str = a[pic_url_data]  # 获取a标签中图片的url地址,需要先查看网站中url的属性值print('-'*40+'\r\n'+f"a标签中第{i+1}个style内容是:", img_str)# first_pos = img_str.find('"') + 1# print(first_pos)# second_pos = img_str.find('"', first_pos)# print(second_pos)# img_url = img_str[first_pos: second_pos]  # 使用Python的切片功能截取双引号之间的内容img_name = str(i+1)if not img_str.startswith(('http:', 'https:')):img_url = 'https:' + img_strself.save_img(img_url, img_name)  # 调用save_img方法来保存图片i = int(i) + 1except KeyError as e:print("标签不存在:", e)def save_img(self, url, name):  # 保存图片print('开始请求图片地址,过程会有点长...')if url.find('https') != -1:print(url)img = self.request(url)time.sleep(1)file_name = name + '.jpg'if img is not None:print('开始保存图片')with open(file_name, 'wb') as f:f.write(img.content)print(file_name, '图片保存成功!')def request(self, url):  # 返回网页的responsetry:r = requests.get(url, headers=self.headers)  # 像目标url地址发送get请求,返回一个response对象。有没有headers参数都可以。print(r.status_code)if r.status_code == 200:return r    # return的结果需要判断请求是否成功,成功就返回,未成功就返回Noneelse:return Noneexcept Exception as e:print("http请求报错", e)return Nonedef mkdir(self, path):  # 这个函数创建文件夹path = path.strip()isExists = os.path.exists(path)if not isExists:print('创建名字叫做', path, '的文件夹')os.makedirs(path)print('创建成功!')else:print(path, '文件夹已经存在了,不再创建')if __name__ == "__main__":beauty = BeautifulPicture("https://www.51miz.com/so-sucai/3581313.html", r'E:\pic\mizi')  # 创建类的实例beauty.get_pic("data-layzeload")  # 执行类中的方法

参考:

https://blog.csdn.net/huaweichenai/article/details/136480489

https://gitcode.csdn.net/65e83f7d1a836825ed78b70b.html

http://www.dtcms.com/a/572694.html

相关文章:

  • Ubuntu 24.04 安装 FreeSWITCH 完整教程
  • LeetCode(python)——49.字母异位词分组
  • Redis 性能优化与故障排查指南
  • 24.java openCV4.x 入门-Imgproc之轮廓凸包与凹陷检测(形状识别)
  • IDEA 插件推荐
  • 虚拟 DOM(Virtual DOM)的工作原理及其性能优化机制
  • git详细使用教程
  • 北京工程工程建设交易信息网站和城乡建设部网站
  • soular零基础学习,如何通过工作台聚合TikLab所有工具链
  • 建立企业网站电商网站建设开题报告
  • css font-size 的妙用
  • Jenkins安装部署
  • 阿里云 CDN + 静态资源(图片 / JS/CSS)缓存优化
  • 荣耀前端开发面试题及参考答案
  • shtml怎么做网站建设学校网站多钱
  • Navicat 连接 SQL Server 报错 [08001] 超时错误(258)的排查与解决方案
  • Linux 使用 `wait` 函数回收子进程
  • 八股训练营第 8 天 | TCP连接三次握手的过程?TCP连接四次挥手的过程?HTTP的Keep-Alive是什么?
  • 【计算网络学习笔记】网络基础之网络协议栈
  • CyberSecEval 2
  • wordpress 两栏专业seo培训学校
  • 【浏览器CORS问题解决方案】SpringBoot+Vue3前后端全覆盖:浏览器跨域问题的多样化解决方案
  • 论述AI和人类的分工
  • 第四阶段C#通讯开发-6:Socket之UDP
  • 广州建设网站公司简介百度seo排名推广
  • 四自由度机械臂运动学与动力学分析
  • Spring Security 使用
  • Web3开发中的前端、后端与合约:角色定位与协作逻辑
  • 神经网络—— 人工神经网络
  • GroupNet:基于多尺度神经网络的交互推理轨迹预测