知识周汇|SAP脚本自动化-淋过雨的人更懂得伞的价值
目录
摘要
1 知识概览
1.1SAP GUI脚本
1.2Tracker工具
2 实践案例
2.1步骤1:SAP启动并进入系统(文本关键)
2.1.1手动操作:鼠标双击SAP,并点击所需要系统
2.1.2代码实现
2.2步骤2:通过tracker完善后续操作
2.3完整代码示例
3 常见问题
3.1问题1:提示用户已禁用脚本支持
3.2问题2:如何将SAP界面设置成经典界面
4 参考文章
5 总结与思考
摘要
在许多企业中,核心业务数据通常存储在SAP系统中,且仅限内网访问以确保数据安全。然而,传统的手动操作方式,如逐条输入指令和频繁点击界面,不仅效率低下,还容易出错,给办公人员带来了极大的负担。为了解决这一问题,本文通过SAP GUI脚本模拟人工操作,实现数据下载的自动化。这种方法不仅显著提升了操作效率,还减少了重复性工作,从而大幅提高了办公人员的幸福度。
1 知识概览
本周知识点汇总时,先了解几个概念
1.1SAP GUI脚本
SAP GUI脚本:可以模拟用户在SAP GUI中的操作,如输入事务代码、填写表单、点击按钮等。它的概念与VBA(Visual Basic for Applications)中的宏在概念和功能上非常相似,都是通过录制或编写脚本来自动化重复性任务,从而提高工作效率。以下SAP录制脚本按钮,但是录制功能只有进入SAP指定系统后才能使用。
1.2Tracker工具
Tracker 是一款用于自动化操作 SAP GUI 的辅助工具,能够记录用户在 SAP 界面中的操作步骤,并将其转换为 Python 脚本。通过 Tracker,用户可以快速生成操作 SAP 的 Python 代码,减少手动编写脚本的工作量,提升开发效率。
Tracker软件下载(绿色版本下载直接用)
通过网盘分享的文件:Tracker_x64
链接: https://pan.baidu.com/s/1n-fyp4zqC-2L_MLxrbxT8w?pwd=smam 提取码: smam
2 实践案例
实践案例将基于人工操作步骤,逐步实现自动化。
2.1步骤1:SAP启动并进入系统(文本关键)
2.1.1手动操作:鼠标双击SAP,并点击所需要系统
2.1.2代码实现
# coding=utf-8
# 指定文件编码为UTF-8,确保脚本可以处理中文字符
import os # 用于操作系统相关功能,如执行命令
import subprocess # 用于启动外部程序
import time # 用于时间控制,如延时
import win32com.client # 用于与COM对象交互(如SAP GUI)
import win32con # 提供Windows常量
import win32gui # 用于操作Windows GUI元素
# SAP登录启动函数
def saplogon_start():
sessionCnt = 0 # 初始化会话计数器
while sessionCnt == 0: # 如果没有成功创建会话,则循环尝试
# SAP Logon程序的本地路径
sap_app = r"C:\Program Files (x86)\SAP\FrontEnd\SAPgui\saplogon.exe"
# 使用subprocess启动SAP Logon程序
subprocess.Popen(sap_app)
time.sleep(1) # 等待1秒,确保程序启动
flt = 0 # 初始化过滤器句柄
while flt == 0: # 循环直到找到过滤器输入框
try:
# 查找SAP Logon窗口句柄
hwnd = win32gui.FindWindow(None, "SAP Logon 770")
# 查找过滤器输入框的句柄
flt = win32gui.FindWindowEx(hwnd, None, "Edit", None)
except:
time.sleep(0.5) # 如果未找到,等待0.5秒后重试
# 向过滤器输入框发送文本"PS4"
win32gui.SendMessage(flt, win32con.WM_SETTEXT, None, "PS4")
# 模拟按下右键(用于确认输入)
win32gui.SendMessage(flt, win32con.WM_KEYDOWN, win32con.VK_RIGHT, 0)
win32gui.SendMessage(flt, win32con.WM_KEYUP, win32con.VK_RIGHT, 0)
time.sleep(0.1) # 等待0.1秒
# 查找登录按钮的句柄
dlg = win32gui.FindWindowEx(hwnd, None, "Button", None)
# 模拟点击登录按钮
win32gui.SendMessage(dlg, win32con.WM_LBUTTONDOWN, 0)
win32gui.SendMessage(dlg, win32con.WM_LBUTTONUP, 0)
# 获取SAP GUI的COM对象
SapGuiAuto = win32com.client.GetObject("SAPGUI")
if not type(SapGuiAuto) == win32com.client.CDispatch:
return # 如果获取失败,直接返回
# 获取SAP脚本引擎
application = SapGuiAuto.GetScriptingEngine
if not type(application) == win32com.client.CDispatch:
SapGuiAuto = None # 如果获取失败,释放资源并返回
return
time.sleep(1) # 等待1秒
# 获取第一个连接
connection = application.Children(0)
if not type(connection) == win32com.client.CDispatch:
application = None # 如果获取失败,释放资源并返回
SapGuiAuto = None
return
time.sleep(2) # 等待2秒
flag = 0 # 初始化标志位
sessionCnt = connection.Children.count # 获取当前连接的会话数量
try:
# 获取第一个会话
session = connection.Children(0)
if not type(session) == win32com.client.CDispatch:
connection = None # 如果获取失败,释放资源并返回
application = None
SapGuiAuto = None
return
except:
try:
# 如果发生异常,尝试强制关闭SAP Logon程序
os.system('taskkill /F /IM saplogon.exe')
except:
pass # 如果关闭失败,忽略异常
return session # 返回成功创建的会话对象
代码解读:
1. 导入模块
import os
import subprocess
import time
import win32com.client
import win32con
import win32gui
-
os
:用于执行系统命令(如终止进程)。 -
subprocess
:用于启动外部程序(如 SAP Logon)。 -
time
:用于添加延时。 -
win32com.client
、win32con
、win32gui
:用于操作 Windows GUI 元素。
2. saplogon_start
函数
def saplogon_start():
sessionCnt = 0
while sessionCnt == 0:
sap_app = r"C:\Program Files (x86)\SAP\FrontEnd\SAPgui\saplogon.exe" # 您的saplogon程序本地完整路径
subprocess.Popen(sap_app)
time.sleep(1)
- 启动 SAP Logon 客户端。
- 使用
subprocess.Popen
启动saplogon.exe
程序。 -
添加 1 秒的延时,等待程序启动。
flt = 0
while flt == 0:
try:
hwnd = win32gui.FindWindow(None, "SAP Logon 770")
flt = win32gui.FindWindowEx(hwnd, None, "Edit", None) # capture handle of filter
except:
time.sleep(0.5)
-
查找SAP Logon窗口及其内部的输入框(用于输入系统名称)。
-
使用 win32gui.FindWindow和win32gui.FindWindowEx获取窗口句柄。
-
如果未找到窗口或输入框,等待 0.5 秒后重试。
win32gui.SendMessage(flt, win32con.WM_SETTEXT, None, "PS4")
win32gui.SendMessage(flt, win32con.WM_KEYDOWN, win32con.VK_RIGHT, 0)
win32gui.SendMessage(flt, win32con.WM_KEYUP, win32con.VK_RIGHT, 0)
time.sleep(0.1)
-
在输入框中输入系统名称(如 PS4)。
-
模拟键盘按下和释放右箭头键。
- 添加 0.1 秒的延时,等待输入完成。
dlg = win32gui.FindWindowEx(hwnd, None, "Button", None) # 登陆(0)
win32gui.SendMessage(dlg, win32con.WM_LBUTTONDOWN, 0)
win32gui.SendMessage(dlg, win32con.WM_LBUTTONUP, 0)
-
查找并点击登录按钮。
-
使用
win32gui.SendMessage
模拟鼠标点击。
SapGuiAuto = win32com.client.GetObject("SAPGUI")
if not type(SapGuiAuto) == win32com.client.CDispatch:
return
application = SapGuiAuto.GetScriptingEngine
if not type(application) == win32com.client.CDispatch:
SapGuiAuto = None
return
time.sleep(1)
connection = application.Children(0)
if not type(connection) == win32com.client.CDispatch:
application = None
SapGuiAuto = None
return
time.sleep(2)
-
获取 SAP GUI 脚本引擎对象。
-
获取当前连接和会话对象。
-
如果获取失败,返回
None
。
flag = 0
sessionCnt = connection.Children.count
try:
session = connection.Children(0)
if not type(session) == win32com.client.CDispatch:
connection = None
application = None
SapGuiAuto = None
return
except:
try:
os.system('taskkill /F /IM saplogon.exe')
except:
pass
return session
-
检查会话是否成功建立。
-
如果失败,终止 SAP Logon 进程并重试。
-
返回 SAP 会话对象。
3. 总结
- 功能:通过 Python 脚本自动化启动 SAP Logon 客户端并登录到指定系统。
- 适用场景:需要频繁登录 SAP 系统的场景。
- 依赖:需要安装
pywin32
库,并确保 SAP GUI 脚本功能已启用。
2.2步骤2:通过tracker完善后续操作
进入到以下界面就已经成功一大半啦,因为后面可以借助tracker软件快速生成操作 SAP 的 Python 代码
以下是操作步骤
将上述代码整理下,均是通过tracker生成代码
session.findById("wnd[0]").maximize()
session.findById("wnd[0]/usr/txtRSYST-BNAME").text = user_sap # 此次放入您的SAP登陆用户名
session.findById("wnd[0]/usr/pwdRSYST-BCODE").text = password_sap # 此次放入您的SAP登陆密码
session.findById("wnd[0]/usr/txtRSYST-LANGU").text = "EN" # 设置英文
session.findById("wnd[0]/tbar[0]/btn[0]").press()
try:
session.findById("wnd[1]/usr/radMULTI_LOGON_OPT2").select()
session.findById("wnd[1]/usr/radMULTI_LOGON_OPT2").setFocus()
session.findById("wnd[1]/tbar[0]/btn[0]").press()
except:
pass
try:
session.findById("wnd[1]/tbar[0]/btn[0]").press()
except:
pass
代码解读
这段代码用于自动化登录SAP系统,并处理登录过程中可能出现的多会话提示框。它通过SAP GUI的脚本接口(session.findById
)来操作SAP的窗口和控件。
session.findById("wnd[0]").maximize()
-
wnd[0]
是SAP GUI的主窗口标识。 -
maximize()
是最大化窗口的方法。
session.findById("wnd[0]/usr/txtRSYST-BNAME").text = user_sap
session.findById("wnd[0]/usr/pwdRSYST-BCODE").text = password_sap
-
在SAP登录界面的用户名输入框中输入用户名和密码
-
wnd[0]/usr/txtRSYST-BNAME
是用户名输入框的路径。 -
text
是输入框的属性,用于设置或获取文本内容。 -
user_sap
是变量,存储SAP登录用户名。 -
wnd[0]/usr/pwdRSYST-BCODE
是密码输入框的路径。 -
password_sap
是变量,存储SAP登录密码。
session.findById("wnd[0]/usr/txtRSYST-LANGU").text = "EN"
- 设置SAP登录界面的语言为英文。
wnd[0]/usr/txtRSYST-LANGU
是语言输入框的路径。"EN"
表示英文,可以根据需要更改为其他语言代码(如ZH
表示中文)。
session.findById("wnd[0]/tbar[0]/btn[0]").press()
-
点击SAP登录界面上的“确认”按钮。
-
wnd[0]/tbar[0]/btn[0]
是“确认”按钮的路径。 -
press()
是模拟点击按钮的方法。
try:
session.findById("wnd[1]/usr/radMULTI_LOGON_OPT2").select()
session.findById("wnd[1]/usr/radMULTI_LOGON_OPT2").setFocus()
session.findById("wnd[1]/tbar[0]/btn[0]").press()
except:
pass
try:
session.findById("wnd[1]/tbar[0]/btn[0]").press()
except:
pass
-
功能处理多会话登录提示框(如果有)。
-
wnd[1]
是多会话提示框的窗口标识。 -
wnd[1]/usr/radMULTI_LOGON_OPT2
是选择“继续登录”选项的单选框路径。 -
select()
是选择单选框的方法。 -
setFocus()
是将焦点设置到该选项。 -
wnd[1]/tbar[0]/btn[0]
是多会话提示框的“确认”按钮路径。 -
如果多会话提示框不存在,
try-except
会捕获异常并跳过。
2.3完整代码示例
# coding=utf-8
# coding=utf-8
import os
import subprocess
import time
import win32com.client
import win32con
import win32gui
# SAP开始共同部分
def saplogon_start():
sessionCnt = 0
while sessionCnt == 0:
sap_app = r"C:\Program Files (x86)\SAP\FrontEnd\SAPgui\saplogon.exe" # 您的saplogon程序本地完整路径
subprocess.Popen(sap_app)
time.sleep(1)
flt = 0
while flt == 0:
try:
hwnd = win32gui.FindWindow(None, "SAP Logon 770")
flt = win32gui.FindWindowEx(hwnd, None, "Edit", None) # capture handle of filter
except:
time.sleep(0.5)
win32gui.SendMessage(flt, win32con.WM_SETTEXT, None, "PS4")
win32gui.SendMessage(flt, win32con.WM_KEYDOWN, win32con.VK_RIGHT, 0)
win32gui.SendMessage(flt, win32con.WM_KEYUP, win32con.VK_RIGHT, 0)
time.sleep(0.1)
dlg = win32gui.FindWindowEx(hwnd, None, "Button", None) # 登陆(0)
win32gui.SendMessage(dlg, win32con.WM_LBUTTONDOWN, 0)
win32gui.SendMessage(dlg, win32con.WM_LBUTTONUP, 0)
SapGuiAuto = win32com.client.GetObject("SAPGUI")
if not type(SapGuiAuto) == win32com.client.CDispatch:
return
application = SapGuiAuto.GetScriptingEngine
if not type(application) == win32com.client.CDispatch:
SapGuiAuto = None
return
time.sleep(1)
connection = application.Children(0)
if not type(connection) == win32com.client.CDispatch:
application = None
SapGuiAuto = None
return
time.sleep(2)
flag = 0
sessionCnt = connection.Children.count
try:
session = connection.Children(0)
if not type(session) == win32com.client.CDispatch:
connection = None
application = None
SapGuiAuto = None
return
except:
try:
os.system('taskkill /F /IM saplogon.exe')
except:
pass
return session
def get_infor_from_SAP(user_sap, password_sap, sap_query, save_txt='保存.text'):
session = saplogon_start()
time.sleep(1)
session.findById("wnd[0]").maximize()
session.findById("wnd[0]/usr/txtRSYST-BNAME").text = user_sap # 此次放入您的SAP登陆用户名
session.findById("wnd[0]/usr/pwdRSYST-BCODE").text = password_sap # 此次放入您的SAP登陆密码
session.findById("wnd[0]/usr/txtRSYST-LANGU").text = "EN" # 设置英文
session.findById("wnd[0]/tbar[0]/btn[0]").press()
try:
session.findById("wnd[1]/usr/radMULTI_LOGON_OPT2").select()
session.findById("wnd[1]/usr/radMULTI_LOGON_OPT2").setFocus()
session.findById("wnd[1]/tbar[0]/btn[0]").press()
except:
pass
try:
session.findById("wnd[1]/tbar[0]/btn[0]").press()
except:
pass
"""下载记账数据过程"""
# session.findById("wnd[0]").maximize()
session.findById("wnd[0]/tbar[0]/okcd").text = sap_query
session.findById("wnd[0]/tbar[0]/btn[0]").press()
session.findById("wnd[0]/usr/ctxtS_FICTR-LOW").text = '000'
session.findById("wnd[0]/usr/ctxtPAR_01").text = '000'
session.findById("wnd[0]/usr/ctxtPAR_02").text = '000'
session.findById("wnd[0]/usr/ctxtPAR_03").text = str('1')
session.findById("wnd[0]/usr/ctxtPAR_04").text = str('2')
session.findById("wnd[0]/tbar[1]/btn[8]").press()
try:
session.findById("wnd[1]/tbar[0]/btn[0]").press()
session.findById("wnd[0]/usr/lbl[74,13]").setFocus()
session.findById("wnd[0]/usr/lbl[74,13]").caretPosition = 0
except:
pass
# 获取信息
try:
session.findById("wnd[0]").sendVKey(48)
session.findById("wnd[1]/usr/radCFDOWNLOAD-FORMAT_TEX").select()
session.findById("wnd[1]/usr/ctxtCFDOWNLOAD-FILE").text = save_txt
session.findById("wnd[1]/usr/ctxtCFDOWNLOAD-FILE").caretPosition = 59
session.findById("wnd[1]/tbar[0]/btn[0]").press()
except:
pass
try:
session.findById("wnd[2]/usr/btnSPOP-VAROPTION1").press()
except:
pass
session.findById("wnd[0]").close()
session.findById("wnd[1]/usr/btnSPOP-OPTION1").press()
try:
os.system('taskkill /F /IM saplogon.exe')
except:
pass
return save_txt
# 程序入口
if __name__ == "__main__":
get_infor_from_SAP(user_sap='123', password_sap='123', sap_query='123', save_txt='保存.text')
上述代码在实际运行中需要注意以下事项:①运行环境依赖:执行代码的计算机需要预先安装SAP客户端及相关组件;②安全配置:代码中的账务认证信息(包括用户名、密码等)需要替换为实际可用的有效凭证;③函数适配:get_infor_from_SAP()函数是通过tracker调试工具开发实现,在实际使用时需要根据具体项目需求进行相应调整
-
导入模块:引入所需的 Python 库。
-
saplogon_start
函数:启动 SAP Logon 客户端并登录到指定系统。 -
get_infor_from_SAP
函数:登录 SAP 系统,执行查询并下载数据。 -
主程序:调用
get_infor_from_SAP
函数,传入用户名、密码、查询代码和保存路径。
3 常见问题
3.1问题1:提示用户已禁用脚本支持
解决方案:
3.2问题2:如何将SAP界面设置成经典界面
解决方案:
4 参考文章
博客《使用Python完成SAP客户端的打开和系统登录 - NewJune - 博客园》
博客《python驱动SAP完成数据导出(一) - NewJune - 博客园》
博客《几行python代码轻松实现SAP自动登录 - NewJune - 博客园》
5 总结与思考
感谢各位耐心看到最后,这篇文章是我一直想写的。过去几年,随着部门业务的调整,接手了部门中最繁琐的业务,SAP成为日常工作的核心。然而,手动操作效率低下,一直都很痛苦。在尝试来也科技的UiBot未果后,最终通过Python实现了SAP数据的自动下载,完成了从0到1的突破。结合pandas进行数据分析,极大提升了工作效率。特别感谢上述4的参考文章,它们写得非常好。正如那句“曾经淋过雨的人,更懂得伞的价值”,愿我手中的伞,为你撑起一片无雨的晴空。希望这篇文章能对各位有所帮助。