大家好,我是何三,80后老猿,独立开发者
今天给大家推荐的这个python库是在github上坐拥31.9K星的crawl4ai——一个开源的对大语言模型有好的web爬虫工具。
开源地址:https://github.com/unclecode/crawl4ai
简介
Crawl4AI是开源Python库,旨在简化网页抓取并轻松从网页中提取有价值的信息。无论是将其集成为REST API还是直接在Python项目中使用,Crawl4AI都提供了强大、灵活且完全异步的解决方案,专为LLM和AI应用程序量身定制。
Crawl4AI 提供:
- 异步爬网程序 AsyncWebCrawler。
- 通过 BrowserConfig 和 CrawlerRunConfig 配置浏览器和运行设置。
- 通过 DefaultMarkdownGenerator 自动将 HTML 转换为 Markdown(支持可选筛选器)。
- 多种提取策略(基于 LLM 或基于“传统”CSS/XPath)。
在本指南结束时,您将执行基本爬网,生成 Markdown,尝试两种提取策略,并爬取使用 “Load More” 按钮或 JavaScript 更新的动态页面。
快速开始
- 基本安装
pip install crawl4ai
这将安装核心 Crawl4AI 库以及必要的依赖项。目前还没有包含任何高级功能(如 transformer 或 PyTorch)。
- 初始设置和诊断
2.1 运行 Setup 命令 安装后,调用:
crawl4ai-setup
它有什么作用?- 安装或更新所需的 Playwright 浏览器(Chromium、Firefox 等) - 执行作系统级别的检查(例如,Linux 上缺少库) - 确认您的环境已准备好进行爬网
2.2 诊断
或者,您可以运行诊断来确认一切正常:
crawl4ai-doctor
此命令尝试: - 检查 Python 版本兼容性 - 验证 Playwright 安装 - 检查环境变量或库冲突
如果出现任何问题,请按照其建议进行作(例如,安装其他系统软件包)并重新运行 。crawl4ai-setup
- 验证安装:A Simple Crawl(如果您已经运行了,请跳过此步骤crawl4ai-doctor)
下面是一个演示基本爬网的最小 Python 脚本。为了清楚起见,它使用了我们新的 BrowserConfig 和 CrawlerRunConfig,但此示例中没有传递自定义设置:
import asyncio
from crawl4ai import AsyncWebCrawler, BrowserConfig, CrawlerRunConfig
async def main():
async with AsyncWebCrawler() as crawler:
result = await crawler.arun(
url="https://www.h3blog.com",
)
print(result.markdown[:300]) # Show the first 300 characters of extracted text
if __name__ == "__main__":
asyncio.run(main())
预期结果: - 无头浏览器会话加载 - Crawl4AI 返回 ~300 个字符的 markdown。 如果出现错误,请重新运行或手动确保 Playwright 安装正确。
- 高级安装(可选)
警告: 仅在您真正需要它们时才安装它们。它们带来了更大的依赖项,包括大型模型,这可能会显著增加磁盘使用率和内存负载。
4.1 Torch、Transformer 或 All
- 文本聚类 (Torch)
pip install crawl4ai[torch]
crawl4ai-setup
安装基于 PyTorch 的功能(例如,余弦相似性或高级语义分块)。
- 变形金刚
pip install crawl4ai[transformer]
crawl4ai-setup
添加基于 Hugging Face 的摘要或生成策略。
- 所有功能
pip install crawl4ai[all]
crawl4ai-setup
- (可选)预取模型
crawl4ai-download-models
此步骤在本地缓存大型模型(如果需要)。仅当您的工作流程需要时,才执行此作。
- Docker(实验性)
我们提供了一种临时的 Docker 测试方法。它不稳定,并且可能会在未来版本中中断。我们计划在未来的稳定版本(2025 年第一季度)中对 Docker 进行重大改造。如果您仍想尝试:
docker pull unclecode/crawl4ai:basic
docker run -p 11235:11235 unclecode/crawl4ai:basic
然后,您可以发出 POST 请求以执行爬网。在我们的新 Docker 方法准备就绪之前(计划在 2025 年 1 月或 2 月),不建议使用生产环境。http://localhost:11235/crawl
# 安装库
pip install -U crawl4ai
# 安装最新预览版本
pip install crawl4ai --pre
# 安装后设置
crawl4ai-setup
# 验证安装是否成功
crawl4ai-doctor
如果您遇到任何与浏览器相关的问题,您可以手动安装它们:
python -m playwright install --with-deps chromium
特点
-
Markdown生成
- 清洁Markdown:生成结构清晰、格式准确的干净Markdown。
- 适配Markdown:基于启发式的过滤,去除噪音和无关部分,便于AI处理。
- 引用和参考文献:将页面链接转换为编号参考列表,并提供干净的引用。
- 自定义策略:用户可以根据特定需求创建自己的Markdown生成策略。
- BM25算法:使用基于BM25的过滤提取核心信息并移除不相关内容。
-
结构化数据提取
- LLM驱动提取:支持所有LLM(开源和专有)进行结构化数据提取。
- 分块策略:实施分块(基于主题、正则表达式、句子级别)以实现针对性的内容处理。
- 余弦相似性:根据用户查询找到相关的内容块,用于语义提取。
- 基于CSS的提取:使用XPath和CSS选择器快速进行基于模式的数据提取。
- 模式定义:定义自定义模式,从重复模式中提取结构化的JSON。
-
浏览器集成
- 管理浏览器:使用用户拥有的浏览器,完全控制,避免被检测为机器人。
- 远程浏览器控制:连接到Chrome开发者工具协议,实现大规模远程数据提取。
- 会话管理:保存浏览器状态并在多步骤爬取中重复使用。
- 代理支持:无缝连接代理并支持身份验证,确保安全访问。
- 完整的浏览器控制:修改头信息、cookie、用户代理等,定制爬取设置。
- 多浏览器支持:兼容Chromium、Firefox和WebKit。
- 动态视口调整:自动调整浏览器视口以匹配页面内容,确保完整渲染和捕获所有元素。
-
爬取与抓取
- 媒体支持:提取图片、音频、视频以及响应式图像格式如
srcset
和picture
。 - 动态爬取:执行JS并等待异步或同步加载,以便动态内容提取。
- 截图:在爬取过程中捕捉页面截图,用于调试或分析。
- 原始数据爬取:直接处理原始HTML(
raw:
)或本地文件(file://
)。 - 全面链接提取:提取内部链接、外部链接和嵌入的iframe内容。
- 可定制钩子:在每个步骤定义钩子以定制爬取行为。
- 缓存:缓存数据以提高速度并避免冗余获取。
- 元数据提取:从网页中检索结构化的元数据。
- IFrame内容提取:无缝提取嵌入的iframe内容。
- 懒加载处理:等待图片完全加载,确保不会因懒加载而遗漏任何内容。
- 全页扫描:模拟滚动以加载并捕获所有动态内容,适合无限滚动页面。
- 媒体支持:提取图片、音频、视频以及响应式图像格式如
-
部署
- Docker化设置:带有API服务器的优化Docker镜像,易于部署。
- API网关:一键部署,通过安全令牌认证实现基于API的工作流。
- 可扩展架构:设计用于大规模生产,并优化服务器性能。
- DigitalOcean部署:适用于DigitalOcean及类似平台的准备部署配置。
-
附加功能
- 隐身模式:通过模仿真实用户来避免被检测为机器人。
- 标签为基础的内容提取:基于自定义标签、标题或元数据细化爬取。
- 链接分析:提取并分析所有链接以进行详细的数据探索。
- 错误处理:强大的错误管理,确保平稳执行。
- CORS与静态服务:支持基于文件系统的缓存和跨域请求。
- 清晰文档:简化且更新的指南,便于入门和高级使用。
- 社区认可:承认贡献者和拉取请求,保持透明度。
Crawl4AI 是做什么的?
Crawl4AI 是一个功能丰富的爬虫和爬虫,旨在:
- 生成 Clean Markdown:非常适合 RAG 管道或直接摄取到 LLM 中。 结构化提取:使用基于 CSS、XPath 或 LLM 的提取解析重复模式。
- 高级浏览器控制:钩子、代理、隐身模式、会话重用 - 精细控制。
- 高性能:并行爬取、基于块的提取、实时用例。
- 开源:没有强制的 API 密钥,没有付费专区——每个人都可以访问他们的数据。
核心理念: - 数据大众化:免费使用、透明且高度可配置。 - LLM 友好:处理最少、结构良好的文本、图像和元数据,因此 AI 模型可以轻松使用它们。
代码案例
- 执行 JavaScript 和使用 CSS 选择器
通过执行自定义 JavaScript 并使用 CSS 选择器定位特定元素来增强抓取任务。
import asyncio
from crawl4ai import AsyncWebCrawler
async def main():
async with AsyncWebCrawler(verbose=True) as crawler:
js_code = [
"const loadMoreButton = Array.from(document.querySelectorAll('button')).find(button => button.textContent.includes('Load More')); loadMoreButton && loadMoreButton.click();"
]
result = await crawler.arun(
url="https://www.h3blog.com",
js_code=js_code,
css_selector="article.tease-card",
bypass_cache=True
)
print(result.extracted_content)
if __name__ == "__main__":
asyncio.run(main())
- 使用代理
通过代理路由爬取任务增强隐私和访问。
import asyncio
from crawl4ai import AsyncWebCrawler
async def main():
async with AsyncWebCrawler(verbose=True, proxy="http://127.0.0.1:7890") as crawler:
result = await crawler.arun(
url="https:/www.h3blog.com",
bypass_cache=True
)
print(result.markdown)
if __name__ == "__main__":
asyncio.run(main())
- 无LLM 提取结构化数据
利用JsonCssExtractionStrategyCSS
选择器精确提取结构化数据。
import asyncio
import json
from crawl4ai import AsyncWebCrawler
from crawl4ai.extraction_strategy import JsonCssExtractionStrategy
async def extract_news_teasers():
schema = {
"name": "News Teaser Extractor",
"baseSelector": ".wide-tease-item__wrapper",
"fields": [
{"name": "category", "selector": ".unibrow span[data-testid='unibrow-text']", "type": "text"},
{"name": "headline", "selector": ".wide-tease-item__headline", "type": "text"},
{"name": "summary", "selector": ".wide-tease-item__description", "type": "text"},
{"name": "time", "selector": "[data-testid='wide-tease-date']", "type": "text"},
{
"name": "image",
"type": "nested",
"selector": "picture.teasePicture img",
"fields": [
{"name": "src", "type": "attribute", "attribute": "src"},
{"name": "alt", "type": "attribute", "attribute": "alt"},
],
},
{"name": "link", "selector": "a[href]", "type": "attribute", "attribute": "href"},
],
}
extraction_strategy = JsonCssExtractionStrategy(schema, verbose=True)
async with AsyncWebCrawler(verbose=True) as crawler:
result = await crawler.arun(
url="https://www.nbcnews.com/business",
extraction_strategy=extraction_strategy,
bypass_cache=True,
)
assert result.success, "Failed to crawl the page"
news_teasers = json.loads(result.extracted_content)
print(f"Successfully extracted {len(news_teasers)} news teasers")
print(json.dumps(news_teasers[0], indent=2))
if __name__ == "__main__":
asyncio.run(extract_news_teasers())
- 使用 OpenAI 提取结构化数据
利用 OpenAI 的功能动态提取和结构化数据。
import os
import asyncio
from crawl4ai import AsyncWebCrawler
from crawl4ai.extraction_strategy import LLMExtractionStrategy
from pydantic import BaseModel, Field
class OpenAIModelFee(BaseModel):
model_name: str = Field(..., description="Name of the OpenAI model.")
input_fee: str = Field(..., description="Fee for input token for the OpenAI model.")
output_fee: str = Field(..., description="Fee for output token for the OpenAI model.")
async def main():
async with AsyncWebCrawler(verbose=True) as crawler:
result = await crawler.arun(
url='https://openai.com/api/pricing/',
word_count_threshold=1,
extraction_strategy=LLMExtractionStrategy(
provider="openai/gpt-4o",
api_token=os.getenv('OPENAI_API_KEY'),
schema=OpenAIModelFee.schema(),
extraction_type="schema",
instruction="""From the crawled content, extract all mentioned model names along with their fees for input and output tokens.
Do not miss any models in the entire content. One extracted model JSON format should look like this:
{"model_name": "GPT-4", "input_fee": "US$10.00 / 1M tokens", "output_fee": "US$30.00 / 1M tokens"}."""
),
bypass_cache=True,
)
print(result.extracted_content)
if __name__ == "__main__":
asyncio.run(main())
- 会话管理和动态内容抓取
处理复杂的场景,例如抓取通过 JavaScript 加载动态内容的多个页面。
import asyncio
import re
from bs4 import BeautifulSoup
from crawl4ai import AsyncWebCrawler
async def crawl_typescript_commits():
first_commit = ""
async def on_execution_started(page):
nonlocal first_commit
try:
while True:
await page.wait_for_selector('li.Box-sc-g0xbh4-0 h4')
commit = await page.query_selector('li.Box-sc-g0xbh4-0 h4')
commit = await commit.evaluate('(element) => element.textContent')
commit = re.sub(r'\s+', '', commit)
if commit and commit != first_commit:
first_commit = commit
break
await asyncio.sleep(0.5)
except Exception as e:
print(f"Warning: New content didn't appear after JavaScript execution: {e}")
async with AsyncWebCrawler(verbose=True) as crawler:
crawler.crawler_strategy.set_hook('on_execution_started', on_execution_started)
url = "https://github.com/microsoft/TypeScript/commits/main"
session_id = "typescript_commits_session"
all_commits = []
js_next_page = """
const button = document.querySelector('a[data-testid="pagination-next-button"]');
if (button) button.click();
"""
for page in range(3): # Crawl 3 pages
result = await crawler.arun(
url=url,
session_id=session_id,
css_selector="li.Box-sc-g0xbh4-0",
js=js_next_page if page > 0 else None,
bypass_cache=True,
js_only=page > 0
)
assert result.success, f"Failed to crawl page {page + 1}"
soup = BeautifulSoup(result.cleaned_html, 'html.parser')
commits = soup.select("li.Box-sc-g0xbh4-0")
all_commits.extend(commits)
print(f"Page {page + 1}: Found {len(commits)} commits")
await crawler.crawler_strategy.kill_session(session_id)
print(f"Successfully crawled {len(all_commits)} commits across 3 pages")
if __name__ == "__main__":
asyncio.run(crawl_typescript_commits())
Crawl4AI 性能基准
Crawl4AI 专为速度和效率而设计,其表现始终优于许多付费服务。以下是我们性能基准的概览:
Service | Time Taken | Content Length | Images |
---|---|---|---|
Firecrawl | 7.02s | 42074字符 | 49 |
Crawl4AI (Simple Crawl) | 1.60s | 18238字符 | 49 |
Crawl4AI (With JS Exec.) | 4.64s | 40869字符 | 89 |
关键要点:
- Simple Crawl:Crawl4AI比 Firecrawl快 4 倍以上。
- With JavaScript Exec:即使在执行 JavaScript 加载额外内容(将图像数量加倍)时,Crawl4AI 的速度仍然比 Firecrawl 的简单爬取速度快得多。
🔥 福利时间:在公众号【何三笔记】后台回复关键词「20250217」,免费领取《清华大学出品DeepSeek使用精髓》:
- 【清华大学第一版】DeepSeek从入门到精通.pdf
- 【清华大学第二版】DeepSeek赋能职场.pdf
- 【清华大学第三版】普通人如何抓住DeepSeek红利.pdf
- 【清华大学第四版】DeepSeek+DeepResearch:让科研像聊天一样简单.pdf
- 【清华大学第五版】:DeepSeek与AI幻觉.pdf
最后放上项目地址,建议先star⭐防丢失:
https://github.com/unclecode/crawl4ai
大家有什么想实现的AI应用场景?欢迎在评论区留言