别再手动爬数据了!Skyvern用AI操控浏览器,完成你想都不敢想的自动化任务

别再手动爬数据了!Skyvern用AI操控浏览器,完成你想都不敢想的自动化任务

别再手动爬数据了!Skyvern用AI操控浏览器,完成你想都不敢想的自动化任务


为什么值得关注 / 项目简介

传统浏览器自动化的困境

在 Skyvern 出现之前,如果你想实现浏览器自动化,通常会面临以下挑战:

  • Selenium/Playwright 需要编写大量 xpath 选择器,一旦网站改版就全部失效
  • 反爬机制越来越严格,验证码、IP限制让数据采集难上加贵
  • 复杂的多步骤表单操作(比如需要先登录、再查询、最后导出)需要手动维护大量状态
  • 无法处理动态加载的内容,页面交互逻辑需要写大量等待代码

Skyvern带来了什么改变

Skyvern 是一个基于 AI 的浏览器自动化框架,它的核心思想是:让大语言模型直接理解和操控网页。你只需要用自然语言描述你想做什么,Skyvern 就会:

  • 自动分析网页结构,理解各个元素的作用
  • 规划完成任务的步骤序列
  • 像真人一样与网页交互——点击、输入、滚动、等待
  • 智能处理验证码、弹窗、加载等待等边界情况
  • 自动提取结构化的目标数据

想象一下,你告诉 Skyvern:「帮我登录这个网站,搜索关键词’人工智能’,然后把所有搜索结果的标题和链接整理成一个表格」,它就能自动完成这一切——不需要写任何 xpath,不需要分析网站结构,只需要描述你的需求。

这个项目目前在 GitHub 上已经获得了大量关注,它重新定义了浏览器自动化的可能性边界。


环境搭建 / Getting Started

系统要求

在开始之前,确保你的系统满足以下要求:

  • Python 3.10 或更高版本
  • 至少 4GB 可用内存
  • 稳定的网络连接(需要访问外部网站)
  • 推荐使用 Linux 或 macOS 系统(Windows 也支持但可能需要额外配置)

使用Docker快速启动(推荐)

Docker 是最简单的方式,可以避免依赖冲突问题:

# 拉取最新的 Skyvern 镜像
docker pull skyvern/skyvern:latest

# 运行容器
docker run -p 8080:8080 \
  -e OPENAI_API_KEY="your-api-key-here" \
  -e SKYVERNET_DB_URL="postgresql://user:pass@localhost:5432/skyvern" \
  skyvern/skyvern:latest

服务启动后,可以通过 http://localhost:8080 访问 Web 界面。

本地安装

如果你更喜欢本地开发环境,可以按以下步骤操作:

# 创建虚拟环境(推荐使用 uv 或 conda)
python -m venv skyvern-env

# 激活虚拟环境
source skyvern-env/bin/activate  # Linux/macOS
# skyvern-env\Scripts\activate   # Windows

# 克隆仓库
git clone https://github.com/Skyvern-AI/skyvern.git
cd skyvern

# 安装依赖
pip install -e .

# 或者使用 uv 安装(更快)
uv pip install -e .

必需的环境变量

Skyvern 需要配置一些环境变量才能正常工作:

# 创建环境变量配置文件
cat > .env << 'EOF'
# LLM API 配置(必需)
OPENAI_API_KEY="sk-xxxxxxxxxxxxxxxxxxxx"

# 数据库配置(使用 SQLite 用于开发)
SKYVERNET_DB_URL="sqlite:///./skyvern.db"

# 可选配置
SKYVERNET_TEMP_DIR="/tmp/skyvern"
LOG_LEVEL="INFO"

# 如果使用 Playwright 作为浏览器后端
SKYVERNET_BROWSER_TYPE="playwright"
EOF

验证安装

安装完成后,运行以下命令验证配置是否正确:

# 验证脚本 - 保存为 verify_installation.py
"""
Skyvern 安装验证脚本
用于检查所有依赖和配置是否正确
"""

import sys

def verify_python_version():
    """检查 Python 版本"""
    version = sys.version_info
    if version.major < 3 or (version.major == 3 and version.minor < 10):
        print(f"✗ Python 版本过低: {version.major}.{version.minor}")
        print("  Skyvern 需要 Python 3.10 或更高版本")
        return False
    print(f"✓ Python 版本: {version.major}.{version.minor}.{version.micro}")
    return True

def verify_dependencies():
    """检查关键依赖包"""
    required_packages = [
        'fastapi',
        'playwright',
        'openai',
        'httpx',
        'sqlalchemy',
        'pydantic'
    ]

    missing = []
    for package in required_packages:
        try:
            __import__(package)
            print(f"✓ {package} 已安装")
        except ImportError:
            print(f"✗ {package} 缺失")
            missing.append(package)

    return len(missing) == 0

def verify_api_key():
    """检查 API Key 配置"""
    import os
    api_key = os.environ.get('OPENAI_API_KEY', '')

    if not api_key:
        print("✗ OPENAI_API_KEY 未设置")
        print("  请设置环境变量: export OPENAI_API_KEY='your-key'")
        return False

    if not api_key.startswith('sk-'):
        print("✗ API Key 格式可能不正确(应以 sk- 开头)")
        return False

    print("✓ OPENAI_API_KEY 已配置")
    return True

if __name__ == "__main__":
    print("=" * 50)
    print("Skyvern 安装验证")
    print("=" * 50)

    results = [
        verify_python_version(),
        verify_dependencies(),
        verify_api_key()
    ]

    print("=" * 50)
    if all(results):
        print("✓ 所有检查通过!可以开始使用 Skyvern")
    else:
        print("✗ 部分检查失败,请修复上述问题")
    print("=" * 50)

运行验证脚本:

python verify_installation.py

安装 Playwright 浏览器

Skyvern 使用 Playwright 进行浏览器控制,需要安装浏览器:

# 安装 Playwright 浏览器(使用 uvx 或 npx)
uvx playwright install chromium

# 或者使用 npm
npx playwright install chromium

# 如果需要其他浏览器
npx playwright install firefox
npx playwright install webkit

核心概念与架构

Skyvern 的工作原理

Skyvern 的架构设计非常精妙,理解它的工作流程能帮助你更好地使用这个工具:

用户需求(自然语言)
        ↓
┌─────────────────────────────────────┐
│         LLM 分析层                    │
│  理解任务意图 → 规划执行步骤           │
└─────────────────────────────────────┘
        ↓
┌─────────────────────────────────────┐
│         浏览器控制层                   │
│  Playwright/Selenium 操作浏览器       │
└─────────────────────────────────────┘
        ↓
┌─────────────────────────────────────┐
│         元素识别层                    │
│  OCR + 视觉模型 定位交互元素           │
└─────────────────────────────────────┘
        ↓
┌─────────────────────────────────────┐
│         数据提取层                    │
│  结构化输出 → JSON/CSV/数据库         │
└─────────────────────────────────────┘

关键组件详解

1. Task(任务)

Task 是 Skyvern 的核心执行单元,每个自动化任务对应一个 Task 实例:

# Task 的基本结构
task_config = {
    "url": "https://example.com/search",      # 目标网页
    "instruction": "搜索'Python教程',提取前10个结果的标题和链接",
    "webhook_url": "https://your-server.com/callback",  # 完成回调(可选)
    "max_steps": 20,                           # 最大执行步数
    "提取字段": ["title", "url", "description"]  # 期望提取的字段
}

2. Agent(代理)

Agent 负责理解任务并规划执行策略:

"""
Agent 的决策流程
当遇到需要决策的页面时,Agent 会分析当前状态并决定下一步操作
"""

class AgentDecisionExample:
    """
    模拟 Agent 的决策过程
    """

    def analyze_page_state(self, page_content):
        """分析页面当前状态"""
        # Agent 会识别:
        # - 当前在哪个页面
        # - 有哪些可交互元素
        # - 是否出现了预期外的元素(弹窗、验证码等)
        pass

    def plan_next_action(self, task_goal, current_state):
        """规划下一步动作"""
        # Agent 可能的决策:
        # - 点击某个按钮
        # - 填写表单
        # - 滚动页面
        # - 等待加载
        # - 提取数据
        pass

3. Browser(浏览器)

Browser 组件封装了实际的浏览器操作:

"""
Browser 组件支持的浏览器操作
"""

# 可用的浏览器操作示例
browser_operations = {
    # 基础操作
    "navigate": "打开指定URL",
    "click": "点击元素",
    "type": "输入文本",
    "hover": "鼠标悬停",
    "scroll": "滚动页面",

    # 等待操作
    "wait_for_selector": "等待元素出现",
    "wait_for_navigation": "等待页面导航完成",
    "wait_for_timeout": "等待指定时间",

    # 特殊操作
    "screenshot": "截图",
    "extract_content": "提取页面内容",
    "handle_popup": "处理弹窗"
}

实战教程:从入门到精通

第一个示例:自动搜索并提取数据

让我们从最简单的例子开始——使用 Skyvern 自动访问 Google 搜索某个关键词,并提取搜索结果:

"""
第一个 Skyvern 示例:自动化网页搜索与数据提取
保存为: example_01_basic_search.py
"""

from skyvern.forge import Forge
from skyvern.forge.sdk.schemas.task import Task

async def main():
    """主函数:执行自动化搜索任务"""

    # 初始化 Forge 实例
    # Forge 是 Skyvern 的核心编排器
    forge = Forge()

    # 创建任务配置
    search_task = Task(
        url="https://www.google.com",
        instruction="""
        1. 在搜索框中输入 "Python 自动化测试"
        2. 点击搜索按钮或按回车键
        3. 等待搜索结果加载完成
        4. 提取前5个搜索结果的:
           - 标题(title)
           - 链接(url)
           - 简要描述(snippet)
        5. 将结果以 JSON 格式返回
        """,
        max_steps=15,  # 最多执行15步,避免无限循环
        extraction_fields=[
            {"name": "title", "type": "text"},
            {"name": "url", "type": "link"},
            {"name": "snippet", "type": "text"}
        ]
    )

    # 提交任务并等待完成
    task_result = await forge.run_task(search_task)

    # 输出结果
    print("任务执行完成!")
    print(f"任务状态: {task_result.status}")
    print(f"提取的数据: {task_result.data}")

if __name__ == "__main__":
    import asyncio
    asyncio.run(main())

第二个示例:登录并填写表单

这个例子展示如何处理需要登录的复杂流程:

"""
第二个示例:登录网站并填写表单
保存为: example_02_login_form.py
"""

from skyvern.forge import Forge
from skyvern.forge.sdk.schemas.task import Task

async def login_and_submit_form():
    """演示登录和表单填写流程"""

    forge = Forge()

    # 定义一个需要登录的表单填写任务
    form_task = Task(
        url="https://example.com/login",
        instruction="""
        这个任务演示完整的登录和数据提交流程:

        第一步:登录
        1. 找到用户名输入框,输入 "test_user"
        2. 找到密码输入框,输入 "test_password"
        3. 点击登录按钮

        第二步:导航到表单页面
        4. 登录成功后,点击"新建表单"按钮
        5. 等待表单页面加载完成

        第三步:填写表单
        6. 在"姓名"字段输入 "张三"
        7. 在"邮箱"字段输入 "zhangsan@example.com"
        8. 在"备注"字段输入 "这是测试备注"
        9. 点击提交按钮

        第四步:验证提交结果
        10. 确认页面显示提交成功的提示
        11. 提取提交后的确认信息
        """,
        max_steps=25,
        # 如果目标网站需要登录,可以提供凭据
        credentials={
            "username": "test_user",
            "password": "test_password"
        }
    )

    result = await forge.run_task(form_task)
    return result

# 运行示例
if __name__ == "__main__":
    import asyncio
    result = asyncio.run(login_and_submit_form())
    print(result)

第三个示例:批量数据采集

实际工作中经常需要采集多个页面的数据:

"""
第三个示例:批量数据采集任务
保存为: example_03_batch_scraping.py
"""

from skyvern.forge import Forge
from skyvern.forge.sdk.schemas.task import Task
import asyncio

async def batch_scraping_example():
    """批量采集多个页面的数据"""

    forge = Forge()

    # 要采集的页面列表
    target_urls = [
        "https://news.ycombinator.com/news",
        "https://news.ycombinator.com/news?p=2",
        "https://news.ycombinator.com/news?p=3",
    ]

    # 采集任务配置
    extraction_instruction = """
    1. 等待页面完全加载
    2. 滚动页面加载更多内容(如果需要)
    3. 提取页面上所有新闻条目的:
       - 标题(title)
       - 链接(url)
       - 点数(score)
       - 评论数(comments)
    4. 返回结构化的 JSON 数据
    """

    all_results = []

    # 遍历每个 URL 进行采集
    for page_num, url in enumerate(target_urls, 1):
        print(f"正在采集第 {page_num} 页: {url}")

        task = Task(
            url=url,
            instruction=extraction_instruction,
            max_steps=10,
            extraction_fields=[
                {"name": "title", "type": "text"},
                {"name": "url", "type": "link"},
                {"name": "score", "type": "number"},
                {"name": "comments", "type": "number"}
            ]
        )

        result = await forge.run_task(task)

        if result.status == "completed":
            all_results.extend(result.data)
            print(f"✓ 第 {page_num} 页采集完成,获取 {len(result.data)} 条数据")
        else:
            print(f"✗ 第 {page_num} 页采集失败: {result.error}")

    # 合并所有结果
    print(f"\n总计采集到 {len(all_results)} 条数据")
    return all_results

if __name__ == "__main__":
    results = asyncio.run(batch_scraping_example())

    # 可以将结果保存为 JSON 文件
    import json
    with open("scraped_data.json", "w", encoding="utf-8") as f:
        json.dump(results, f, ensure_ascii=False, indent=2)

    print("数据已保存到 scraped_data.json")

第四个示例:处理验证码和弹窗

实际场景中经常会遇到各种障碍,Skyvern 内置了智能处理机制:

"""
第四个示例:处理验证码和其他障碍
保存为: example_04_handling_obstacles.py
"""

from skyvern.forge import Forge
from skyvern.forge.sdk.schemas.task import Task

async def handle_obstacles():
    """演示如何处理各种网页障碍"""

    forge = Forge()

    task = Task(
        url="https://example-site-with-captcha.com",
        instruction="""
        任务要求:完成整个数据查询流程

        正常流程:
        1. 导航到目标页面
        2. 如果出现 Cookie 同意弹窗,点击"接受"或"同意全部"
        3. 如果出现登录弹窗,说明这是预期的登录流程
        4. 如果出现验证码:
           - 尝试识别并填写验证码
           - 如果无法自动处理,记录下来并尝试刷新重试
        5. 完成主要的业务操作
        6. 如果遇到任何无法处理的弹窗,尝试点击关闭或取消按钮

        异常处理策略:
        - 超时重试:最多重试3次
        - 元素找不到:尝试备用选择器或滚动到可见区域
        - 页面卡住:截图记录当前状态并终止任务
        """,
        max_steps=30,
        # 配置处理策略
        obstacle_handling={
            "captcha": "auto",        # 自动处理验证码
            "popup": "auto",          # 自动关闭弹窗
            "cookie_banner": "accept" # 自动接受 Cookie
        }
    )

    return await forge.run_task(task)

if __name__ == "__main__":
    import asyncio
    result = asyncio.run(handle_obstacles())
    print(f"任务状态: {result.status}")

第五个示例:与数据库集成

Skyvern 可以直接将采集的数据存入数据库:

"""
第五个示例:数据采集并存入数据库
保存为: example_05_database_integration.py
"""

from skyvern.forge import Forge
from skyvern.forge.sdk.schemas.task import Task
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
import json

# 数据库配置
DATABASE_URL = "sqlite:///./skyvern_data.db"
engine = create_engine(DATABASE_URL)

# 定义数据库表结构
from sqlalchemy import Column, Integer, String, Text, DateTime
from sqlalchemy.ext.declarative import declarative_base
from datetime import datetime

Base = declarative_base()

class ScrapedArticle(Base):
    """存储采集的文章数据"""
    __tablename__ = "articles"

    id = Column(Integer, primary_key=True, autoincrement=True)
    title = Column(String(500), nullable=False)
    url = Column(String(1000), unique=True)
    snippet = Column(Text)
    source = Column(String(100))
    scraped_at = Column(DateTime, default=datetime.utcnow)

    def __repr__(self):
        return f"<Article(title='{self.title}', url='{self.url}')>"

# 创建表
Base.metadata.create_all(engine)
Session = sessionmaker(bind=engine)

async def scrape_and_store():
    """采集数据并存储到数据库"""

    forge = Forge()
    session = Session()

    task = Task(
        url="https://example-news-site.com/tech",
        instruction="""
        1. 提取页面上所有科技新闻的标题、链接和摘要
        2. 返回 JSON 格式的数据列表
        """,
        max_steps=15
    )

    result = await forge.run_task(task)

    if result.status == "completed" and result.data:
        # 将结果写入数据库
        for item in result.data:
            article = ScrapedArticle(
                title=item.get("title", ""),
                url=item.get("url", ""),
                snippet=item.get("snippet", ""),
                source="example-news-site.com"
            )
            session.add(article)

        session.commit()
        print(f"成功存储 {len(result.data)} 条记录到数据库")

    session.close()
    return result

if __name__ == "__main__":
    import asyncio
    asyncio.run(scrape_and_store())

    # 验证数据
    session = Session()
    articles = session.query(ScrapedArticle).limit(5).all()
    for article in articles:
        print(f"- {article.title}")
    session.close()

常见使用场景

场景一:竞品价格监控

"""
使用场景示例:电商竞品价格监控
保存为: use_case_price_monitoring.py
"""

from skyvern.forge import Forge
from skyvern.forge.sdk.schemas.task import Task
from datetime import datetime
import json

async def monitor_competitor_prices():
    """监控竞品价格变动"""

    forge = Forge()

    # 要监控的商品列表
    products = [
        {
            "name": "iPhone 15",
            "urls": [
                "https://www.jd.com/product/iphone15.html",
                "https://www.amazon.cn/dp/iphone15",
                "https://www.taobao.com/search?q=iphone15"
            ]
        },
        {
            "name": "MacBook Pro",
            "urls": [
                "https://www.jd.com/product/macbookpro.html",
                "https://www.amazon.cn/dp/macbookpro"
            ]
        }
    ]

    results = []

    for product in products:
        product_name = product["name"]

        for url in product["urls"]:
            task = Task(
                url=url,
                instruction=f"""
                提取 {product_name} 的以下信息:
                1. 当前价格(注意单位:元/美元)
                2. 是否在促销中(discount)
                3. 促销信息(如有)
                4. 库存状态
                5. 评价评分

                返回 JSON 格式结果,包含价格历史记录
                """,
                max_steps=10
            )

            result = await forge.run_task(task)

            if result.status == "completed":
                results.append({
                    "product": product_name,
                    "source": url,
                    "price_data": result.data,
                    "scraped_at": datetime.now().isoformat()
                })

    # 保存监控结果
    with open(f"price_monitor_{datetime.now().strftime('%Y%m%d_%H%M%S')}.json", "w") as f:
        json.dump(results, f, ensure_ascii=False, indent=2)

    return results

if __name__ == "__main__":
    import asyncio
    asyncio.run(monitor_competitor_prices())

场景二:社交媒体数据采集

"""
使用场景示例:社交媒体数据分析
保存为: use_case_social_media.py
"""

from skyvern.forge import Forge
from skyvern.forge.sdk.schemas.task import Task

async def analyze_social_media():
    """分析社交媒体上的热门话题"""

    forge = Forge()

    task = Task(
        url="https://twitter.com/explore/tabs/trending",
        instruction="""
        1. 等待页面完全加载
        2. 提取当前热门话题列表
        3. 对每个话题,获取:
           - 话题标题
           - 讨论数量
           - 相关的标签
        4. 按讨论数量排序
        5. 返回前10个热门话题
        """,
        max_steps=15,
        # 如果需要登录,配置凭据
        # credentials={"username": "...", "password": "..."}
    )

    result = await forge.run_task(task)
    return result

# 类似的,可以采集:
# - 微博热搜榜
# - 知乎热榜
# - Reddit 热门帖子
# - LinkedIn 职位信息

场景三:自动化测试

"""
使用场景示例:Web 应用自动化测试
保存为: use_case_automated_testing.py
"""

from skyvern.forge import Forge
from skyvern.forge.sdk.schemas.task import Task
import asyncio

async def automated_web_testing():
    """使用 Skyvern 进行 Web 应用端到端测试"""

    forge = Forge()

    test_cases = [
        {
            "name": "用户注册流程测试",
            "url": "https://app.example.com/register",
            "steps": [
                "填写用户名",
                "填写邮箱",
                "填写密码",
                "确认密码",
                "点击注册按钮",
                "验证注册成功提示"
            ]
        },
        {
            "name": "用户登录测试",
            "url": "https://app.example.com/login",
            "steps": [
                "填写用户名",
                "填写密码",
                "点击登录按钮",
                "验证登录后跳转"
            ]
        },
        {
            "name": "商品搜索测试",
            "url": "https://shop.example.com",
            "steps": [
                "在搜索框输入商品名称",
                "点击搜索",
                "验证搜索结果"
            ]
        }
    ]

    test_results = []

    for test_case in test_cases:
        print(f"执行测试: {test_case['name']}")

        task = Task(
            url=test_case["url"],
            instruction=f"""
            执行以下测试步骤:
            {' '.join([f'{i+1}. {step}' for i, step in enumerate(test_case['steps'])])}

            验证每个步骤是否正常执行
            如果发现问题,详细记录错误信息
            """,
            max_steps=20
        )

        result = await forge.run_task(task)

        test_results.append({
            "test_name": test_case["name"],
            "status": result.status,
            "passed": result.status == "completed",
            "error": result.error if result.status != "completed" else None
        })

    # 输出测试报告
    print("\n" + "=" * 50)
    print("测试报告")
    print("=" * 50)

    passed = sum(1 for r in test_results if r["passed"])
    failed = len(test_results) - passed

    for result in test_results:
        status_icon = "✓" if result["passed"] else "✗"
        print(f"{status_icon} {result['test_name']}")
        if result["error"]:
            print(f"  错误: {result['error']}")

    print(f"\n总计: {passed} 通过, {failed} 失败")

    return test_results

if __name__ == "__main__":
    asyncio.run(automated_web_testing())

场景四:学术文献调研

"""
使用场景示例:学术文献自动化调研
保存为: use_case_academic_research.py
"""

from skyvern.forge import Forge
from skyvern.forge.sdk.schemas.task import Task

async def academic_paper_research():
    """自动化学术文献调研"""

    forge = Forge()

    # 在 Google Scholar 上搜索相关论文
    task = Task(
        url="https://scholar.google.com/scholar?q=machine+learning+optimization+2024",
        instruction="""
        1. 等待搜索结果加载完成
        2. 提取前20篇论文的信息:
           - 论文标题
           - 作者列表
           - 发表期刊/会议
           - 发表年份
           - 引用次数
           - 摘要(如果可见)
        3. 按引用次数降序排列
        4. 对于每篇论文,检查是否能访问 PDF 全文
        """,
        max_steps=15
    )

    result = await forge.run_task(task)

    return result

# 同样可以用于:
# - arXiv 论文搜索
# - PubMed 医学文献检索
# - IEEE Xplore 技术文献
# - CNKI 中国知网

高级技巧与最佳实践

技巧一:优化任务指令

好的指令能显著提高任务成功率:

"""
最佳实践:如何写好任务指令
"""

# 不好的指令示例(太模糊)
BAD_INSTRUCTION = """
去那个网站看看有没有什么有用的信息
"""

# 好的指令示例(清晰、具体)
GOOD_INSTRUCTION = """
执行以下操作序列:

第一步:导航和搜索
1. 打开 https://example-ecommerce.com
2. 在顶部搜索框中输入 "无线蓝牙耳机"
3. 点击搜索按钮或按回车键

第二步:筛选和导航
4. 等待搜索结果页面加载完成
5. 点击"价格从低到高"排序
6. 点击"有货"筛选条件

第三步:数据提取
7. 提取前10个商品的以下信息:
   - 商品名称
   - 当前价格
   - 原价(如有)
   - 评分(5星制)
   - 评论数量
   - 商品链接

第四步:输出格式
8. 将结果整理成 JSON 格式,每个商品包含上述所有字段
9. 确保返回的是有效的数据,不要返回空值或占位符
"""

# 更多优化建议:
OPTIMIZATION_TIPS = """
1. 分解大任务为小步骤
   - AI 更擅长完成一系列简单的步骤
   - 而不是一个模糊的大型目标

2. 明确输出格式
   - 指定返回 JSON、CSV 还是其他格式
   - 列出所有需要的字段名称

3. 设置边界条件
   - 最多处理多少条数据
   - 遇到问题时如何处理

4. 提供上下文信息
   - 如果需要登录,提供凭据
   - 如果有特殊要求,提前说明
"""

技巧二:提高稳定性

"""
高级技巧:提高任务执行的稳定性
"""

from skyvern.forge import Forge
from skyvern.forge.sdk.schemas.task import Task

async def stable_task_execution():
    """稳定执行任务的配置建议"""

    forge = Forge()

    task = Task(
        url="https://example-dynamic-site.com",
        instruction="""
        执行数据采集任务
        """,
        max_steps=15,

        # 重试配置
        retry={
            "max_attempts": 3,           # 最多重试3次
            "retry_on_timeout": True,     # 超时后重试
            "retry_on_error": True       # 出错后重试
        },

        # 等待策略
        wait_strategy={
            "page_load": "network_idle",  # 等待网络空闲
            "element_appear": 5,          # 元素出现最多等5秒
            "navigation": 10               # 导航最多等10秒
        },

        # 浏览器配置
        browser_config={
            "viewport": {"width": 1920, "height": 1080},
            "user_agent": "Mozilla/5.0 ...",
            "headless": False,            # 开发时可设为 False 便于调试
            "slow_mo": 100                # 操作延迟(毫秒),便于观察
        }
    )

    return await forge.run_task(task)

技巧三:处理复杂页面

"""
高级技巧:处理复杂动态页面
"""

from skyvern.forge import Forge
from skyvern.forge.sdk.schemas.task import Task

async def handle_complex_pages():
    """处理需要滚动、加载更多的页面"""

    forge = Forge()

    task = Task(
        url="https://example-infinite-scroll.com/feed",
        instruction="""
        这个页面采用无限滚动加载机制,需要特殊处理:

        1. 初始加载
           - 打开页面,等待初始内容加载
           - 提取当前可见的前20条内容

        2. 滚动加载(重复5次)
           - 滚动到页面底部
           - 等待3秒让新内容加载
           - 提取新加载的20条内容
           - 记录已提取的总数

        3. 数据提取
           - 内容类型:帖子
           - 提取字段:
             * 帖子标题/内容
             * 作者
             * 发布时间
             * 点赞数
             * 评论数

        4. 停止条件
           - 已提取100条数据后停止
           - 或滚动5次后仍未加载新内容时停止

        5. 输出
           - 返回所有提取的数据
           - 标注每条数据的加载轮次
        """,
        max_steps=40  # 需要更多步骤来处理滚动
    )

    return await forge.run_task(task)

技巧四:并发执行多个任务

"""
高级技巧:并发执行提高效率
"""

import asyncio
from skyvern.forge import Forge
from skyvern.forge.sdk.schemas.task import Task

async def concurrent_task_execution():
    """同时执行多个任务"""

    # 创建多个 Forge 实例(每个实例管理独立的浏览器)
    forge_1 = Forge()
    forge_2 = Forge()
    forge_3 = Forge()

    # 定义不同的任务
    tasks = [
        Task(url="https://site1.com/data", instruction="提取数据1"),
        Task(url="https://site2.com/data", instruction="提取数据2"),
        Task(url="https://site3.com/data", instruction="提取数据3"),
    ]

    # 为每个 Forge 实例分配一个任务
    forge_task_pairs = [
        (forge_1, tasks[0]),
        (forge_2, tasks[1]),
        (forge_3, tasks[2]),
    ]

    # 并发执行
    async def run_single_task(forge, task):
        return await forge.run_task(task)

    # 使用 asyncio.gather 并发运行所有任务
    results = await asyncio.gather(
        *[run_single_task(forge, task) for forge, task in forge_task_pairs]
    )

    for i, result in enumerate(results):
        print(f"任务 {i+1} 完成: {result.status}")

    return results

# 运行并发任务
if __name__ == "__main__":
    results = asyncio.run(concurrent_task_execution())

Web API 使用指南

Skyvern 提供了完整的 REST API,可以通过 HTTP 请求调用:

启动 API 服务

# 启动 API 服务
uvicorn skyvern.forge.api.app:app --host 0.0.0.0 --port 8080

API 端点

1. 创建任务

# 创建新任务
curl -X POST http://localhost:8080/api/v1/tasks \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://example.com",
    "instruction": "提取页面标题和所有链接",
    "max_steps": 10
  }'

2. 获取任务状态

# 查看任务状态和结果
curl http://localhost:8080/api/v1/tasks/{task_id}

3. 列出所有任务

# 获取任务列表
curl "http://localhost:8080/api/v1/tasks?status=completed&limit=10"

Python API 客户端

"""
使用 Python 客户端调用 Skyvern API
"""

import httpx
import asyncio

class SkyvernAPIClient:
    """Skyvern API 客户端封装"""

    def __init__(self, base_url: str = "http://localhost:8080"):
        self.base_url = base_url
        self.client = httpx.AsyncClient(timeout=60.0)

    async def create_task(
        self,
        url: str,
        instruction: str,
        max_steps: int = 15
    ) -> dict:
        """创建新任务"""
        response = await self.client.post(
            f"{self.base_url}/api/v1/tasks",
            json={
                "url": url,
                "instruction": instruction,
                "max_steps": max_steps
            }
        )
        response.raise_for_status()
        return response.json()

    async def get_task(self, task_id: str) -> dict:
        """获取任务详情"""
        response = await self.client.get(
            f"{self.base_url}/api/v1/tasks/{task_id}"
        )
        response.raise_for_status()
        return response.json()

    async def list_tasks(
        self,
        status: str = None,
        limit: int = 10
    ) -> dict:
        """列出任务"""
        params = {"limit": limit}
        if status:
            params["status"] = status

        response = await self.client.get(
            f"{self.base_url}/api/v1/tasks",
            params=params
        )
        response.raise_for_status()
        return response.json()

    async def wait_for_completion(self, task_id: str, poll_interval: int = 2) -> dict:
        """等待任务完成"""
        import time

        while True:
            task = await self.get_task(task_id)
            status = task.get("status")

            if status in ["completed", "failed", "cancelled"]:
                return task

            print(f"任务进行中... 当前状态: {status}")
            time.sleep(poll_interval)

    async def close(self):
        """关闭客户端"""
        await self.client.aclose()

# 使用示例
async def api_usage_example():
    """API 使用示例"""

    client = SkyvernAPIClient()

    try:
        # 创建任务
        task = await client.create_task(
            url="https://news.ycombinator.com",
            instruction="提取首页前10条新闻的标题和链接"
        )
        task_id = task["task_id"]
        print(f"任务已创建: {task_id}")

        # 等待完成
        result = await client.wait_for_completion(task_id)

        # 处理结果
        if result["status"] == "completed":
            print(f"任务完成!")
            print(f"提取的数据: {result['data']}")
        else:
            print(f"任务失败: {result.get('error')}")

    finally:
        await client.close()

if __name__ == "__main__":
    asyncio.run(api_usage_example())

常见问题与解决方案

Q1: 任务执行超时怎么办?

"""
问题:任务执行时间过长导致超时
解决方案:
"""

# 方案1:增加超时时间
task = Task(
    url="https://slow-loading-site.com",
    instruction="采集数据",
    max_steps=30  # 增加步数限制
)

# 方案2:优化指令,减少不必要的步骤
optimized_instruction = """
优化后的指令:
1. 直接导航到目标数据页面(而不是通过首页逐层点击)
2. 只提取必要的字段,减少处理步骤
3. 设置合理的超时时间
"""

# 方案3:分批处理
async def batch_processing():
    """将大任务拆分为多个小任务"""
    # 不是一次性提取1000条数据
    # 而是分10次,每次提取100条
    pass

Q2: 如何处理需要登录的网站?

"""
问题:目标网站需要登录才能访问
解决方案:
"""

# 方案1:提供登录凭据
task = Task(
    url="https://member-only-site.com/data",
    instruction="采集数据",
    credentials={
        "username": "your_username",
        "password": "your_password"
    }
)

# 方案2:预先处理登录流程
async def login_first():
    """分步骤处理登录"""
    forge = Forge()

    # 第一步:登录
    login_task = Task(
        url="https://site.com/login",
        instruction="""
        1. 输入用户名和密码
        2. 点击登录
        3. 等待登录成功
        4. 截图确认当前状态
        """,
        max_steps=10
    )
    login_result = await forge.run_task(login_task)

    # 获取登录后的 cookies
    cookies = login_result.cookies

    # 第二步:使用 cookies 访问目标页面
    data_task = Task(
        url="https://site.com/protected/data",
        instruction="采集数据",
        cookies=cookies  # 复用登录 cookies
    )

    return await forge.run_task(data_task)

# 方案3:使用 session 管理
from playwright.sync_api import sync_playwright

with sync_playwright() as p:
    browser = p.chromium.launch()
    context = browser.new_context()
    page = context.new_page()

    # 手动完成登录
    page.goto("https://site.com/login")
    page.fill("#username", "user")
    page.fill("#password", "pass")
    page.click("#login-btn")
    page.wait_for_url("**/dashboard")

    # 保存 cookies
    cookies = context.cookies()

    browser.close()

# 后续任务使用保存的 cookies
task = Task(
    url="https://site.com/protected/data",
    instruction="采集数据",
    cookies=cookies
)

Q3: 遇到验证码怎么处理?

"""
问题:页面包含验证码阻止操作
解决方案:
"""

# Skyvern 内置了一些验证码处理策略
task = Task(
    url="https://site-with-captcha.com",
    instruction="完成任务",

    # 验证码处理策略
    captcha_handling={
        # 策略1:自动识别(需要额外的验证码识别服务)
        "strategy": "auto",
        "ocr_provider": "your-ocr-api",  # 如 2Captcha, Anti-Captcha

        # 策略2:人工介入
        "strategy": "manual",
        "notify_webhook": "https://your-server.com/captcha-alert",

        # 策略3:跳过(如果验证码是关键步骤,任务会失败)
        "strategy": "skip"
    }
)

# 对于简单的图片验证码,Skyvern 会尝试 OCR 识别
# 对于复杂的验证码,建议使用策略2:人工介入模式

Q4: 如何处理动态加载的内容?

"""
问题:页面内容通过 JavaScript 动态加载
解决方案:
"""

task = Task(
    url="https://spa-example.com",
    instruction="""
    这个页面使用 JavaScript 动态加载内容,需要特殊处理:

    1. 等待初始内容渲染(等待3秒)
    2. 检查内容是否已加载
    3. 如果没有,继续等待或尝试滚动触发加载
    4. 滚动到页面底部(可能触发懒加载)
    5. 等待新内容加载完成
    6. 重复步骤4-5,直到获取足够数据或页面不再有新内容
    """,
    max_steps=30  # 动态页面需要更多步骤
)

Q5: 数据提取不完整怎么办?

"""
问题:提取的数据不完整或格式不对
解决方案:
"""

# 优化指令,明确输出格式
task = Task(
    url="https://example.com",
    instruction="""
    严格按照以下格式提取数据:

    输出必须是一个 JSON 数组,每个元素包含:
    {
        "title": "文章标题(字符串,不能为空)",
        "url": "文章链接(必须是完整的 URL,包含 https://)",
        "date": "发布日期(格式:YYYY-MM-DD)",
        "views": "阅读量(整数,如:1234)"
    }

    注意事项:
    - 确保每个字段都有值,不要返回 null 或空字符串
    - 如果某个字段在页面上找不到,在该字段填入 "N/A"
    - 返回至少10条数据
    """,
    max_steps=20
)

# 额外技巧:添加验证步骤
instruction_with_validation = """
1. 提取数据
2. 对提取的数据进行验证:
   - 检查每个字段是否有值
   - 检查 URL 格式是否正确
   - 检查日期格式是否符合要求
3. 如果验证失败,尝试重新提取
4. 返回通过验证的数据
"""

性能优化与生产部署

生产环境配置

"""
生产环境部署配置示例
保存为: production_config.py
"""

import os

# 生产环境环境变量配置
PRODUCTION_CONFIG = """
# 数据库配置(使用 PostgreSQL)
SKYVERNET_DB_URL="postgresql://user:password@host:5432/skyvern_prod"

# Redis 配置(用于任务队列)
SKYVERNET_REDIS_URL="redis://localhost:6379/0"

# LLM 配置
OPENAI_API_KEY="${OPENAI_API_KEY}"
OPENAI_API_BASE="https://api.openai.com/v1"

# 并发配置
SKYVERNET_MAX_CONCURRENT_TASKS=10
SKYVERNET_TASK_TIMEOUT=600

# 浏览器配置
SKYVERNET_BROWSER_HEADLESS=true
SKYVERNET_BROWSER_MAX_POOL_SIZE=20

# 日志配置
LOG_LEVEL="WARNING"
LOG_FORMAT="json"

# 安全配置
SKYVERNET_SECRET_KEY="your-secret-key-here"
"""

# Docker Compose 配置
DOCKER_COMPOSE_YML = """
version: '3.8'

services:
  skyvern:
    image: skyvern/skyvern:latest
    ports:
      - "8080:8080"
    environment:
      - OPENAI_API_KEY=${OPENAI_API_KEY}
      - SKYVERNET_DB_URL=postgresql://postgres:postgres@db:5432/skyvern
      - SKYVERNET_REDIS_URL=redis://redis:6379/0
    depends_on:
      - db
      - redis
    restart: unless-stopped

  db:
    image: postgres:15
    environment:
      - POSTGRES_USER=postgres
      - POSTGRES_PASSWORD=postgres
      - POSTGRES_DB=skyvern
    volumes:
      - postgres_data:/var/lib/postgresql/data
    restart: unless-stopped

  redis:
    image: redis:7-alpine
    volumes:
      - redis_data:/data
    restart: unless-stopped

volumes:
  postgres_data:
  redis_data:
"""

监控与日志

"""
生产环境监控配置
"""

from skyvern.forge import Forge
import logging

# 配置日志
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)

logger = logging.getLogger("skyvern")

# 创建 Forge 实例时启用监控
forge = Forge(
    enable_metrics=True,
    metrics_port=9090,  # Prometheus 指标端口

    # Webhook 通知
    webhook_config={
        "on_start": "https://your-server.com/webhook/start",
        "on_progress": "https://your-server.com/webhook/progress",
        "on_complete": "https://your-server.com/webhook/complete",
        "on_error": "https://your-server.com/webhook/error"
    }
)

# 监控指标
METRICS_TO_TRACK = """
Prometheus 指标:
- skyvern_tasks_total:任务总数
- skyvern_tasks_completed:完成的任务数
- skyvern_tasks_failed:失败的任务数
- skyvern_task_duration_seconds:任务执行时长
- skyvern_browser_actions_total:浏览器操作总数
- skyvern_llm_calls_total:LLM 调用次数
"""

总结与资源链接

Skyvern 的优势总结

特性 传统方案(Selenium) Skyvern
配置复杂度 高(需要写大量代码) 低(自然语言描述)
维护成本 高(网站改版即失效) 低(AI 智能适配)
错误处理 需要手动编写 内置自动重试
学习曲线 陡峭 平缓
适用场景 结构固定的场景 任意网页场景

相关项目推荐

如果你对 Skyvern 感兴趣,以下项目也值得关注:

  • Playwright:强大的浏览器自动化工具,Skyvern 的底层依赖
  • Puppeteer:Node.js 生态的浏览器自动化框架
  • Selenium:老牌浏览器自动化工具
  • LangChain:构建 LLM 应用的框架,可与 Skyvern 结合
  • AutoGPT:自主 AI Agent 项目,与 Skyvern 有相似的理念
  • Browser-use:另一个基于 AI 的浏览器自动化库
  • Crawl4AI:专为 LLM 设计的网页爬虫

继续学习资源

  • 官方文档:https://docs.skyvern.ai
  • GitHub 仓库:https://github.com/Skyvern-AI/skyvern
  • 示例代码:https://github.com/Skyvern-AI/skyvern/tree/main/examples
  • Discord 社区:加入讨论获取帮助
  • OpenAI 文档:了解更多关于 GPT-4 Vision 的能力

动手实践

学习 Skyvern 最好的方式就是动手实践。建议按照以下顺序进行练习:

  1. 第一阶段:运行官方示例,理解基本用法
  2. 第二阶段:修改示例代码,尝试不同的任务
  3. 第三阶段:解决实际问题,如数据采集、自动化测试等
  4. 第四阶段:深入源码,理解内部机制
  5. 第五阶段:贡献代码,参与开源社区

无论你是开发者、数据科学家,还是对自动化感兴趣的普通用户,Skyvern 都能帮助你以前所未有的效率完成网页自动化任务。现在就开始你的 Skyvern 之旅吧!


本文档会持续更新,欢迎关注 GitHub 仓库获取最新内容。如果有任何问题或建议,请在评论区留言!

如果内容对您有帮助,欢迎打赏

您的支持是我继续创作的动力

前往打赏页面

评论区

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注