1. BeautifulSoup4库介绍

和lxml一样,Beautiful Soup也是一个HTML/XML的解析器,主要功能也是如何解析和提取HTML/XML数据。

lxml只会局部遍历,Beautiful Soup是基于HTML DOM(Document Object Model)的,会载入整个文档,解析整个DOM树,因此时间和内存开销都会大很多,所以性能要低于lxml。

DOM树

image-20250920145858440

安装方法:使用pip安装

pip install beautifulsoup4

BeautifulSoup4解析引擎

我们使用requests库发送请求获取html,获得的是html字符串,只有正则表达式(re)才可以直接对html字符串进行解析(最快的方式),而对于html字符串我们无法使用xpath语法和bs4语法进行直接提取,需要通过lxml或者bs4对html字符串进行解析,解析为html页面才能进行数据提取。

在xpath中我们使用lxml进行解析,但是在bs4中,我们有很多的解析器对网页进行解析。

解析器 优势 安装方式
html.parser Python 内置,无需额外安装 内置
lxml 解析速度快,支持 HTML 和 XML pip install lxml
html5lib 处理不规范 HTML 更优,模拟浏览器解析 pip install html5lib

2. BeautifulSoup4数据获取

使用bs4库对以下这个index.html进行数据提取

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>招聘信息页面</title>
    <style>
        .company {
            border: 1px solid #ccc;
            padding: 10px;
            margin-bottom: 15px;
            border-radius: 5px;
        }
        .job {
            background-color: #f9f9f9;
            padding: 8px;
            margin: 5px 0;
            border-left: 3px solid #007bff;
        }
        .high-salary {
            color: red;
            font-weight: bold;
        }
    </style>
</head>
<body>
    <div class="page-header">
        <h1>互联网行业招聘信息汇总</h1>
        <p>包含多家知名企业的各类岗位招聘</p>
    </div>
    <div class="company-list">
        <div class="company" id="company-alibaba">
            <h2>阿里巴巴集团</h2>
            <div class="job" data-id="ali-1">
                <h3>Java 高级开发工程师</h3>
                <p>薪资:<span class="salary">25k-40k</span>/月</p>
                <p>要求:本科及以上学历,5年以上 Java 开发经验,熟悉分布式系统</p>
                <p>部门:阿里云事业部</p>
            </div>
            <div class="job" data-id="ali-2">
                <h3>前端技术专家</h3>
                <p>薪资:<span class="salary high-salary">30k-50k</span>/月</p>
                <p>要求:硕士及以上学历,精通 Vue、React 等框架,有大型项目架构经验</p>
                <p>部门:淘宝技术部</p>
            </div>
        </div>
        <div class="company" id="company-tencent">
            <h2>腾讯公司</h2>
            <div class="job" data-id="tencent-1">
                <h3>后端开发工程师</h3>
                <p>薪资:<span class="salary">20k-35k</span>/月</p>
                <p>要求:本科及以上学历,熟悉 Go 语言,有微服务开发经验优先</p>
                <p>部门:微信事业群</p>
            </div>
            <div class="job" data-id="tencent-2">
                <h3>测试开发工程师</h3>
                <p>薪资:<span class="salary">18k-30k</span>/月</p>
                <p>要求:本科及以上学历,熟悉自动化测试框架,有性能测试经验</p>
                <p>部门:腾讯云测试部</p>
            </div>
            <div class="job" data-id="tencent-3">
                <h3>产品经理</h3>
                <p>薪资:<span class="salary">22k-38k</span>/月</p>
                <p>要求:本科及以上学历,有社交类产品经验,逻辑思维清晰</p>
                <p>部门:QQ 产品部</p>
            </div>
        </div>
        <div class="company" id="company-baidu">
            <h2>百度公司</h2>
            <div class="job" data-id="baidu-1">
                <h3>算法工程师</h3>
                <p>薪资:<span class="salary high-salary">35k-60k</span>/月</p>
                <p>要求:博士学历,机器学习、深度学习方向,有顶会论文优先</p>
                <p>部门:人工智能实验室</p>
            </div>
            <div class="job" data-id="baidu-2">
                <h3>大数据开发工程师</h3>
                <p>薪资:<span class="salary">25k-45k</span>/月</p>
                <p>要求:本科及以上学历,熟悉 Hadoop、Spark 等生态,有大数据平台开发经验</p>
                <p>部门:百度大数据部</p>
            </div>
        </div>
    </div>
    <div class="footer">
        <p>招聘信息仅供参考,具体以企业官方发布为准</p>
    </div>
</body>
</html>

image-20250920151034791

1.导入库

from bs4 import BeautifulSoup

2.创建 BeautifulSoup 对象(解析文档)

with open("index.html","r",encoding='utf-8') as f:
    soup = BeautifulSoup(f, "lxml")
  • (1)获取所有的div标签
divs = soup.find_all("div")
print(divs)
  • (2)获取指定的div标签(从第二个到最后一个)
divs = soup.find_all('div')[1:]
print(div)
  • (3)获取拥有指定属性的标签写法1(“id='company-baidu'的div标签”):用逗号分隔属性值
		divs = soup.find_all('div',id='company-baidu')
		for div in divs:
    	 print(div)
    	 print("*"*50)

image-20250920152028075

  • 获取拥有指定属性的标签写法2(“id='company-baidu'的div标签”):设置attrs参数,将属性名称和属性值输入为字典中的键值对,

image-20250920152557954

image-20250920152631786

  • (4)获取拥有多个属性的标签
# class是Python的关键字,使用方法1的话不能直接作为参数名,需要用class_ 代替
# 对于带有连字符的属性(如 data-id),不能直接作为关键字参数传递,需要用字典形式
divs = soup.find_all('div',attrs = {'class':'job','data-id':'baidu-1'})
print(divs)

image-20250920153803869

  • (5)获取拥有多个属性的标签(span标签的class属性值)

    • 通过下标的方式
    spanlist = soup.find_all("span")
        for span in spanlist:
            class_value = span['class']
            print(class_value)
    
    • 通过attrs属性获取
    spanlist = soup.find_all("span")
        for span in spanlist:
            class_value = span.attrs["class"]
            print(class_value)
    
  • (6)获取所有职位的信息

from bs4 import BeautifulSoup
def extract_job_info(job_title):
    try:
        title = job_title.find('h3').string or "未知岗位"
        salary = job_title.find('span',class_='salary').string or "薪资未公开"
        p_tags = job_title.find_all('p')
        requirement = p_tags[1].string.split(":")[1] if len(p_tags)>=2 else "要求未公开"
        department = p_tags[2].string.split(":")[1] if len(p_tags)>=3 else "部门未公开"
        return {
            "title": title,
            "salary": salary,
            "requirement": requirement,
            "department": department
        }

    except(AttributeError,IndexError):
        print(f"岗位解析错误:{e}")
        return None

with open("index.html","r",encoding='utf-8') as f:
    soup = BeautifulSoup(f, "lxml")
    divs = soup.find_all("div",class_ = 'company')
    for div in divs:
        #获取公司名称
        company_name = div.find('h2').string
        company_name = company_name.string if company_name else "未知公司"
        print(f"公司名称:{company_name}")

        #获取当前公司所有岗位
        job_titles = div.find_all('div',class_='job')
        #遍历所有岗位
        for job in job_titles:
            job_info = extract_job_info(job)
            if job_info:
                print(f"岗位: {job_info['title']}")
                print(f"薪资: {job_info['salary']}")
                print(f"要求: {job_info['requirement']}")
                print(f"部门: {job_info['department']}\n")
        print("*"*50)

3. bs4爬虫实战

爬虫实战步骤:

1.导入模块

2.定义URL和请求头参数

3.发送html请求,获取html字符串

4.实例化BeautifulSoup对象

5.获取数据

6.存储数据

3.1 BeautifulSoup4实战练手——新浪微博

  • 任务: 爬取热度榜的内容和热度值

image-20250921110741653

代码示例:

def get_weibo_top():
    #定义URL和请求头
    url = "https://s.weibo.com/top/summary"
    cookies = {
        'SUB': '_2AkMfNI0Df8NxqwFRmvkdzGviZY9xzwDEieKpaHzYJRMxHRl-yT9kqmkJtRB6NLSj7MvpaHOQcuWqXYtKn5BN9uhI3rp8',
        'SUBP': '0033WrSXqPxfM72-Ws9jqgMF55529P9D9WF0kEihP.YcLRquXMaVBn8b',
        'SINAGLOBAL': '4129228658367.541.1758171541219',
        '_s_tentry': '-',
        'Apache': '5258822436993.217.1758423981101',
        'ULV': '1758423981102:2:2:1:5258822436993.217.1758423981101:1758171541220',
    }

    headers = {
        'accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7',
        'accept-language': 'zh-CN,zh;q=0.9',
        'cache-control': 'max-age=0',
        'priority': 'u=0, i',
        'sec-ch-ua': '"Chromium";v="140", "Not=A?Brand";v="24", "Google Chrome";v="140"',
        'sec-ch-ua-mobile': '?0',
        'sec-ch-ua-platform': '"macOS"',
        'sec-fetch-dest': 'document',
        'sec-fetch-mode': 'navigate',
        'sec-fetch-site': 'none',
        'sec-fetch-user': '?1',
        'upgrade-insecure-requests': '1',
        'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/140.0.0.0 Safari/537.36',
        # 'cookie': 'SUB=_2AkMfNI0Df8NxqwFRmvkdzGviZY9xzwDEieKpaHzYJRMxHRl-yT9kqmkJtRB6NLSj7MvpaHOQcuWqXYtKn5BN9uhI3rp8; SUBP=0033WrSXqPxfM72-Ws9jqgMF55529P9D9WF0kEihP.YcLRquXMaVBn8b; SINAGLOBAL=4129228658367.541.1758171541219; _s_tentry=-; Apache=5258822436993.217.1758423981101; ULV=1758423981102:2:2:1:5258822436993.217.1758423981101:1758171541220',
    }

    #发送请求,获取html字符串并使用解析器解析
    res = requests.get(url, headers=headers,cookies=cookies)
    res.encoding = "utf-8"
    html = BeautifulSoup(res.text,'lxml')
    #使用bs4库方法获取目标数据
    infos = html.find('div',class_="data",id="pl_top_realtimehot").find('tbody').find_all('tr')[1:]
    i = 0
    for info in infos:
        try:
            i += 1
            # print(info)
            print("*"*60)
            title = info.find('a').text
            hot = info.find('span').text
            status = info.find('i').text if info.find('i') else None
            print(f"{title}\t热度:{hot}\t状态:{status}")
        except Exception as e:
            print(f"错误信息{e}")
            continue
    print(f"总共输出{i}条热搜")
if __name__ == "__main__":
    get_weibo_top()

image-20250921113620544

3.2 BeautifulSoup4实战练手——汽车之家新闻

任务: 爬取汽车之家指定页数的新闻页面

爬取的数据: 新闻标题、新闻链接、发表时间、新闻概要

image-20250921120217216

代码示例:

def get_car_news(num):
    #1.定义URL的列表,和请求参数headers
    urls = []
    for i in range(1,num+1):
        url = f"https://www.autohome.com.cn/news/{i}/#liststart"
        urls.append(url)
    headers = {
        "User-Agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/140.0.0.0 Safari/537.36"
    }
    news_list = []
    #2.遍历URL 发送请求
    for url in urls:
        res = requests.get(url,headers=headers)
        res.encoding = "gb2312"
        #解析HTML字符串,获取想要数据
        html = BeautifulSoup(res.text,'lxml')
        #分析页面,新闻数据在四个ul标签下,获取全部ul标签然后遍历,并存储数据
        uls = html.find_all('ul',class_="article")
        for ul in uls:
            titles = ul.find_all('h3')
            hrefs = ul.find_all('a',href=True)
            times = ul.find_all('span',class_="fn-left")
            descriptions = ul.find_all('p')

            for(title,href,time,description) in zip(titles,hrefs,times,descriptions):
                title = title.text
                href = href.get("href")[2:]
                time = time.text
                description = description.text
                news = {
                    "标题":{title},
                    "链接":{href},
                    "发布时间":{time},
                    "描述":{description},
                }
                news_list.append(news)
    #3.打印数据
    for news in news_list:
        print(news)
        print("*"*60)

if __name__ == "__main__":
 	  #获取前两页的新闻
    get_car_news(2)