rpaworkflow


Namerpaworkflow JSON
Version 0.0.1 PyPI version JSON
download
home_pageNone
SummaryRPA工作流框架,支持多种自动化工具的工作流管理和节点执行
upload_time2025-08-06 10:08:32
maintainerNone
docs_urlNone
authorNone
requires_python>=3.11
licenseMIT
keywords automation playwright rpa selenium uiautomator2 workflow
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            # RPA Workflow

一个灵活、可扩展的RPA(机器人流程自动化)工作流框架,支持多种自动化工具的工作流管理和节点执行。

## 特性

- 统一的工作流管理接口
- 支持多种自动化工具:
  - Selenium
  - UIAutomator2 (Android)
  - Undetected ChromeDriver
  - Playwright
  - DrissionPage
- 可扩展的节点系统
- 内置错误处理和重试机制
- 上下文数据共享

## 安装

### 基本安装

```bash
pip install rpaworkflow
```

### 安装特定框架依赖

rpaworkflow支持多种自动化框架,您可以根据需要安装特定框架的依赖:

```bash
# 安装Selenium依赖
pip install rpaworkflow[selenium]

# 安装Playwright依赖
pip install rpaworkflow[playwright]

# 安装UIAutomator2依赖
pip install rpaworkflow[uiautomator2]

# 安装Undetected ChromeDriver依赖
pip install rpaworkflow[undetected-chromedriver]

# 安装DrissionPage依赖
pip install rpaworkflow[drissionpage]

# 安装所有框架依赖
pip install rpaworkflow[all]
```

使用uv安装:

```bash
# 安装Selenium依赖
uv pip install rpaworkflow[selenium]

# 安装所有框架依赖
uv pip install rpaworkflow[all]
```

## 快速开始

### Selenium Web 自动化示例

```python
from rpaworkflow.selenium import ConnectBrowserNode, NavigateNode, CloseBrowserNode
from rpaworkflow.manager import WorkflowManager, WorkflowStatus
from rpaworkflow.nodes import WaitTimeNode

# 创建工作流管理器
workflow = WorkflowManager(name="简单Web自动化")

# 添加节点
workflow.add_node(ConnectBrowserNode(name="连接浏览器"))
workflow.add_node(NavigateNode(name="打开网页", url="https://www.example.com"))
workflow.add_node(WaitTimeNode(name="等待加载", min_time=2))

workflow.set_finally_node(CloseBrowserNode(name='关闭浏览器'))

# 执行工作流
result = workflow.run()
print(f"工作流执行状态: {result.status}")

assert result.status == WorkflowStatus.SUCCESS
```

### Android 自动化示例

```python
from rpaworkflow.uiautomator2 import ConnectDeviceNode, ClickNode, LaunchAppNode, StopAppNode
from rpaworkflow.manager import WorkflowManager, WorkflowStatus

# 创建工作流管理器
workflow = WorkflowManager(name="Android自动化")

package_name = '...'  # 例如 'com.example.app'

# 添加节点
workflow.add_node(ConnectDeviceNode(name="连接设备"))
workflow.add_node(LaunchAppNode(name="启动APP", package_name=package_name))
workflow.add_node(ClickNode(name="点击按钮", selector={"resourceId": "com.example.app:id/button1"}))

workflow.set_finally_node(StopAppNode(name="停止APP", package_name=package_name))

# 执行工作流
result = workflow.run()

assert result.status == WorkflowStatus.SUCCESS
```

### Playwright 现代 Web 自动化示例

```python
from rpaworkflow.playwright import ConnectBrowserNode, NavigateNode, ClickNode, InputTextNode, ScreenshotNode, CloseBrowserNode
from rpaworkflow.manager import WorkflowManager, WorkflowStatus

# 创建工作流管理器
workflow = WorkflowManager(name="Playwright自动化")

# 添加节点
workflow.add_node(ConnectBrowserNode(name="连接浏览器", browser_type="chromium"))
workflow.add_node(NavigateNode(name="打开网页", url="https://www.example.com"))
workflow.add_node(ClickNode(name="点击登录按钮", selector="#login-button"))
workflow.add_node(InputTextNode(name="输入用户名", selector="#username", text="user"))
workflow.add_node(ScreenshotNode(name="截图", file_path="result.png"))

workflow.set_finally_node(CloseBrowserNode(name="关闭浏览器"))

# 执行工作流
result = workflow.run()

assert result.status == WorkflowStatus.SUCCESS
```

### DrissionPage 多功能 Web 自动化示例

```python
from rpaworkflow.drissionpage import ConnectPageNode, NavigateNode, ClickNode, InputTextNode, ScreenshotNode, QuitBrowserNode
from rpaworkflow.manager import WorkflowManager, WorkflowStatus

# 创建工作流管理器
workflow = WorkflowManager(name="DrissionPage自动化")

# 添加节点
workflow.add_node(ConnectPageNode(name="连接页面", page_type="web"))
workflow.add_node(NavigateNode(name="打开网页", url="https://www.example.com"))
workflow.add_node(ClickNode(name="点击按钮", locator="#login-button"))
workflow.add_node(InputTextNode(name="输入文本", locator="#username", text="user"))
workflow.add_node(ScreenshotNode(name="截图", filename="screenshot.png", save_path='./'))

workflow.set_finally_node(QuitBrowserNode(name="退出浏览器"))

# 执行工作流
result = workflow.run()

assert result.status == WorkflowStatus.SUCCESS
```

### Undetected ChromeDriver 反检测 Web 自动化示例

```python
import undetected_chromedriver as uc
from rpaworkflow.undetected_chromedriver import ConnectBrowserNode, NavigateNode, ClickNode, InputTextNode, CloseBrowserNode
from rpaworkflow.manager import WorkflowManager, WorkflowStatus

# 创建工作流管理器
workflow = WorkflowManager(name="Undetected Chrome自动化")

# 添加节点
workflow.add_node(ConnectBrowserNode(name="连接浏览器", headless=False, suppress_welcome=True))
workflow.add_node(NavigateNode(name="打开网页", url="https://bot.sannysoft.com"))  # 指纹检测网站
workflow.add_node(ClickNode(name="点击按钮", by=uc.By.CSS_SELECTOR, value="#some-button"))
workflow.add_node(InputTextNode(name="输入文本", by=uc.By.CSS_SELECTOR, value="#some-input", text="测试文本"))

workflow.set_finally_node(CloseBrowserNode(name="关闭浏览器"))

# 执行工作流
result = workflow.run()

assert result.status == WorkflowStatus.SUCCESS
```

## 高级用法

### 条件分支

```python
from rpaworkflow.nodes import ConditionNode

# 创建条件节点
condition_node = ConditionNode(
    name="条件判断",
    condition=lambda ctx: ctx.get("some_value") > 10,
    true_node=SomeNode(name="条件为真时执行"),
    false_node=SomeNode(name="条件为假时执行")
)

# 添加到工作流
workflow.add_node(condition_node)
```

### 逻辑组合函数

`and_` 和 `or_` 函数可以组合多个节点的条件,用于创建复杂的条件逻辑。

```python
from rpaworkflow.func import or_, and_
from rpaworkflow.nodes import CheckElementNode

# 创建检查元素节点
check_button1 = CheckElementNode(name="检查按钮1", selector="#button1")
check_button2 = CheckElementNode(name="检查按钮2", selector="#button2")
check_button3 = CheckElementNode(name="检查按钮3", selector="#button3")

# 使用 or_ 函数:前面节点若正常运行, 则不执行下一个节点, 否则执行下一个节点
or_node = or_(check_button1, check_button2)
workflow.add_node(or_node)

# 使用 and_ 函数:前一个节点异常, 不执行下一个节点, 否则执行下一个节点
and_node = and_(check_button1, check_button2, check_button3)
workflow.add_node(and_node)

# 组合使用
complex_condition = or_(check_button1, and_(check_button2, check_button3))
workflow.add_node(complex_condition)
```

### 循环节点

```python
from rpaworkflow.nodes import LoopNode, LambdaActionNode

# 创建一个计数器节点
increment_counter = LambdaActionNode(
    name="增加计数器",
    action=lambda ctx: ctx.set("counter", ctx.get("counter", 0) + 1)
)

# 创建循环节点
loop_node = LoopNode(
    name="循环执行",
    loop_condition=lambda ctx: ctx.get("counter", 0) < 5,  # 循环条件:计数器小于5
    loop_node=increment_counter  # 循环执行的节点
)

# 初始化计数器
workflow.add_node(LambdaActionNode(
    name="初始化计数器",
    action=lambda ctx: ctx.set("counter", 0)
))

# 添加循环节点
workflow.add_node(loop_node)
```

### 空节点和错误节点

```python
from rpaworkflow.nodes import EmptyNode, ErrorNode, ErrorStorageNode

# 空节点 - 不执行任何操作,可用作占位符
workflow.add_node(EmptyNode(name="空操作"))

# 错误节点 - 抛出异常
workflow.add_node(ErrorNode(
    name="抛出错误",
    error="自定义错误信息"  # 也可以传入Exception对象
))

# 错误存储节点 - 抛出异常并存储到上下文
workflow.add_node(ErrorStorageNode(
    name="存储错误",
    error="自定义错误信息",
    output_key="error_info"  # 错误信息将存储在上下文的这个键下
))
```

## 贡献

欢迎贡献代码、报告问题或提出改进建议!

## 许可证

本项目采用 MIT 许可证。详见 LICENSE 文件。

            

Raw data

            {
    "_id": null,
    "home_page": null,
    "name": "rpaworkflow",
    "maintainer": null,
    "docs_url": null,
    "requires_python": ">=3.11",
    "maintainer_email": null,
    "keywords": "automation, playwright, rpa, selenium, uiautomator2, workflow",
    "author": null,
    "author_email": "RPA Workflow Team <example@example.com>",
    "download_url": "https://files.pythonhosted.org/packages/80/bf/80cd3b0d44e8a42afc5668304481e86cfddac834e193ad47cd7d74a42772/rpaworkflow-0.0.1.tar.gz",
    "platform": null,
    "description": "# RPA Workflow\n\n\u4e00\u4e2a\u7075\u6d3b\u3001\u53ef\u6269\u5c55\u7684RPA\uff08\u673a\u5668\u4eba\u6d41\u7a0b\u81ea\u52a8\u5316\uff09\u5de5\u4f5c\u6d41\u6846\u67b6\uff0c\u652f\u6301\u591a\u79cd\u81ea\u52a8\u5316\u5de5\u5177\u7684\u5de5\u4f5c\u6d41\u7ba1\u7406\u548c\u8282\u70b9\u6267\u884c\u3002\n\n## \u7279\u6027\n\n- \u7edf\u4e00\u7684\u5de5\u4f5c\u6d41\u7ba1\u7406\u63a5\u53e3\n- \u652f\u6301\u591a\u79cd\u81ea\u52a8\u5316\u5de5\u5177\uff1a\n  - Selenium\n  - UIAutomator2 (Android)\n  - Undetected ChromeDriver\n  - Playwright\n  - DrissionPage\n- \u53ef\u6269\u5c55\u7684\u8282\u70b9\u7cfb\u7edf\n- \u5185\u7f6e\u9519\u8bef\u5904\u7406\u548c\u91cd\u8bd5\u673a\u5236\n- \u4e0a\u4e0b\u6587\u6570\u636e\u5171\u4eab\n\n## \u5b89\u88c5\n\n### \u57fa\u672c\u5b89\u88c5\n\n```bash\npip install rpaworkflow\n```\n\n### \u5b89\u88c5\u7279\u5b9a\u6846\u67b6\u4f9d\u8d56\n\nrpaworkflow\u652f\u6301\u591a\u79cd\u81ea\u52a8\u5316\u6846\u67b6\uff0c\u60a8\u53ef\u4ee5\u6839\u636e\u9700\u8981\u5b89\u88c5\u7279\u5b9a\u6846\u67b6\u7684\u4f9d\u8d56\uff1a\n\n```bash\n# \u5b89\u88c5Selenium\u4f9d\u8d56\npip install rpaworkflow[selenium]\n\n# \u5b89\u88c5Playwright\u4f9d\u8d56\npip install rpaworkflow[playwright]\n\n# \u5b89\u88c5UIAutomator2\u4f9d\u8d56\npip install rpaworkflow[uiautomator2]\n\n# \u5b89\u88c5Undetected ChromeDriver\u4f9d\u8d56\npip install rpaworkflow[undetected-chromedriver]\n\n# \u5b89\u88c5DrissionPage\u4f9d\u8d56\npip install rpaworkflow[drissionpage]\n\n# \u5b89\u88c5\u6240\u6709\u6846\u67b6\u4f9d\u8d56\npip install rpaworkflow[all]\n```\n\n\u4f7f\u7528uv\u5b89\u88c5\uff1a\n\n```bash\n# \u5b89\u88c5Selenium\u4f9d\u8d56\nuv pip install rpaworkflow[selenium]\n\n# \u5b89\u88c5\u6240\u6709\u6846\u67b6\u4f9d\u8d56\nuv pip install rpaworkflow[all]\n```\n\n## \u5feb\u901f\u5f00\u59cb\n\n### Selenium Web \u81ea\u52a8\u5316\u793a\u4f8b\n\n```python\nfrom rpaworkflow.selenium import ConnectBrowserNode, NavigateNode, CloseBrowserNode\nfrom rpaworkflow.manager import WorkflowManager, WorkflowStatus\nfrom rpaworkflow.nodes import WaitTimeNode\n\n# \u521b\u5efa\u5de5\u4f5c\u6d41\u7ba1\u7406\u5668\nworkflow = WorkflowManager(name=\"\u7b80\u5355Web\u81ea\u52a8\u5316\")\n\n# \u6dfb\u52a0\u8282\u70b9\nworkflow.add_node(ConnectBrowserNode(name=\"\u8fde\u63a5\u6d4f\u89c8\u5668\"))\nworkflow.add_node(NavigateNode(name=\"\u6253\u5f00\u7f51\u9875\", url=\"https://www.example.com\"))\nworkflow.add_node(WaitTimeNode(name=\"\u7b49\u5f85\u52a0\u8f7d\", min_time=2))\n\nworkflow.set_finally_node(CloseBrowserNode(name='\u5173\u95ed\u6d4f\u89c8\u5668'))\n\n# \u6267\u884c\u5de5\u4f5c\u6d41\nresult = workflow.run()\nprint(f\"\u5de5\u4f5c\u6d41\u6267\u884c\u72b6\u6001: {result.status}\")\n\nassert result.status == WorkflowStatus.SUCCESS\n```\n\n### Android \u81ea\u52a8\u5316\u793a\u4f8b\n\n```python\nfrom rpaworkflow.uiautomator2 import ConnectDeviceNode, ClickNode, LaunchAppNode, StopAppNode\nfrom rpaworkflow.manager import WorkflowManager, WorkflowStatus\n\n# \u521b\u5efa\u5de5\u4f5c\u6d41\u7ba1\u7406\u5668\nworkflow = WorkflowManager(name=\"Android\u81ea\u52a8\u5316\")\n\npackage_name = '...'  # \u4f8b\u5982 'com.example.app'\n\n# \u6dfb\u52a0\u8282\u70b9\nworkflow.add_node(ConnectDeviceNode(name=\"\u8fde\u63a5\u8bbe\u5907\"))\nworkflow.add_node(LaunchAppNode(name=\"\u542f\u52a8APP\", package_name=package_name))\nworkflow.add_node(ClickNode(name=\"\u70b9\u51fb\u6309\u94ae\", selector={\"resourceId\": \"com.example.app:id/button1\"}))\n\nworkflow.set_finally_node(StopAppNode(name=\"\u505c\u6b62APP\", package_name=package_name))\n\n# \u6267\u884c\u5de5\u4f5c\u6d41\nresult = workflow.run()\n\nassert result.status == WorkflowStatus.SUCCESS\n```\n\n### Playwright \u73b0\u4ee3 Web \u81ea\u52a8\u5316\u793a\u4f8b\n\n```python\nfrom rpaworkflow.playwright import ConnectBrowserNode, NavigateNode, ClickNode, InputTextNode, ScreenshotNode, CloseBrowserNode\nfrom rpaworkflow.manager import WorkflowManager, WorkflowStatus\n\n# \u521b\u5efa\u5de5\u4f5c\u6d41\u7ba1\u7406\u5668\nworkflow = WorkflowManager(name=\"Playwright\u81ea\u52a8\u5316\")\n\n# \u6dfb\u52a0\u8282\u70b9\nworkflow.add_node(ConnectBrowserNode(name=\"\u8fde\u63a5\u6d4f\u89c8\u5668\", browser_type=\"chromium\"))\nworkflow.add_node(NavigateNode(name=\"\u6253\u5f00\u7f51\u9875\", url=\"https://www.example.com\"))\nworkflow.add_node(ClickNode(name=\"\u70b9\u51fb\u767b\u5f55\u6309\u94ae\", selector=\"#login-button\"))\nworkflow.add_node(InputTextNode(name=\"\u8f93\u5165\u7528\u6237\u540d\", selector=\"#username\", text=\"user\"))\nworkflow.add_node(ScreenshotNode(name=\"\u622a\u56fe\", file_path=\"result.png\"))\n\nworkflow.set_finally_node(CloseBrowserNode(name=\"\u5173\u95ed\u6d4f\u89c8\u5668\"))\n\n# \u6267\u884c\u5de5\u4f5c\u6d41\nresult = workflow.run()\n\nassert result.status == WorkflowStatus.SUCCESS\n```\n\n### DrissionPage \u591a\u529f\u80fd Web \u81ea\u52a8\u5316\u793a\u4f8b\n\n```python\nfrom rpaworkflow.drissionpage import ConnectPageNode, NavigateNode, ClickNode, InputTextNode, ScreenshotNode, QuitBrowserNode\nfrom rpaworkflow.manager import WorkflowManager, WorkflowStatus\n\n# \u521b\u5efa\u5de5\u4f5c\u6d41\u7ba1\u7406\u5668\nworkflow = WorkflowManager(name=\"DrissionPage\u81ea\u52a8\u5316\")\n\n# \u6dfb\u52a0\u8282\u70b9\nworkflow.add_node(ConnectPageNode(name=\"\u8fde\u63a5\u9875\u9762\", page_type=\"web\"))\nworkflow.add_node(NavigateNode(name=\"\u6253\u5f00\u7f51\u9875\", url=\"https://www.example.com\"))\nworkflow.add_node(ClickNode(name=\"\u70b9\u51fb\u6309\u94ae\", locator=\"#login-button\"))\nworkflow.add_node(InputTextNode(name=\"\u8f93\u5165\u6587\u672c\", locator=\"#username\", text=\"user\"))\nworkflow.add_node(ScreenshotNode(name=\"\u622a\u56fe\", filename=\"screenshot.png\", save_path='./'))\n\nworkflow.set_finally_node(QuitBrowserNode(name=\"\u9000\u51fa\u6d4f\u89c8\u5668\"))\n\n# \u6267\u884c\u5de5\u4f5c\u6d41\nresult = workflow.run()\n\nassert result.status == WorkflowStatus.SUCCESS\n```\n\n### Undetected ChromeDriver \u53cd\u68c0\u6d4b Web \u81ea\u52a8\u5316\u793a\u4f8b\n\n```python\nimport undetected_chromedriver as uc\nfrom rpaworkflow.undetected_chromedriver import ConnectBrowserNode, NavigateNode, ClickNode, InputTextNode, CloseBrowserNode\nfrom rpaworkflow.manager import WorkflowManager, WorkflowStatus\n\n# \u521b\u5efa\u5de5\u4f5c\u6d41\u7ba1\u7406\u5668\nworkflow = WorkflowManager(name=\"Undetected Chrome\u81ea\u52a8\u5316\")\n\n# \u6dfb\u52a0\u8282\u70b9\nworkflow.add_node(ConnectBrowserNode(name=\"\u8fde\u63a5\u6d4f\u89c8\u5668\", headless=False, suppress_welcome=True))\nworkflow.add_node(NavigateNode(name=\"\u6253\u5f00\u7f51\u9875\", url=\"https://bot.sannysoft.com\"))  # \u6307\u7eb9\u68c0\u6d4b\u7f51\u7ad9\nworkflow.add_node(ClickNode(name=\"\u70b9\u51fb\u6309\u94ae\", by=uc.By.CSS_SELECTOR, value=\"#some-button\"))\nworkflow.add_node(InputTextNode(name=\"\u8f93\u5165\u6587\u672c\", by=uc.By.CSS_SELECTOR, value=\"#some-input\", text=\"\u6d4b\u8bd5\u6587\u672c\"))\n\nworkflow.set_finally_node(CloseBrowserNode(name=\"\u5173\u95ed\u6d4f\u89c8\u5668\"))\n\n# \u6267\u884c\u5de5\u4f5c\u6d41\nresult = workflow.run()\n\nassert result.status == WorkflowStatus.SUCCESS\n```\n\n## \u9ad8\u7ea7\u7528\u6cd5\n\n### \u6761\u4ef6\u5206\u652f\n\n```python\nfrom rpaworkflow.nodes import ConditionNode\n\n# \u521b\u5efa\u6761\u4ef6\u8282\u70b9\ncondition_node = ConditionNode(\n    name=\"\u6761\u4ef6\u5224\u65ad\",\n    condition=lambda ctx: ctx.get(\"some_value\") > 10,\n    true_node=SomeNode(name=\"\u6761\u4ef6\u4e3a\u771f\u65f6\u6267\u884c\"),\n    false_node=SomeNode(name=\"\u6761\u4ef6\u4e3a\u5047\u65f6\u6267\u884c\")\n)\n\n# \u6dfb\u52a0\u5230\u5de5\u4f5c\u6d41\nworkflow.add_node(condition_node)\n```\n\n### \u903b\u8f91\u7ec4\u5408\u51fd\u6570\n\n`and_` \u548c `or_` \u51fd\u6570\u53ef\u4ee5\u7ec4\u5408\u591a\u4e2a\u8282\u70b9\u7684\u6761\u4ef6\uff0c\u7528\u4e8e\u521b\u5efa\u590d\u6742\u7684\u6761\u4ef6\u903b\u8f91\u3002\n\n```python\nfrom rpaworkflow.func import or_, and_\nfrom rpaworkflow.nodes import CheckElementNode\n\n# \u521b\u5efa\u68c0\u67e5\u5143\u7d20\u8282\u70b9\ncheck_button1 = CheckElementNode(name=\"\u68c0\u67e5\u6309\u94ae1\", selector=\"#button1\")\ncheck_button2 = CheckElementNode(name=\"\u68c0\u67e5\u6309\u94ae2\", selector=\"#button2\")\ncheck_button3 = CheckElementNode(name=\"\u68c0\u67e5\u6309\u94ae3\", selector=\"#button3\")\n\n# \u4f7f\u7528 or_ \u51fd\u6570\uff1a\u524d\u9762\u8282\u70b9\u82e5\u6b63\u5e38\u8fd0\u884c, \u5219\u4e0d\u6267\u884c\u4e0b\u4e00\u4e2a\u8282\u70b9, \u5426\u5219\u6267\u884c\u4e0b\u4e00\u4e2a\u8282\u70b9\nor_node = or_(check_button1, check_button2)\nworkflow.add_node(or_node)\n\n# \u4f7f\u7528 and_ \u51fd\u6570\uff1a\u524d\u4e00\u4e2a\u8282\u70b9\u5f02\u5e38, \u4e0d\u6267\u884c\u4e0b\u4e00\u4e2a\u8282\u70b9, \u5426\u5219\u6267\u884c\u4e0b\u4e00\u4e2a\u8282\u70b9\nand_node = and_(check_button1, check_button2, check_button3)\nworkflow.add_node(and_node)\n\n# \u7ec4\u5408\u4f7f\u7528\ncomplex_condition = or_(check_button1, and_(check_button2, check_button3))\nworkflow.add_node(complex_condition)\n```\n\n### \u5faa\u73af\u8282\u70b9\n\n```python\nfrom rpaworkflow.nodes import LoopNode, LambdaActionNode\n\n# \u521b\u5efa\u4e00\u4e2a\u8ba1\u6570\u5668\u8282\u70b9\nincrement_counter = LambdaActionNode(\n    name=\"\u589e\u52a0\u8ba1\u6570\u5668\",\n    action=lambda ctx: ctx.set(\"counter\", ctx.get(\"counter\", 0) + 1)\n)\n\n# \u521b\u5efa\u5faa\u73af\u8282\u70b9\nloop_node = LoopNode(\n    name=\"\u5faa\u73af\u6267\u884c\",\n    loop_condition=lambda ctx: ctx.get(\"counter\", 0) < 5,  # \u5faa\u73af\u6761\u4ef6\uff1a\u8ba1\u6570\u5668\u5c0f\u4e8e5\n    loop_node=increment_counter  # \u5faa\u73af\u6267\u884c\u7684\u8282\u70b9\n)\n\n# \u521d\u59cb\u5316\u8ba1\u6570\u5668\nworkflow.add_node(LambdaActionNode(\n    name=\"\u521d\u59cb\u5316\u8ba1\u6570\u5668\",\n    action=lambda ctx: ctx.set(\"counter\", 0)\n))\n\n# \u6dfb\u52a0\u5faa\u73af\u8282\u70b9\nworkflow.add_node(loop_node)\n```\n\n### \u7a7a\u8282\u70b9\u548c\u9519\u8bef\u8282\u70b9\n\n```python\nfrom rpaworkflow.nodes import EmptyNode, ErrorNode, ErrorStorageNode\n\n# \u7a7a\u8282\u70b9 - \u4e0d\u6267\u884c\u4efb\u4f55\u64cd\u4f5c\uff0c\u53ef\u7528\u4f5c\u5360\u4f4d\u7b26\nworkflow.add_node(EmptyNode(name=\"\u7a7a\u64cd\u4f5c\"))\n\n# \u9519\u8bef\u8282\u70b9 - \u629b\u51fa\u5f02\u5e38\nworkflow.add_node(ErrorNode(\n    name=\"\u629b\u51fa\u9519\u8bef\",\n    error=\"\u81ea\u5b9a\u4e49\u9519\u8bef\u4fe1\u606f\"  # \u4e5f\u53ef\u4ee5\u4f20\u5165Exception\u5bf9\u8c61\n))\n\n# \u9519\u8bef\u5b58\u50a8\u8282\u70b9 - \u629b\u51fa\u5f02\u5e38\u5e76\u5b58\u50a8\u5230\u4e0a\u4e0b\u6587\nworkflow.add_node(ErrorStorageNode(\n    name=\"\u5b58\u50a8\u9519\u8bef\",\n    error=\"\u81ea\u5b9a\u4e49\u9519\u8bef\u4fe1\u606f\",\n    output_key=\"error_info\"  # \u9519\u8bef\u4fe1\u606f\u5c06\u5b58\u50a8\u5728\u4e0a\u4e0b\u6587\u7684\u8fd9\u4e2a\u952e\u4e0b\n))\n```\n\n## \u8d21\u732e\n\n\u6b22\u8fce\u8d21\u732e\u4ee3\u7801\u3001\u62a5\u544a\u95ee\u9898\u6216\u63d0\u51fa\u6539\u8fdb\u5efa\u8bae\uff01\n\n## \u8bb8\u53ef\u8bc1\n\n\u672c\u9879\u76ee\u91c7\u7528 MIT \u8bb8\u53ef\u8bc1\u3002\u8be6\u89c1 LICENSE \u6587\u4ef6\u3002\n",
    "bugtrack_url": null,
    "license": "MIT",
    "summary": "RPA\u5de5\u4f5c\u6d41\u6846\u67b6\uff0c\u652f\u6301\u591a\u79cd\u81ea\u52a8\u5316\u5de5\u5177\u7684\u5de5\u4f5c\u6d41\u7ba1\u7406\u548c\u8282\u70b9\u6267\u884c",
    "version": "0.0.1",
    "project_urls": {
        "Bug Tracker": "https://github.com/yourusername/rpaworkflow/issues",
        "Homepage": "https://github.com/yourusername/rpaworkflow"
    },
    "split_keywords": [
        "automation",
        " playwright",
        " rpa",
        " selenium",
        " uiautomator2",
        " workflow"
    ],
    "urls": [
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "bc9f059bcae867141aaf815267f50fcc63d3945b9c3b23fa41b32cf202acb021",
                "md5": "c7abc6ab7cb16bd1d3cda0e58231d879",
                "sha256": "94d8923d680e3215ab8dc5c93cde57928fd7329ee6e2f4b1538cf8f2ac372318"
            },
            "downloads": -1,
            "filename": "rpaworkflow-0.0.1-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "c7abc6ab7cb16bd1d3cda0e58231d879",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": ">=3.11",
            "size": 137859,
            "upload_time": "2025-08-06T10:08:30",
            "upload_time_iso_8601": "2025-08-06T10:08:30.325878Z",
            "url": "https://files.pythonhosted.org/packages/bc/9f/059bcae867141aaf815267f50fcc63d3945b9c3b23fa41b32cf202acb021/rpaworkflow-0.0.1-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "80bf80cd3b0d44e8a42afc5668304481e86cfddac834e193ad47cd7d74a42772",
                "md5": "7cc16434914818edbf736fe5ea2aeac9",
                "sha256": "4b10b49d15f5091ffbfb5fc23922ef033039a4e2eb529f35c6cb04be453ce194"
            },
            "downloads": -1,
            "filename": "rpaworkflow-0.0.1.tar.gz",
            "has_sig": false,
            "md5_digest": "7cc16434914818edbf736fe5ea2aeac9",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.11",
            "size": 143481,
            "upload_time": "2025-08-06T10:08:32",
            "upload_time_iso_8601": "2025-08-06T10:08:32.337899Z",
            "url": "https://files.pythonhosted.org/packages/80/bf/80cd3b0d44e8a42afc5668304481e86cfddac834e193ad47cd7d74a42772/rpaworkflow-0.0.1.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2025-08-06 10:08:32",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "yourusername",
    "github_project": "rpaworkflow",
    "github_not_found": true,
    "lcname": "rpaworkflow"
}
        
Elapsed time: 2.26868s