爬虫实战:如何优雅地抓取网页中隐藏在伪元素(::before)里的文本?

简介: 本文揭秘爬虫中“页面可见但代码抓不到”的元凶——CSS伪元素(::before/::after)。它生成的文本不在DOM中,导致requests+BS4失效。文章剖析content四种来源,提供接口直采、CSS正则解析、渲染引擎+代理IP三阶方案,并附Selenium实战代码与避坑指南。(239字)
相信很多写过一段时间爬虫的同学大概率撞过这堵“隐形墙”:在浏览器里明明白白显示着"¥9.9"的价格,或者一段验证码文本,但当你切换到 DevTools 时,发现那个数字安安静静地挂在某个标签里。然而,当你用 requests 抓下来,再用 BeautifulSoup 或 XPath 去提取得时候,拿到的却是一个空列表。即便你认为是 JS 动态加载,一通 Selenium 渲染操作后,DOM 里依旧是一无所有。 这其实并非 JS 的锅,而是 CSS 伪元素(Pseudo-elements)在作祟。 今天这篇文章,我们就从技术根源出发,手把手教你如何破除这层“伪装”,并结合强大的代理工具实现稳定抓取。

根因剖析:文本去哪了?

问题的根源在于,你想要抓取的文本 根本就不在 DOM 树里 这些文本通常是 CSS 引擎根据 ::before ::after 伪元素的 content 属性在运行时凭空生成的。因为伪元素并不属于 DOM 节点集合,传统的 getElementById querySelector 或者 XPath 自然全都失效了。 在动手写代码前,我们必须先分清 content 的四种常见取值来源:
  • 写死的字符串:例如 content: "¥9.9";,值直接硬编码在 CSS 规则中。
  • attr()attr() 函数引用:例如 content: attr(data-price);,真实数据其实藏在 HTML 元素的 data-price 属性里。
  • url()url() 函数引入:插入的是一张图片而不是文字,遇到这种情况无法当做文本抓取,只能走结构化接口或 OCR。
  • counter()counter() 计数器:通常用于自动编号,值是渲染引擎在运行时算出来的,必须依赖浏览器渲染才能读取。

破局方案与实战演示

应对这种反爬,我们的决策优先级永远是: 能拿接口绝不渲染,能抓静态绝不上自动化
  • 首选方案:直采接口。绝大数伪元素只是装饰,背后都有明文 JSON 接口支撑。找到接口直接发纯 HTTP 请求,速度快且反爬压力小。
  • 次选方案:正则解析 CSS。如果 content 是写死的字符串或来自 attr(),可以直接把 CSS 抓下来用正则提取,不需要启动浏览器。
  • 终极方案:渲染引擎 + 代理 IP。当 CSS 是动态注入的,或者强制使用 counter() 渲染时,我们就必须祭出 Selenium / Playwright 并调用 window.getComputedStyle 来读取最终样式。

渲染抓取实战(配合爬虫代理)

走到终极方案时,一个无法回避的问题是:单个 IP 使用 Selenium 频繁访问,其极其明显的自动化特征会迅速触发验证码甚至封禁。浏览器需要加载完整资源、停留时间长,IP 暴露面极大。 在这类渲染型抓取中,因为会话连续性(如保持登录态、Cookie 验证、表单提交)的要求,一旦中途跳变 IP,会导致之前的验证全部失效。此时,我们需要使用代理厂商提供的 固定转发模式 爬虫代理 为例,其固定转发模式能够在一个特定时间窗口(通常 1 到 3 分钟)内保持出口 IP 一致,完美契合了渲染型会话的需要。 以下是基于 Selenium 接入固定转发代理,并抓取伪元素数据的完整示例代码:
# 推荐使用 selenium-wire 以支持原生不支持的代理账密认证
from seleniumwire import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
import time

# 1. 配置亿牛云代理(固定转发模式,保持IP在时间窗口内连续)
proxyHost = "t.16yun.cn"
proxyPort = "31111"
proxyUser = "username"      # 替换成你的账号
proxyPass = "password"      # 替换成你的密码

options = {
   
    "proxy": {
   
        "http":  f"http://{proxyUser}:{proxyPass}@{proxyHost}:{proxyPort}",
        "https": f"http://{proxyUser}:{proxyPass}@{proxyHost}:{proxyPort}",
        "no_proxy": "localhost,127.0.0.1" # 绕过本地请求
    }
}

# 2. 启动浏览器
driver = webdriver.Chrome(seleniumwire_options=options)

try:
    url = "https://example.com/product/123"
    driver.get(url)

    # 3. 避免 headless 时序陷阱,不能仅等待元素存在,要轮询判断 content 是否渲染
    # 定义一个自定义的等待函数,轮询获取 ::before 的内容
    deadline = time.time() + 10 # 10秒超时
    extracted_price = None

    while time.time() < deadline:
        # 执行 JS 获取 getComputedStyle
        val = driver.execute_script(
            "return getComputedStyle(document.querySelector('.price'), '::before').getPropertyValue('content');"
        )

        # 伪元素存在时,有效内容不会是空或 'none'
        if val and val != "none":
            # getPropertyValue('content') 返回的结果通常会包裹着额外的双引号或单引号
            extracted_price = val.strip('"').strip("'")
            break
        time.sleep(0.3)

    if extracted_price:
        print(f"成功抓取伪元素价格: {extracted_price}")
    else:
        print("伪元素内容未能在规定时间内渲染。")

finally:
    driver.quit()

进阶避坑指南

在实操过程中,再点出几个很容易踩坑的地方:
  1. 返回值自带引号:就像代码中展示的一样,使用 getPropertyValue('content') 取出来的如果是字符串,它会原样返回 ""¥9.9""。一定要用 strip('"').strip("'") 进行清洗,不然你的数据库里全都是脏数据。
  2. 警惕图片“文字”:如果 content 返回的是 url(...),请立刻停止尝试提取文本,那实质上是一张图片。你要么去找其他数据源接口,要么只能引入复杂的 OCR 链路。
  3. 不要轻信 attr()attr():如果通过静态解析 CSS 发现 content attr(data-price),千万别以为大功告成了,你拿到的只是一个表达式。你必须回到那个真实的 DOM 节点上,去提取它自身的 data-price 属性值。
一句话总结这套打法: 先判断来源,能拿接口绝不渲染;真要渲染,记得上稳定代理(如爬虫代理)保底 。希望这篇文章能帮你打破“看见抓不着”的窘境,让你的爬虫更上一层楼!
相关文章
|
10天前
|
人工智能 JSON 自然语言处理
让教学更智慧:用阿里云百炼工作流,自动生成中小学教材内容#小有可为#有温度的AI
通过可视化工作流编排,将大模型推理能力转化为标准化的教学内容生成引擎。教师只需输入教材标题和适用学段,即可自动获得结构完整、符合课程标准的章节内容,大幅降低备课门槛,助力教育资源均衡化。
486 126
|
19天前
|
Linux 程序员 数据格式
【2026最新】Notepad++下载、安装和使用一篇搞定(附中文版安装包)
Notepad++ 是一款免费开源、轻量高效的 Windows 文本编辑器,支持 C/Python/HTML 等 80+ 语言语法高亮、代码折叠、正则替换、编码转换及插件扩展,专为程序员与文本处理用户打造,完美替代系统记事本。(239字)
|
4天前
|
人工智能 缓存 安全
Claude Code 封号真实原因曝光,这次彻底不装了,直接针对国内开发者的账号下手?
Claude Code 封号潮背后:逆向扒出客户端隐写区域标记,Anthropic 政策收紧叠加 DeepSeek 7 月涨价,国产替代更紧迫。
|
6天前
|
人工智能 安全 Cloud Native
Higress 新发布:AI Gateway 能力增强,Gateway API 及其推理扩展持续打磨
增强 AI 网关能力,持续打磨 Gateway API 及其推理扩展。
343 124
|
5天前
|
人工智能 安全 程序员
终于,Claude Code 封号的原因被曝光了!竟然针对中国用户,植入隐形代码?!
通俗易懂地揭秘 Claude Code 封号的手段,分享一些自己对 AI 编程困境的思考,Codex、Cursor、DeepSeek、智谱 GLM、甚至是豆包,都有所行动了
343 1
|
14天前
|
机器学习/深度学习 人工智能 调度
🐴 HappyHorse 1.1 现已上线阿里云百炼!快来查收模型使用指南,现在调用享 6 折~
HappyHorse 1.1 是新一代视频生成大模型,全面升级动态表现力、角色一致性、指令遵循、视觉质感与音画协同能力。支持I2V/T2V/R2V三类生成,适配短剧、电商广告、品牌营销等场景,提供高质、流畅、可控的AI视频生产力。
842 5
🐴 HappyHorse 1.1 现已上线阿里云百炼!快来查收模型使用指南,现在调用享 6 折~
|
11天前
|
人工智能 定位技术 SEO
我学 GEO 第 15 天:终于知道AI GEO该如何做?
我是暴走的莉莉酱,边旅行边研究AI GEO的数字游民。专注普通人如何提升“AI可见度”——让AI在回答用户问题时准确识别、理解并推荐你。不讲玄学,只做可测、可调、可持续的GEO实践。
467 127