# 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"
}