运维笔记

BeautifulSoup vs Scrapy:动态JS网站爬虫选型实战,别再被忽悠了

Infrastructure 技术可视化

开篇:别纠结了,先看场景

上周有个兄弟问我:“我该用 BeautifulSoup 还是 Scrapy 爬动态网站?”我直接回了一句:你连目标网站的反爬强度都没搞清楚,选啥都是白搭。

说实话,这俩根本不是同一维度的东西。BeautifulSoup 是轻量级解析库,Scrapy 是工业级爬虫框架。拿它们直接对比,就像问“螺丝刀和电钻哪个好”——你得先告诉我你要拧什么螺丝。

但既然大家都在问,我就把我在生产环境踩过的坑、压过的测、翻过的车,一次性说清楚。

核心差异:解析 vs 框架

维度BeautifulSoupScrapy
定位HTML/XML 解析库异步爬虫框架
依赖需搭配 requests、selenium 等内置下载器、调度器、中间件
并发单线程,需自己搞多线程原生异步,基于 Twisted
JS 渲染必须对接 Selenium/Playwright可对接 Splash/Scrapy-Playwright
学习曲线低,30分钟上手中等,理解架构需时间
反爬对抗需手动处理 cookies、headers内置中间件,可定制 Retry、Proxy
性能(静态)~200 req/s(单机)~800-1200 req/s(单机)
性能(动态 JS)受限于 Selenium,~10-30 req/s受限于 Splash/Playwright,~20-60 req/s

简单总结: 你如果只是爬个静态博客,BeautifulSoup 真香;但遇到动态 JS 网站,尤其是反爬拉满的那种,Scrapy 的框架优势就出来了。

动态 JS 渲染:两种方案的硬碰硬

BeautifulSoup + Selenium

这是大多数新手的选择。代码看起来挺简单:

from selenium import webdriver
from bs4 import BeautifulSoup

driver = webdriver.Chrome()
driver.get('https://example.com')
soup = BeautifulSoup(driver.page_source, 'html.parser')
# 开始解析...
driver.quit()

但这里有个坑:Selenium 启动浏览器实例的开销巨大。我测过,单次启动 Chrome 实例到页面加载完毕,平均耗时 2-3 秒。如果你要爬 1000 个页面,光是浏览器启动时间就占了 2000-3000 秒。

更恶心的是,有些网站会检测 navigator.webdriver 标志。Selenium 默认会把这个标志设为 true,反爬直接识别你是机器人。你得手动注入脚本覆盖:

options = webdriver.ChromeOptions()
options.add_argument('--disable-blink-features=AutomationControlled')
driver.execute_cdp_cmd('Page.addScriptToEvaluateOnNewDocument', {
    'source': 'Object.defineProperty(navigator, "webdriver", {get: () => undefined})'
})

说实话,这套组合拳打下来,代码已经变得又臭又长。

Scrapy + Scrapy-Playwright

Scrapy 2.0 之后,官方推荐用 scrapy-playwright 集成。这是我最喜欢的方案,因为:

  1. 复用浏览器上下文:不用每次请求都启动新浏览器
  2. 异步非阻塞:一个浏览器实例可以同时处理多个页面
  3. 自动管理 cookies 和 headers:Scrapy 的中间件机制天然支持

代码示例:

import scrapy
from scrapy_playwright.page import PageMethod

class MySpider(scrapy.Spider):
    name = 'dynamic_spider'
    
    custom_settings = {
        'DOWNLOAD_HANDLERS': {
            'http': 'scrapy_playwright.handler.ScrapyPlaywrightDownloadHandler',
            'https': 'scrapy_playwright.handler.ScrapyPlaywrightDownloadHandler',
        },
        'PLAYWRIGHT_BROWSER_TYPE': 'chromium',
        'CONCURRENT_REQUESTS': 32,
    }

    def start_requests(self):
        yield scrapy.Request(
            url='https://example.com',
            meta=dict(
                playwright=True,
                playwright_include_page=True,
                playwright_page_methods=[
                    PageMethod('wait_for_selector', 'div.content-loaded'),
                    PageMethod('evaluate', 'window.scrollTo(0, document.body.scrollHeight)'),
                ]
            )
        )

    async def parse(self, response):
        page = response.meta['playwright_page']
        # 此时页面已经渲染完成
        title = response.css('h1::text').get()
        # 还可以继续执行 JS
        data = await page.evaluate('() => JSON.parse(localStorage.getItem("data"))')
        yield {'title': title, 'data': data}
        await page.close()

性能数据对比(实测)

方案1000 页耗时内存占用反爬成功率
BS + Selenium(单线程)~45 分钟1.2GB+~60%
BS + Selenium(多线程 8)~8 分钟4.5GB+~55%
Scrapy + Playwright(并发 32)~2 分钟2.1GB~85%

我的结论: 如果目标网站有 Cloudflare、Akamai 这类 WAF,直接用 Scrapy + Playwright。BS+Selenium 在这种场景下基本是送人头。

反爬对抗:框架 vs 库的降维打击

这里我要说点得罪人的话:用 BeautifulSoup 做反爬对抗,基本是拿菜刀上战场。

Scrapy 的中间件机制太香了。举个例子,你要做 IP 轮换:

class RotateProxyMiddleware:
    def process_request(self, request, spider):
        request.meta['proxy'] = get_random_proxy()
    
    def process_response(self, request, response, spider):
        if response.status == 403:
            # 被 ban 了,重试并换 IP
            new_request = request.copy()
            new_request.meta['proxy'] = get_new_proxy()
            return new_request
        return response

BeautifulSoup 这边呢?你得在每次请求前手动设置代理,还要自己管理重试逻辑。代码很快就变成一坨屎山。

反爬对抗能力对比

功能BeautifulSoupScrapy
User-Agent 轮换手动内置中间件
IP 代理池手动中间件 + 扩展
请求重试手动内置 RetryMiddleware
Cookie 管理手动内置 CookieMiddleware
请求去重内置 DupeFilter
限速控制内置 AutoThrottle

什么时候该用 BeautifulSoup?

别误会,我不是说 BeautifulSoup 没用。它最适合的场景是:

  1. 一次性的小任务:比如爬个文档、抓个博客
  2. API 返回值解析:配合 requests 抓 JSON,用 BS 解析 HTML 片段
  3. 快速原型验证:先拿 BS 试试水,再决定要不要上框架

我团队里有个原则:如果爬取目标少于 50 个页面,直接用 BS。超过 100 个,必须上 Scrapy。

FAQ

BeautifulSoup 和 Scrapy 哪个更快?

看场景。静态页面 Scrapy 快 3-5 倍,动态页面快 2-3 倍。核心原因是 Scrapy 的异步架构和连接复用。

动态网站可以用 BeautifulSoup 吗?

可以,但必须搭配 Selenium 或 Playwright。问题在于性能瓶颈在浏览器渲染,而不是解析速度。用 BS 解析 Selenium 返回的 HTML,解析本身很快,但浏览器启动和页面加载才是真正的性能杀手。

Scrapy 学习曲线比 BeautifulSoup 陡峭吗?

是的,Scrapy 需要理解 Spider、Item Pipeline、Middleware 等概念。但一旦上手,你会发现这些抽象层让代码更清晰、更可维护。我见过最烂的 BS 代码是 2000 行的一个文件,而同样的功能用 Scrapy 只需要 200 行分散在 5 个文件中。

哪种方案反爬能力更强?

毫无疑问是 Scrapy。它的中间件架构让你可以轻松插入代理、Cookie、验证码处理等逻辑。BS 需要你从零搭建这些基础设施。


最后说一句:工具只是工具,别被工具绑架。 选 BeautifulSoup 还是 Scrapy,取决于你的目标网站、团队能力、以及项目规模。如果拿不准,先用 BS 做 POC,然后果断切 Scrapy。