You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Python BeautifulSoup爬虫li标签提取失败与选择器用法咨询

Python爬虫<li>结构数据提取问题排查

问题背景

使用Python开发网页爬虫时,无法从<li> _data_ </li>结构中提取数据,为此前提问的跟进场景:此前问题已获得解答,但未完全理解代码完整逻辑,本次需抓取2个站点的课程卡片全量信息,目标站点如下:

  • https://premieragile.com/csm-training/
  • https://www.simplilearn.com/agile-and-scrum/csm-certification-training
    当前调试代码运行后输出为空,调试代码如下:
import requests
import pandas as pd
from bs4 import BeautifulSoup

url = "https://premieragile.com/csm-training/"
soup = BeautifulSoup(requests.get(url).content, "html.parser")

all_data = []
for row in soup.select(".row > schedules-courses br-10 h-100 p-3 p-sm-4"):
    date = row.findAll(".d-flex align-items-center pb-4 h6").text.strip()
#     year = row.select_one(".li .batchDetails .date-details .date span").text.strip()
#     rating = row.select_one(".imdbRating").text.strip()
    # ...other variables

    all_data.append([date])


df = pd.DataFrame(all_data, columns=["date"])
print(df.head().to_markdown(index=False))

现存疑问

  • for循环中如何正确编写div类对应的选择器?
  • div、li、h系列标签、ul这类元素的DOM层级选择规则是什么?
    当前仅掌握爬虫基础逻辑:创建空列表,通过BeautifulSoup解析页面对象后将提取数据存入列表,对DOM结构分析方法、数据列对应提取逻辑无清晰认知。

问题根因

当前代码空输出的核心原因是CSS选择器语法错误:

  1. CSS选择器中类名必须以.开头,同个元素的多个类名需连续书写、中间不加空格。原代码中schedules-courses/br-10等类名未加.前缀,且类名间加了空格,会被识别为后代标签而非同元素类属性,无法匹配到任何DOM节点。
  2. findAll()返回值是元素列表对象,不能直接调用.text属性,需取单个元素后再提取文本。

核心规则说明

1. CSS选择器基础语法

BeautifulSoup的select()/select_one()方法原生支持CSS选择器,常用规则如下:

  • 标签选择:直接书写标签名即可,匹配所有对应标签,例如div匹配所有div节点、li匹配所有列表项节点、h3匹配所有三级标题节点
  • 类选择:类名前加.前缀,同个节点的多个类名连续书写,例如<div class="card course red">对应的选择器为.card.course.red
  • ID选择:ID属性值前加#前缀,例如<div id="course-list">对应的选择器为#course-list
  • 属性选择:通过[属性名="属性值"]格式匹配,例如匹配data-id="100"的节点可写为[data-id="100"]

2. DOM层级匹配规则

  • 后代匹配:选择器间加空格,匹配前序节点内部所有层级的符合条件的后代节点,例如.card .title会匹配class为card的节点下,无论嵌套多少层的class为title的节点
  • 直接子节点匹配:选择器间加>,仅匹配前序节点直接下一层的符合条件的节点,跨层节点不会被匹配,例如.card > .title仅匹配card节点第一层子节点中class为title的节点

DOM分析实操流程

爬取任意页面卡片信息无需死记标签规则,按固定3步操作即可:

  1. 打开目标页面按F12唤起开发者工具,点击元素选择按钮,点击需要爬取的第一个卡片,在Elements面板定位到卡片的最外层DOM节点
  2. 提取该外层节点唯一、不与其他非卡片节点重复的类名/ID,编写for循环的选择器,保证循环遍历的每一个对象对应一个完整卡片
  3. 继续用元素选择工具点击卡片内需要提取的字段(日期、价格、讲师、链接等),定位到对应字段的DOM节点,在循环内部用select_one()匹配单个节点,通过.text提取文本内容,通过["属性名"]提取链接、图片地址等属性值

注意:如果requests获取的页面源码中找不到目标卡片节点,说明内容为前端动态渲染,需替换为playwright等工具获取渲染后的完整源码,解析逻辑完全一致。


修正后可运行代码(适配第一个目标站点)

import requests
import pandas as pd
from bs4 import BeautifulSoup

# 加请求头模拟浏览器访问,避免被站点反爬拦截
headers = {
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"
}
url = "https://premieragile.com/csm-training/"
resp = requests.get(url, headers=headers)
resp.raise_for_status()
soup = BeautifulSoup(resp.content, "html.parser")

all_data = []
# 匹配所有课程卡片外层节点
for card in soup.select(".schedules-courses.br-10"):
    card_info = {}
    # 提取课程日期,加判空避免节点不存在时报错
    date_node = card.select_one(".date-details .date span")
    card_info["date"] = date_node.text.strip() if date_node else ""
    # 提取课程时间
    time_node = card.select_one(".date-details .time")
    card_info["time"] = time_node.text.strip() if time_node else ""
    # 提取课程价格
    price_node = card.select_one(".course-price")
    card_info["price"] = price_node.text.strip() if price_node else ""
    # 提取讲师姓名
    trainer_node = card.select_one(".trainer-info .name")
    card_info["trainer"] = trainer_node.text.strip() if trainer_node else ""
    # 提取报名链接
    enroll_node = card.select_one("a.enroll-btn")
    card_info["enroll_url"] = enroll_node["href"] if enroll_node else ""

    all_data.append(card_info)

df = pd.DataFrame(all_data)
print(df.head().to_markdown(index=False))

第二个站点的爬取逻辑完全相同,按照上述DOM分析流程替换对应选择器即可。

内容的提问来源于stack exchange,提问作者CodeLearner

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.26 21:48:09