Python 一直是网络爬取的首选语言,因为其生态系统深厚、成熟且模块化。你很少只用一个工具构建爬虫:你需要选择一个获取器、一个解析器,以及(当页面需要浏览器时)一个自动化层,然后将它们组合在一起。难点在于知道哪个库擅长哪项工作,这样你就不会用一个完整的爬取框架去解析一个页面,也不会在普通 HTTP 请求就足够时去使用无头浏览器。
本文介绍五个涵盖绝大多数实际爬取工作的 Python 库:Requests、Beautiful Soup、lxml、Scrapy 和 Selenium。对于每个库,你将了解它的实际用途、擅长之处,以及何时使用,并附有简短的代码片段,让代码比文字更直观。读完本文,你应该能够为特定目标规划出合适的技术栈,而不是默认使用上次用过的工具。
如何选择 Python 爬取库
四个问题决定了大多数选择,它们与下面的库一一对应。第一,你如何获取 HTML:是简单的请求,还是真正运行 JavaScript 的浏览器?第二,你如何从标记中提取数据:是用于杂乱 HTML 的宽容解析器,还是用于整洁文档的快速、严格解析器?第三,规模如何:一个页面,还是带有队列、重试和管道的数千个页面?第四,目标是否在客户端渲染内容,导致你下载的 HTML 在脚本运行之前几乎是空的?
将工具与答案匹配,技术栈自然就成形了。Requests 获取静态页面,Beautiful Soup 和 lxml 解析它们,Scrapy 处理大规模爬取,Selenium 在页面只有在 JavaScript 执行后才存在时驱动浏览器。没有哪个是万能的,所以文末的表格将每个工具映射到其擅长的工作。
Requests
Requests 是大多数 Python 爬虫的起点 HTTP 客户端。它专注于一件事:发送请求并返回响应,会话、Cookie、请求头和重定向都通过简洁的 API 处理。它不解析 HTML,也不执行 JavaScript,因此单独使用时只能看到服务器返回的原始标记。对于静态页面、公开目录以及直接返回 HTML 或 JSON 的端点,这就足够了,而且因为没有浏览器开销,速度很快。
每当你想要的内容出现在初始响应中时,就将 Requests 作为获取层。将其与解析器(Beautiful Soup 或 lxml)配合使用,将该响应转换为结构化数据。它的主要限制也正是其速度的反面:它无法爬取通过客户端 JavaScript 构建内容的页面,因为它从不执行任何脚本。
import requests resp = requests.get("https://example.com") print(resp.status_code) # 200 html = resp.text # raw HTML, ready to parse
Beautiful Soup
Beautiful Soup(当前版本为 Beautiful Soup 4)是经典的 Python 解析器,其持久生命力源于一个特质:它能优雅地处理格式不规范的标记。现实中的 HTML 充满了未闭合的标签和破损的嵌套,而 Beautiful Soup 能将即使是杂乱的文档转换成可搜索标签、类或属性的 Python 对象树。其 API 读起来几乎像普通英语,这也是它成为初学者通常学习的第一个解析器的原因。
当标记不规则、项目规模中小或可读性比原始速度更重要时,使用 Beautiful Soup。它本身不获取页面,所以它位于 Requests 之后,并且在大型文档上比 lxml 慢。对于大多数爬取工作,这个差距无关紧要。我们的 Beautiful Soup in Python 指南深入介绍了其选择器和树导航。
from bs4 import BeautifulSoup soup = BeautifulSoup(html, "html.parser") title = soup.find("h1").text links = [a["href"] for a in soup.select("a[href]")]
lxml
lxml 是速度选项。它基于 C 库 libxml2 和 libxslt 构建,解析大型 HTML 和 XML 文档比纯 Python 解析器快得多,并且提供完整的 XPath 支持,让你可以对深度嵌套的标记进行精确、富有表达力的查询。当你处理数千个文档或从结构化 XML 数据流中提取数据时,性能差异就成为选择它的理由。
当速度至关重要、文档较大或需要 XPath 而非 CSS 选择器时,选择 lxml。其代价是它比 Beautiful Soup 更严格,因此非常破损的标记可能会让它出错,而且 API 对初学者不太友好。许多团队同时使用两者:将 lxml 作为 Beautiful Soup 的底层解析器,以获得宽容导航和快速解析的优势。如果你在权衡查询风格,XPath 与 CSS 选择器对比对两者进行了正面比较。
from lxml import html as lxml_html tree = lxml_html.fromstring(html) prices = tree.xpath("//span[@class='price']/text()")
Scrapy
Scrapy 不是一个解析器,它是一个完整的爬取框架。上述库各自完成一个部分,而 Scrapy 提供了整个管道:一个异步引擎,可以并发获取多个页面、请求调度、链接跟随、重试,以及内置的结构化数据导出(JSON、CSV 或 XML)。它专为需要爬取大量页面的项目而构建,将这些工作组织成蜘蛛、数据项定义和处理管道,而不是单个脚本。
当规模和结构是核心需求时,选择 Scrapy:定期爬取、数以万计的 URL,或需要流经清洗和存储步骤的数据。这种能力的代价是比快速的 Requests 加解析器脚本有更陡的学习曲线和更多的设置,因此对于一次性页面来说过于复杂。与 Requests 一样,原生 Scrapy 不执行 JavaScript,但在目标需要渲染时可与浏览器工具集成。
import scrapy class BookSpider(scrapy.Spider): name = "books" start_urls = ["https://books.toscrape.com"] def parse(self, response): for book in response.css("article.product_pod"): yield {"title": book.css("h3 a::attr(title)").get()}
Selenium
Selenium 是浏览器自动化工具。它驱动真实浏览器(Chrome、Firefox 等),使页面完全按用户所见加载,包括所有 JavaScript。这使它成为动态网站的解决方案,在这些网站中,你下载的 HTML 几乎是空的,直到脚本运行并注入内容。由于它控制真实浏览器,它还可以点击按钮、填写表单、滚动并等待元素出现,这对于只有在交互后才加载的内容至关重要。
当目标在客户端渲染且普通请求不返回有用数据时,选择 Selenium。其代价是重量:运行浏览器比 HTTP 请求更慢、更耗资源,而且无法像请求客户端那样读取原始响应状态码。仅在确实需要渲染时使用它,其他静态内容保持使用轻量的 Requests 加解析器组合。更广泛的模式请参见如何爬取 JavaScript 网站。
from selenium import webdriver driver = webdriver.Chrome() driver.get("https://example.com") html = driver.page_source # fully rendered DOM driver.quit()
如果你只需要渲染而不需要完整的 UI 自动化,Playwright 等现代替代品可以通过一个 API 运行多个浏览器,功能集相似。Selenium 仍然是支持最广泛、文档最丰富的选项,这也是它保持为默认浏览器自动化选择的原因,但值得了解这个领域不止一种工具。
库的横向对比
这五个部分适合少数几个插槽。下表将每个工具映射到其擅长的工作及其工具类型,让你可以据此选择:用 Requests 获取,用 Beautiful Soup 或 lxml 解析,用 Scrapy 处理大规模爬取,用 Selenium 渲染。
| 库 | 最适合 | 类型 |
|---|---|---|
| Requests | 获取静态页面和 API | HTTP 客户端 |
| Beautiful Soup | 解析杂乱或不规则的 HTML | HTML 解析器 |
| lxml | 快速解析、大型文档、XPath | HTML/XML 解析器 |
| Scrapy | 大规模爬取和管道 | 爬取框架 |
| Selenium | JavaScript 渲染的交互式页面 | 浏览器自动化 |
注意,没有哪一行是万能答案。实际爬虫会将它们组合:静态页面用 Requests 加 Beautiful Soup,爬取规模扩大时用 Scrapy,页面需要浏览器时用 Selenium。技巧在于将工具与目标匹配,而不是选择一个最爱。
封锁才是真正的瓶颈
选对库,你的代码就正确了,但网络依然是对抗性的。许多目标会用限速、IP 封锁、CAPTCHA,以及只有在 JavaScript 运行后才出现的内容来对抗自动化流量。此时限制因素不再是你的解析器,而是在数千次请求中保持不被封锁,而这项工作(代理轮换、浏览器渲染、重试逻辑)超出了任何单个爬取库的设计范围。
无论你用哪个库解析,Crawlbase Crawling API 都可以作为其底层的获取层。你发送一个 URL,它在服务端处理 IP 轮换、对 JavaScript 密集页面的浏览器渲染以及封锁重试,然后将干净的 HTML 直接返回给 Beautiful Soup、lxml 或 Scrapy。它与你的 Python 技术栈并行工作,而不是替代它,你保留解析逻辑,同时不再维护反封锁基础设施。
这种分工是实用的结论:继续使用适合你解析和爬取需求的 Python 库,让托管获取层承担它从未打算解决的网络问题。更广泛的方法参见如何在不被封锁的情况下爬取网站。
负责任地爬取
无论你构建什么技术栈,都要克制地爬取。尊重网站的服务条款和 robots.txt,专注于公开可用的数据而不是你无权访问的登录后内容,并保持合理的请求速率,避免给你所依赖的服务器造成压力。负责任的节奏也很实际:温和、自我标识清晰的流量被限速或封锁的可能性远低于激进的爬取,因此良好的礼仪与可靠的爬取往往指向同一个方向。
核心要点
- 没有单一最佳库。实际爬虫结合了获取器、解析器,有时还有浏览器,因此将每个工具与工作匹配,而不是选择一个最爱。
- Requests 获取,解析器解析。Requests 快速拉取静态页面和 API,然后 Beautiful Soup 或 lxml 将该 HTML 转换为结构化数据。
- Beautiful Soup 宽容,lxml 快速。对于杂乱标记和可读性使用 Beautiful Soup,对于速度、大型文档和 XPath 使用 lxml。
- Scrapy 适合规模。当你有数千个页面、队列、重试和管道时,才需要完整的爬取框架,而不是一次性脚本。
- Selenium 渲染 JavaScript。当页面在脚本运行前是空的,就驱动真实浏览器,并接受随之而来的速度和资源成本。
常见问题
Python 网络爬取的最佳库是什么?
没有单一最佳库,因为它们做的是不同的工作。对于大多数静态页面,用 Requests 获取加 Beautiful Soup 解析是最简单可靠的技术栈。需要速度或 XPath 时加上 lxml,爬取规模扩大到数千页时加上 Scrapy,目标只用 JavaScript 渲染内容时加上 Selenium。
我应该用 Beautiful Soup 还是 lxml?
当标记杂乱或可读性更重要时,用 Beautiful Soup,因为它能优雅地处理破损的 HTML,读起来几乎像普通英语。当你解析大型文档、需要最高速度或需要 XPath 查询时,用 lxml。它们不互斥:lxml 可以作为 Beautiful Soup 的底层解析器,同时提供宽容导航和快速解析。
我什么时候需要 Scrapy 而不是 Requests?
对于一次性或小型任务,使用 Requests 加解析器。当你需要爬取大量页面并需要内置并发、请求调度、链接跟随、重试和结构化导出时,切换到 Scrapy。Scrapy 将项目组织成蜘蛛和管道,对于单个页面这是多余的开销,但在规模上是真正的优势。
Python 能爬取 JavaScript 渲染的页面吗?
可以,但单独用 Requests 不行,因为它从不运行 JavaScript。对于客户端渲染的页面,使用像 Selenium 这样的浏览器自动化工具,它在真实浏览器中加载页面,使脚本执行并注入内容。代价是浏览器比 HTTP 请求更慢更重,因此仅对确实需要渲染的页面使用。参见如何用 Python 爬取 JavaScript 页面。
我的 Python 爬虫为什么会被封锁?
大多数封锁来自网络,而不是你的代码:请求太快太多、目标标记了你的 IP,或者出现了 CAPTCHA 挑战。解决方法是轮换 IP、合理节奏,以及在需要时渲染。托管获取层(如爬取 API)处理轮换、渲染和重试,让你的解析库专注于提取数据。
一个项目需要所有五个库吗?
不需要。选择目标所需的库。典型的静态站点爬虫只需 Requests 和 Beautiful Soup。只在需要速度或 XPath 时添加 lxml,在大规模爬取时添加 Scrapy,在需要 JavaScript 渲染时添加 Selenium。大多数项目使用其中两到三个,组合覆盖获取、解析,以及在需要时的浏览器渲染。
大规模爬取任何站点,无需与基础设施对抗。
Crawlbase 负责处理代理、指纹和 CAPTCHA,让你的团队专注于交付数据流水线,而非维护爬取管道。1,000 次请求免费,无需信用卡。
