首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >Python 智能爬虫:从“能爬”到“聪明地爬”

Python 智能爬虫:从“能爬”到“聪明地爬”

原创
作者头像
用户12818508
发布于 2026-11-15 10:23:50
发布于 2026-11-15 10:23:50
230
举报

传统的爬虫脚本往往很“脆”——网站改一个 class 名,整个解析逻辑就崩了;遇到 JavaScript 渲染的页面,requests 拿到的是一堆空壳;稍微有点反爬意识的站点,TLS 指纹一检测,IP 就被封了。所谓“智能爬虫”,要解决的核心问题就是适应性和隐蔽性:让爬虫能在网站结构微调后继续工作,能在浏览器指纹层面看起来像真人,甚至能用自然语言描述来提取数据,而不是写死一堆 CSS 选择器。

下面从三个层面展开,每个层面附上可直接运行的代码。


层面一:用 curl_cffi 绕过 TLS 指纹检测

很多开发者以为换了 User-Agent 就万事大吉,但 Cloudflare、Akamai 等 CDN 厂商早就在边缘节点做 JA3 指纹过滤了。Python 标准库 requests 底层用的是固定的 OpenSSL 握手参数,和 Chrome 浏览器的 TLS 指纹完全不同,一眼就能被识别为自动化脚本。

curl_cffi 是目前最成熟的解决方案,它直接模拟真实浏览器的 TLS 和 HTTP2 指纹:

代码语言:javascript
复制
pip install curl_cffi
代码语言:javascript
复制
from curl_cffi import requests

# 模拟 Chrome 120 的完整指纹(TLS + HTTP2 + Header 顺序)
resp = requests.get(
    "https://httpbin.org/headers",
    impersonate="chrome120",
    timeout=15
)

print(resp.status_code)
print(resp.json()["headers"]["User-Agent"])

impersonate 参数支持 chrome110 到 chrome124 以及 safari15_5、safari17_0、firefox133 等多个版本。如果你的目标站点开了 Cloudflare 的“Under Attack”模式,这一步往往是区分“秒封”和“正常返回”的关键。


层面二:用 Playwright 处理动态渲染,同时降低被检测风险

当页面内容由 JavaScript 异步加载时,纯 HTTP 请求拿不到数据,必须上浏览器。Playwright 本身很好用,但默认配置下 navigator.webdriver 为 true,Chrome 自动化扩展的标志也很明显,有经验的站点一眼就能识别。

下面的代码在启动浏览器时做了三件事:禁用自动化控制特征、设置接近真人的视口和语言、使用真实的 User-Agent:

代码语言:javascript
复制
from playwright.sync_api import sync_playwright
from bs4 import BeautifulSoup

def fetch_rendered_page(url: str) -> str:
    with sync_playwright() as p:
        browser = p.chromium.launch(
            headless=True,
            args=[
                "--disable-blink-features=AutomationControlled",
                "--disable-features=IsolateOrigins,site-per-process",
            ]
        )
        context = browser.new_context(
            viewport={"width": 1920, "height": 1080},
            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"
            ),
            locale="zh-CN",
        )
        page = context.new_page()
        page.goto(url, wait_until="networkidle", timeout=30000)
        html = page.content()
        browser.close()
        return html


# 使用示例:抓取一个动态渲染的页面
html = fetch_rendered_page("https://wltx.lvxiair.com/")
soup = BeautifulSoup(html, "html.parser")

for quote in soup.select(".quote"):
    text = quote.select_one(".text").get_text(strip=True)
    author = quote.select_one(".author").get_text(strip=True)
    print(f"{author}: {text[:60]}...")

wait_until="networkidle" 会等待网络请求基本停止后再取内容,比固定 sleep 更可靠。如果目标站点对 headless=True 也有检测,可以改用 headless=False 配合虚拟显示(如 xvfb)运行。


层面三:用 Scrapling 的自适应选择器应对页面改版

这是“智能”体现得最明显的一层。Scrapling 是一个较新的爬虫库,它的核心卖点是自适应选择器:当你保存了某个元素的选择器后,如果网站改版导致 class 名变化,Scrapling 会用相似度算法自动重新定位到最可能的目标元素,而不是直接返回空。

安装:

代码语言:javascript
复制
pip install "scrapling[fetchers]"
scrapling install
代码语言:javascript
复制
from scrapling.fetchers import Fetcher

# 开启自适应模式(全局)
Fetcher.adaptive = True

page = Fetcher.get("https://wlt.lvxiair.com")

# 第一次正常提取,同时保存元素“指纹”
products = page.css("li.product", auto_save=True)

data = []
for product in products:
    name = product.css("h2::text").get()
    price = product.css(".price::text").get()
    data.append({"name": name, "price": price})

print(f"提取到 {len(data)} 个商品")
for item in data[:3]:
    print(item)

关键在 auto_save=True。当网站结构后续发生变化时,你重新运行同样的代码,Scrapling 会基于之前保存的指纹做模糊匹配,尽量找回原来的元素。虽然不能保证 100% 准确,但在类名微调、DOM 层级小幅变动的情况下,能省去大量手动修选择器的时间。


组合使用:一个完整的“智能”抓取函数

把上面三层能力串起来,写一个实用的抓取函数:优先用 curl_cffi 走轻量 HTTP 请求(快、省资源),如果页面内容明显不完整(比如关键元素缺失),自动降级到 Playwright 渲染,解析层用 Scrapling 的自适应选择器兜底。

代码语言:javascript
复制
from curl_cffi import requests
from playwright.sync_api import sync_playwright
from scrapling.parser import Adaptor

def smart_fetch(url: str, required_selector: str, impersonate: str = "chrome120"):
    """
    智能抓取:先 HTTP,不够再上浏览器,解析用自适应选择器。
    """
    # 第一层:轻量 HTTP + 浏览器指纹伪装
    resp = requests.get(url, impersonate=impersonate, timeout=15)
    html = resp.text

    # 用 Scrapling 解析并检查关键元素是否存在
    page = Adaptor(html, url=url)
    if page.css(required_selector):
        print("[HTTP] 直接命中,无需渲染")
        return page

    # 第二层:降级到浏览器渲染
    print("[Playwright] HTTP 内容不完整,切换渲染模式")
    with sync_playwright() as p:
        browser = p.chromium.launch(
            headless=True,
            args=["--disable-blink-features=AutomationControlled"],
        )
        context = browser.new_context(
            viewport={"width": 1920, "height": 1080},
            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"
            ),
        )
        pg = context.new_page()
        pg.goto(url, wait_until="networkidle", timeout=30000)
        rendered_html = pg.content()
        browser.close()

    return Adaptor(rendered_html, url=url)


# 使用:以 quotes.toscrape.com 的 JS 版本为例
result_page = smart_fetch(
    "https://wltx.lvxiair.com/",
    required_selector=".quote .text"
)

quotes = result_page.css(".quote .text::text").getall()
print(f"最终获取 {len(quotes)} 条名言")
print(quotes[0][:80] + "..." if quotes else "未找到内容")

这个模式的好处是按需开销:大部分静态页面走 HTTP 路径,毫秒级完成;只有确实需要渲染的页面才启动浏览器,避免不必要的资源浪费。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

目录
  • 层面一:用 curl_cffi 绕过 TLS 指纹检测
  • 层面二:用 Playwright 处理动态渲染,同时降低被检测风险
  • 层面三:用 Scrapling 的自适应选择器应对页面改版
  • 组合使用:一个完整的“智能”抓取函数
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档