简单记录

时间相关

获取格式化的当前时间

dtmNowTime = Time.Now()
strFormatCurrentTime = Time.Format(dtmNowTime, "yyyy-MM-dd hh:mm:ss")
Log.Info("当前时间:" & strFormatCurrentTime)
strFormatCurrentTime = Time.Format(Time.Now(), "yyyy-MM-dd hh:mm:ss")
Log.Info("当前时间:" & strFormatCurrentTime)

对日期时间进行加减

减一年:Time.DateAdd(“yyyy”, -1, dtmNowTime) 减一季度:Time.DateAdd(“q”, -1, dtmNowTime) 减一月:Time.DateAdd(“m”, -1, dtmNowTime) 减一星期:Time.DateAdd(“ww”, -1, dtmNowTime) 减一天:Time.DateAdd(“d”, -1, dtmNowTime) 减一小时:Time.DateAdd(“h”, -1, dtmNowTime) 减一分钟:Time.DateAdd(“n”, -1, dtmNowTime) 减一秒:Time.DateAdd(“s”, -1, dtmNowTime)

dtmNowTime = Time.Date()
dtmYesterdayTime = Time.DateAdd("d", -1, dtmNowTime)
strFormatYesterdayTime = Time.Format(dtmYesterdayTime , "yyyymmdd")

加的话,把 -1 改成 1

数据操作相关

字符相关

判断指定字符是否在字符串内

判断 a 是否在 asdfghjll 内。没有 in 或者 contains 之类的可以使用,只能是获取一个字符串在另一个字符串中首次出现的位置。

InStr("asdfghjll", "a", 0, False)

但是返回的是位置(数字),不能直接用于判断,所以需要再加个判断,返回的才是 Ture 或者 False

InStr("asdfghjll", "a", 0, False) > 0

https://learn.microsoft.com/zh-cn/office/vba/language/reference/user-interface-help/instr-function

数组相关

获取数组的元素数量

UBound(array)

数组头部添加元素

Unshift(array, "123")

数组过滤

arrFilePath = Filter(arrFilePath, "123", false)

数组克隆

arrNewName = Clone(arrName)

使用变量赋值命令操作数组时,改变赋值后的数组,原数组也发生变化

字符串分割成数组

以指定字符串做分割

 arrRet = Split("qfgh|ert","|")

无指定分隔符,每个字符变为数组的一个元素

arrRet = Regex.FindAll("qfgh|s的发ert","[\\s\\S]")

数组合并成字符串

strNewName = Join(arrNewName, "")

数据表相关

数据表筛选

字段包含指定字符串

objDatatable = Datatable.QueryDataTable(objDatatable,"列名.str.contains('人民币')")

字段包含指定字符串,指定字符串是变量

objDatatable = Datatable.QueryDataTable(objDatatable,"列名.str.contains(\""&筛选关键词&"\")"   

字段等于指定字符串

objDatatable = Datatable.QueryDataTable(objDatatable,"列名=='人民币'")
objDatatable = Datatable.QueryDataTable(objDatatable,"列名==" & strCurrencyType)

字段为空。

objDatatable = Datatable.QueryDataTable(objDatatable,"列名.isnull()")

集合相关

集合初始化时赋值

ObjSet = Set.Create(["1","2"])

字典相关

字典初始化时赋值

dicName = {"1":2,"A":3}

文件/文件夹相关

在指定文件夹下,查找指定名称的文件

arrFilePath = File.SearchFile('''文件夹路径''', "文件名称*.xls*", True)

在指定文件夹下,获取文件或文件夹列表

文件列表,返回的是文件名称

arrFilePath = File.DirFileOrFolder(strInputFolderPath, "file", {"hasPath": false})

文件夹列表,返回的是文件夹的绝对路径

arrFilePath = File.DirFileOrFolder(strInputFolderPath, "file", {"hasPath": True})

文件和文件夹列表,返回的是文件和文件夹的绝对路径

arrFilePath = File.DirFileOrFolder(strInputFolderPath, "fileandfolder", {"hasPath": True})

获取文件扩展名

strExtensionName = File.ExtensionName('''C:\Users''')

获取文件/文件夹名称

File.BaseName('''C:\Users''')

正则相关

正则测试是否能找到

Regex.Test(strFileName, "台账表.*.xls.*")

JS 相关

隐藏元素

document.querySelector(".QuestionButtonGroup").style.display = "none"
document.querySelector(".QuestionButtonGroup").style.visibility = "hidden"
document.querySelector(".QuestionButtonGroup").style.opacity = 0

https://developer.mozilla.org/zh-CN/docs/Web/API/Document/querySelector

显示元素

document.querySelector(".QuestionButtonGroup").style.display = "block"
document.querySelector(".QuestionButtonGroup").style.visibility = "visible"
document.querySelector(".QuestionButtonGroup").style.opacity = 1

获取 iframe 里面的元素

document.querySelector("#iframeResult").contentDocument.querySelector("img")

获取 iframe 里面的元素属性

document.querySelector("#iframeResult").contentDocument.querySelector("img").alt

Python 相关

pip install 的时候临时换源

pip install -i https://pypi.tuna.tsinghua.edu.cn/simple 需要安装的依赖库

pyautogui 实现鼠标移动到绝对坐标

import pyautogui
# 移动鼠标,duration为持续时间
pyautogui.moveTo(100, 100, 0.25) # 持续时间为 0.25 秒,移动到 (100,100),

pyautogui 实现鼠标相对移动

import pyautogui
# pyautogui.FAILSAFE = False # 默认为 True,当鼠标位于主显示器的四个角中的任何一个位置,就会报错;Fasle 为不启用自动防故障,
# pyautogui.PAUSE = 2.5 # 默认 0.1 秒,每个 pyautogui 命令的间隔停顿 0.1 秒

pyautogui.moveRel(500, 0, duration=2) # 从当前位置移动鼠标,持续时间为 2 秒
pyautogui.moveTo(100, 100, 2, pyautogui.easeInQuad) # 开始很慢,不断加速 
pyautogui.moveTo(100, 700, 2, pyautogui.easeOutQuad) # 开始很快,不断减速 
pyautogui.moveTo(700, 100, 2, pyautogui.easeInOutQuad) # 开始和结束都快,中间比较慢 
import pyautogui

pyautogui.moveTo(None, 500) # 坐标传 None 值,表示当前鼠标光标位置
pyautogui.move(-30, None)
pyautogui.move(-30, 0) 

https://pyautogui.readthedocs.io/en/latest/

https://pyautogui.readthedocs.io/en/latest/mouse.html#tween-easing-functions

正则解析中文时间

用正则的思路去实现,代码比较长

import re
import time
from datetime import datetime


class ParseTimeError(Exception):
    """解析时间失败"""


def get_days_ago_date(days, date_format='%Y-%m-%d %H:%M:%S'):
    days = int(days)
    subtractTime = time.time() - days * 24 * 60 * 60
    formatTime = time.strftime(date_format, time.localtime(subtractTime))
    return formatTime


def get_hours_ago_date(hours, date_format='%Y-%m-%d %H:%M:%S'):
    hours = int(hours)
    subtractTime = time.time() - hours * 60 * 60
    formatTime = time.strftime(date_format, time.localtime(subtractTime))
    return formatTime


def get_mins_ago_date(mins, date_format='%Y-%m-%d %H:%M:%S'):
    mins = int(mins)
    subtractTime = time.time() - mins * 60
    formatTime = time.strftime(date_format, time.localtime(subtractTime))
    return formatTime


def parse_time(dateTimeText):
    """根据传入的时间文本进行解析,获取对应的日期时间
    :param dateTimeText: 需要解析的时间文本
    :return: 解析到的日期时间
    :raises: 解析失败时抛出 ParseTimeError
    """
    dateTimeText = str(dateTimeText)
    print("用于解析的时间文本:" + dateTimeText)
    transformTime = ""

    try:
        transformTime = datetime.strptime(dateTimeText, '%Y-%m-%d')
        return dateTimeText
    except ValueError:
        print("输入日期文本不为 yyyy-mm-dd 格式,进行后续处理")

    if '天前' in dateTimeText:
        a = re.findall('(.*?)天前', dateTimeText)
        transformTime = get_days_ago_date(a[0], date_format='%Y-%m-%d')

    if '前天' in dateTimeText:
        interTime = 3
        transformTime = get_days_ago_date(interTime, date_format='%Y-%m-%d')

    if '昨天' in dateTimeText:
        interTime = 1
        transformTime = get_days_ago_date(interTime, date_format='%Y-%m-%d')

    if '小时前' in dateTimeText:
        a = re.findall('(.*?)小时前', dateTimeText)
        transformTime = get_hours_ago_date(a[0], date_format='%Y-%m-%d')

    if '分钟前' in dateTimeText:
        a = re.findall('(.*?)分钟前', dateTimeText)
        transformTime = get_mins_ago_date(a[0], date_format='%Y-%m-%d')

    if '秒前' in dateTimeText:
        interTime = 0
        transformTime = get_mins_ago_date(interTime, date_format='%Y-%m-%d')

    if '刚刚' in dateTimeText:
        interTime = 0
        transformTime = get_mins_ago_date(interTime, date_format='%Y-%m-%d')

    if (not transformTime):
        raise ParseTimeError(f'解析时间: {dateTimeText},失败')

    return transformTime


print(parse_time('2023-03-15'))
print(parse_time('7天前'))
print(parse_time('5天前'))
print(parse_time('4天前'))
print(parse_time('前天'))
print(parse_time('昨天'))
print(parse_time('20小时前'))
print(parse_time('10小时前'))
print(parse_time('10分钟前'))
print(parse_time('10秒前'))
print(parse_time('今晚加班'))
print(parse_time('17小时前'))

用 sinan 实现解析中文时间

from sinan import Sinan

obj = Sinan('5秒前')
result = obj.parse()

print(result)
print(result['datetime'][0])

https://github.com/yiyujianghu/sinan

返回信息:

参数名 含义 示例
datetime 日期时间信息 ‘2020-08-05 17:00:00’
length 长度信息 (5000.0, ‘m’)
weight 质量信息 (2.0, ‘kg’)
time 时间信息 (5400.0, ‘s’)
temperature 温度信息 (20.0 ‘℃’)
money 钱数信息 (300.0, ‘元’)
people 人数信息 (2.0, ‘人’)
phone_num 手机号码 ‘18610086138’
identification 身份证号 ‘110108199703069999’

解析时间

from dateutil.parser import parse

logline = "INFO 2020-01-01T00:00:01 ."
timestamp = parse(logline, fuzzy=True)
print(timestamp)

检测编码

import chardet

print(chardet.detect(b'Hello, world!'))

data = '离离原上草,一岁一枯荣'.encode('gbk')
print(chardet.detect(data))

data = '离离原上草,一岁一枯荣'.encode('utf-8')
print(chardet.detect(data))

获取所有进程名称

import psutil

pids = psutil.pids()
for pid in pids:
    p = psutil.Process(pid)
    print("pid:%d,%s" % (pid, p.name()))

结束包含指定名称的进程

import os
import time

import psutil

os.startfile(r"C:\Windows\System32\PhotoScreensaver.scr") # 启动屏保
time.sleep(10)

pids = psutil.pids()
for pid in pids:
    p = psutil.Process(pid)

    if ".scr" in p.name():
        print(pid, p.name())
        p.terminate() # 终止进程
        # p.kill() # 杀掉进程

跨工作簿复制工作表

import xlwings as xw


def copy_sheet(source_file_path, target_file_path, sheet_name):
    """读取来源 Excel 文件,复制指定 Sheet 到目标 Excel 文件
    :param source_file_path: 来源 Excel 文件的路径
    :param target_file_path: 目标 Excel 文件的路径
    :param sheet_name: 需要复制的 Sheet 名称
    """
    app = xw.App(visible=True)  # 打开 Excel,visible=True 可见,visible=False,不可见
    # 打开来源 Excel 文件
    source_book = xw.Book(source_file_path)
    print("已打开来源 Excel 文件,路径:" + source_file_path)

    # 打开目标 Excel 文件
    target_book = xw.Book(target_file_path)
    print("已打开目标 Excel 文件,路径:" + target_file_path)

    # 复制指定 Sheet 到目标 Excel。-1代表复制到最后一个,0代表复制到倒数第二个
    source_book.sheets[sheet_name].copy(after=target_book.sheets[-1])
    print(f"已复制:{sheet_name}{target_file_path}")

    target_book.save()
    source_book.save()
    target_book.close()
    source_book.close()

    app.quit()  # 关闭excel

https://docs.xlwings.org/zh_CN/latest/quickstart.html

Uibot 密文解密

UiBot 读取 commander 的凭据或者是凭据管理器的凭据,读取出来的密码是密文,需要解密才能获取到明文。

但是为什么这代码要这么写,我不懂,给到我的代码就这样,我就格式化了一下代码。

try:
    import UiBot
except Exception as e:
    class Temp():
        def GetString(self, strPath):
            return ""


    UiBot = Temp()


def _Decrypt(pwd):
    UiBot.PushContext()
    try:
        # 解密失败 抛出异常
        pwd = UiBot.InvokeRobotCore(0, 'GetStringFromSecText', ['', pwd])
    except:
        pass
    UiBot.PopContext()
    return pwd

CMD命令行相关

显示进程列表

tasklist

以 csv 格式显示进程列表,默认格式是table

tasklist /fo csv

筛选出谷歌浏览器的进程

tasklist /fi "IMAGENAME eq chrome.exe"

筛选出 chrome 开头的进程

tasklist /fi "IMAGENAME eq chrome*"

筛选出包含 msedge 的进程

tasklist | findstr /i msedge

筛选出包含 .scr 的进程

tasklist | findstr /i \.scr

筛选出以 .scr 结尾的进程

tasklist |  findstr /i "\.scr\>"

https://learn.microsoft.com/zh-cn/windows-server/administration/windows-commands/tasklist

https://learn.microsoft.com/zh-cn/windows-server/administration/windows-commands/findstr

筛选出 .exe 的进程

wmic Process Where "Name  like '%.exe'" Get Name

杀掉进程

kill 掉谷歌浏览器的进程

Taskkill /f /im chrome.exe

kill 掉屏保

Taskkill /f /t /im PhotoScreensaver.scr

kill 掉 msedge 开头的进程

Taskkill /f /fi "IMAGENAME eq msedge*"
Taskkill /f /t /im msedge*

kill 掉包含 msedge 的进程

for /f %a in ('tasklist ^| findstr /i msedge') do taskkill /f /fi "imagename eq %a"

kill 掉 .scr 结尾的进程,推荐 tasklist 的方式

for /f %a in ('tasklist ^| findstr /i "\.scr\>"') do taskkill /f /fi "imagename eq %a"
for /f %a in ( 'wmic Process Where "Name  like '%.scr'" Get Name') do taskkill /f /fi "imagename eq %a"

https://learn.microsoft.com/zh-cn/windows-server/administration/windows-commands/taskkill

https://learn.microsoft.com/zh-cn/windows-server/administration/windows-commands/findstr

其他

截全屏(元素截图)

UiBot截全屏(元素截图)

显示桌面背景的窗口类名称是 Program,它是 z-index 最低的顶级窗口,所以始终在所有窗口下面。而显示桌面图标的窗口类名称是 SHELLDLL_DefView,是它的子窗口。

SysListView32 和 SHELLDLL_DefView 是管理和显示桌面图标的。Progman 是显示出来的总桌面,也是总程序.

只需要给 Program Manager **窗口发送一个消息 **0x52C ,就可以将 Program Manager 拆分为多个窗口,分别是 Program Manager **窗口和两个 **WorkerW 窗口。

https://blog.csdn.net/qq_40036189/article/details/108210747

https://www.cnblogs.com/choumengqizhigou/p/15702980.html

http://suquark.github.io/lecture/2017/07/01/wallpaper_engine-DIY.html

实际的使用中,建议只选择到 Program Manager 这个顶级窗口,再下面一层的就不再选择。

实际情况下,用 Spy++ 查看过,存在没有 SysListView32 和 SHELLDLL_DefView 的情况,Windows 多窗口分屏(左右分屏)即可复现。

20230513_Program Manager 拆分为多个窗口

20230512_Program Manager

Spy++帮助

截全屏(快捷键 PrintScreen)

截图出来的文件会很大,好几M,不太建议。

Keyboard.Press("PrintScreen", "press", [],{"iDelayAfter": 300, "iDelayBefore": 200, "sSimulate": "simulate"})
Clipboard.SaveImage('''C:\Users\lbj\Desktop\test.jpg''')

杀屏保

如果无法通过设置来调整屏保的话,只能是通过杀掉屏保进程的方式解决。

屏保为 .scr 文件,系统自带的屏保存放在 C:\Windows\System32 文件夹下,可以直接 App.Kill 杀掉屏保

App.Run('''C:\Windows\System32\PhotoScreensaver.scr /s''',0,1)  // 打开屏保
Delay(5000)
App.Kill("PhotoScreensaver.scr") // 杀掉屏保

防止电脑锁屏

如果无法通过设置来防止电脑进入休眠,只能使用以下的方式。

https://www.appinn.com/mouse-jiggler-2/

https://github.com/arkane-systems/mousejiggler

已验证过可行,shell:startup ,把快捷方式加到开机自启,快捷方式添加:-j -m -z 即可

快捷方式,修改属性,把快捷方式的目标 修改为类似:”C:\Program Files\MouseJiggler.exe” -j -m -z

仅对当前用户生效:shell:startup 对所有用户生效:shell:common startup

8个用来取消或中止 Windows 关闭/重启的工具

或者弄个视频一直播放

微软的 PowerToys Awake

短信通知

用起来还可以,一个post请求就能发送短信,0.1 元一条:http://console.smsbao.com/#/index

阿里云的,签名和模板可能不好通过申请:https://dysms.console.aliyun.com/overview

模拟操作、后台操作、系统消息三种操作类型的区别

在Creator中设计RPA流程时,高频用到的命令有“点击目标”、“在目标中输入”等,在这些命令中都一个同名属性”操作类型”,分别有”模拟操作”、”后台操作”、”系统消息”这3个下拉选择项。

那么它们有什么区别,应该怎么使用呢?

1、模拟操作:指通过调用系统 api mouseevent 等实现鼠标操作,会实际移动光标 。

2、系统消息:指发送鼠标消息到目标元素,不移动光标。

3、后台操作:可以理解为调用了一次元素的鼠标响应回调函数。

那他们分别是在哪种场景下使用呢?

由上往下是底层到上层的顺序,越底层兼容性越好,上层有速度和精准的优势。

1、模拟操作 2、系统消息 3、后台操作

https://docs-repo.uibot.com.cn/simulation-backstage-system