1、鼠标、键盘控制
from pynput import mouse, keyboard
from pynput.keyboard import Key
import time
time.sleep(3)
keyC = keyboard.Controller()
mouseC = mouse.Controller()
print('鼠标位置:{0}'.format(mouseC.position))
mouseC.position = (0, 0) # (设置)鼠标位置
mouseC.move(0, 0) # 鼠标移动(x,y)个距离
mouseC.scroll(0, -100) # 鼠标滚轮向上滚动 dx, dy:水平、竖直方向滚动
mouseC.scroll(0, 100) # 鼠标滚轮向下滚动
mouseC.click(mouse.Button.left, 1) # 单击鼠标左键
mouseC.press(mouse.Button.left) # 按下鼠标左键
mouseC.release(mouse.Button.left) # 释放鼠标左键
keyC.press('a') # 键盘按下小写字母a
keyC.release('a') # 键盘释放小写字母a
keyC.type("abcd") # 键盘输入字符串 abcd
with keyC.pressed(Key.shift): # 键盘输入 shift+a
keyC.press('a')
keyC.release('a')
def key_press(key):
try: # 正常按字母、数字、标点符号,是char类型
print('按下字符键:{0}'.format(key.char))
except AttributeError: # 按特殊键时
print('按下特殊键:{0}'.format(key))
def key_release(key):
print('释放键:{0}'.format(key))
if key == Key.esc: # 当检查到释放esc键时,退出键盘监测
return False
def listen_move(x, y): # 监听鼠标移动
pass
# print('Pointer moved to {0}'.format((x, y)))
def listen_click(x, y, button, pressed): # 监听鼠标点击
print('{0} at {1}'.format('Pressed' if pressed else 'Released', (x, y)))
if not pressed:
# 单击结束,没按压,pressed=False,则退出 mouse.Listener()
return False
def listen_scroll(x, y, dx, dy): # 监听鼠标滚轮
print('Scrolled {0}'.format((x, y)))
print('鼠标位置:{0}'.format(mouseC.position))
with keyboard.Listener(on_press=key_press, on_release=key_release) as listener:
listener.join()
with mouse.Listener(on_move=listen_move, on_click=listen_click, on_scroll=listen_scroll) as listener:
listener.join()
2、win32gui
参考:https://blog.csdn.net/weixin_39743423/article/details/111444884
2.1、获得窗口句柄\类名\标题名称
- FindWindow(类名, 窗口名)
- FindWindow(lpClassName=None, lpWindowName=None) 窗口类名 窗口标题名,这两个至少要给出一个,另外一个可以为None
import win32gui, win32con, win32api
# 获取 句柄:FindWindow(lpClassName=None, lpWindowName=None) 窗口类名 窗口标题名,这两个至少要给出一个,另外一个可以为None
handle = win32gui.FindWindow("Notepad", None) # Notepad就是笔记本的类名
print(handle) # 若找到对应句柄则输出数字,否则输出0
# 例如只知道窗口名称:handle = win32gui.FindWindow(None, "'无标题 - 记事本'")
# 获取 窗口位置
x1, y1, x2, y2 = win32gui.GetWindowRect(handle) # 左上角坐标(x1, y1) 右下角坐标(x2, y2)
# 获取 句柄的类名、标题名
classname = win32gui.GetClassName(handle) # classname='Notepad'
title = win32gui.GetWindowText(handle) # title='无标题 - 记事本'
2.2、遍历子窗口
import win32gui, win32con, win32api
# 获取 句柄:FindWindow(lpClassName=None, lpWindowName=None) 窗口类名 窗口标题名,这两个至少要给出一个,另外一个可以为None
handle = win32gui.FindWindow("Notepad", None) # Notepad就是笔记本的类名
def callbackFun(hwnd, param): # param:hwndChildList hwnd是?
param.append(hwnd)
hwndChildList = []
win32gui.EnumChildWindows(handle, callbackFun, hwndChildList) # 可用lambda表达式:lambda hwnd, param: param.append(hwnd)
print(hwndChildList)
3、正则表达式(re模块)
3.1、 re.match(pattern, string, flags=0) & .group()
从string的起始位置匹配pattern,若不是起始位置匹配成功的话,则返回 none:
pattern:正则表达式,string:原字符串,
In [4]: re.match(pattern='abc',string='abcStr',flags=0)
Out[4]: <re.Match object; span=(0, 3), match='abc'>
In [5]: print(re.match(pattern='bc',string='abcStr',flags=0))
None
- flags:标志位
修饰符 | 描述 |
---|---|
re.I | 使匹配对大小写不敏感 |
re.L | 做本地化识别(locale-aware)匹配 |
re.M | 多行匹配,影响 ^ 和 $ |
re.S | 使 . 匹配包括换行在内的所有字符 |
re.U | 根据Unicode字符集解析字符。这个标志影响 \w, \W, \b, \B. |
re.X | 该标志通过给予你更灵活的格式以便你将正则表达式写得更易于理解。 |
例如: |
In [9]: re.match(pattern='abc',string='ABCStr',flags=re.I) #re.I表示不区分大小写
Out[9]: <re.Match object; span=(0, 3), match='ABC'>
3.2、.group()、.groups()方法:
In [13]: re.match(pattern='([0-9]*)([a-z]*)([0-9]*)',string='123abc456').group() # 匹配整个表达式,等价于group(0)
Out[13]: '123abc456'
In [14]: re.match(pattern='([0-9]*)([a-z]*)([0-9]*)',string='123abc456').group(1) # 匹配第1个括号(即第1个group)
Out[14]: '123'
In [15]: re.match(pattern='([0-9]*)([a-z]*)([0-9]*)',string='123abc456').group(2) # 匹配第2个括号(即第2个group)
Out[15]: 'abc'
In [16]: re.match(pattern='([0-9]*)([a-z]*)([0-9]*)',string='123abc456').group(3) # 匹配第3个括号(即第3个group)
Out[16]: '456'
In [18]: re.match(pattern='([0-9]*)([a-z]*)([0-9]*)',string='123abc456').groups()
Out[18]: ('123', 'abc', '456')
3.3、re.search(pattern, string, flags=0)
扫描整个字符串并返回第一个成功的匹配
3.4、常用符号
符号 | 描述 | 示例 |
---|---|---|
\ | 可以理解为转义字符(将下一个字符标记为一个特殊字符、或一个原义字符、或一个 向后引用、或一个八进制转义符) | \n 匹配 换行符 \\ 匹配 \ In [30]: re.sub(pattern=“\\\\”, repl=“/”, string=“\\”) Out[30]: ‘/’ |
. | 匹配任意字符,除了换行符,当re.DOTALL标记被指定时,则可以匹配包括换行符的任意字符 | |
* | *前面的子表达式零次或多次,等价于{0,} | |
+ | +前面的子表达式出现一次或多次,等价于{1,} | |
? | ?前面的子表达式零次或一次,等价于{0, 1} 当该字符紧跟在任何一个其他限制符 *, +, ?, {n}, {n,}, {n,m} 后面时,匹配模式是非贪婪的。非贪婪模式尽可能少的匹配所搜索的字符串,而默认的贪婪模式则尽可能多的匹配所搜索的字符串。例如,对于字符串 "oooo","o+?" 将匹配单个 "o",而 "o+" 将匹配所有 "o" | In [33]: re.match("zo?", "zooooooabcd") # ? Out[33]: <re.Match object; span=(0, 2), match="zo"> In [34]: re.match("zo+","zooooooabcd") # + Out[34]: <re.Match object; span=(0, 7), match="zoooooo"> In [35]: re.match("zo+?","zooooooabcd") # +? Out[35]: <re.Match object; span=(0, 2), match="zo"> |
^ | 匹配字符串的开头 | |
$ | 匹配字符末尾 | In [26]: re.search(pattern="\d$",string="abc 89") Out[26]: <re.Match object; span=(5, 6), match="9"> |
{n} | n为非负整数,匹配前面表达式n次 | In [20]: re.match("o{2}","oo") Out[20]: <re.Match object; span=(0, 2), match="oo"> |
{n,} | n为非负整数,匹配前面表达式至少n次 | In [25]: re.match("o{2,}","ooooooabcd") Out[25]: <re.Match object; span=(0, 6), match="oooooo"> |
{m,n} | m、n均为整数,且0≤m≤n,最少匹配 n 次且最多匹配 m 次 | In [29]: re.match("zo{1,3}","zooooooabcd") # 最多只匹配3个o Out[29]: <re.Match object; span=(0, 4), match="zooo"> |
x|y | 匹配 x 或 y。例如,"z | food" 能匹配 "z" 或 "food"。"(z |
[xyz] | 字符集合。匹配所包含的任意一个字符。例如, "[abc]" 可以匹配 "plain" 中的 "a"。 | |
[^xyz] | 负值字符集合。匹配未包含的任意字符。例如, "[^abc]" 可以匹配 "plain" 中的"p"、"l"、"i"、"n"。 | |
[a-z] | 字符范围。匹配指定范围内的任意字符。例如,"[a-z]" 可以匹配 "a" 到 "z" 范围内的任意小写字母字符。 | |
[^a-z] | 负值字符范围。匹配任何不在指定范围内的任意字符。例如,"[^a-z]" 可以匹配任何不在 "a" 到 "z" 范围内的任意字符。 | |
\b | 匹配一个单词边界,也就是指单词和空格间的位置。例如, "er\b" 可以匹配"never" 中的 "er",但不能匹配 "verb" 中的 "er"。 | In [33]: re.search(pattern="er\b",string="verb never abc") Out[33]: <re.Match object; span=(8, 10), match="er"> |
\B | 匹配非单词边界。"er\B" 能匹配 "verb" 中的 "er",但不能匹配 "never" 中的 "er"。 | In [34]: re.search(pattern="er\B",string="verb never abc") Out[34]: <re.Match object; span=(1, 3), match="er"> |
\cx | 匹配由 x 指明的控制字符。例如, \cM 匹配一个 Control-M 或回车符。x 的值必须为 A-Z 或 a-z 之一。否则,将 c 视为一个原义的 "c" 字符。 | |
\d | 匹配一个数字字符。等价于 [0-9]。 | |
\D | 匹配一个非数字字符。等价于 [^0-9]。 | |
\f | 匹配一个换页符。等价于 \x0c 和 \cL。 | |
\n | 匹配一个换行符。等价于 \x0a 和 \cJ。 | |
\r | 匹配一个回车符。等价于 \x0d 和 \cM。 | |
\s | 匹配任何空白字符,包括空格、制表符、换页符等等。等价于 [ \f\n\r\t\v]。 | |
\S | 匹配任何非空白字符。等价于 [^ \f\n\r\t\v]。 | |
\t | 匹配一个制表符。等价于 \x09 和 \cI。 | |
\v | 匹配一个垂直制表符。等价于 \x0b 和 \cK。 | |
\w | 匹配字母、数字、下划线。等价于"[A-Za-z0-9_]"。 | |
\W | 匹配非字母、数字、下划线。等价于 "[^A-Za-z0-9_]"。 | |
\xn | 匹配 n,其中 n 为十六进制转义值。十六进制转义值必须为确定的两个数字长。例如,"\x41" 匹配 "A"。"\x041" 则等价于 "\x04" & "1"。正则表达式中可以使用 ASCII 编码。 | |
\num | 匹配 num,其中 num 是一个正整数。对所获取的匹配的引用。例如,"(.)\1" 匹配两个连续的相同字符。 | |
\n | 标识一个八进制转义值或一个向后引用。如果 \n 之前至少 n 个获取的子表达式,则 n 为向后引用。否则,如果 n 为八进制数字 (0-7),则 n 为一个八进制转义值。 | |
\nm | 标识一个八进制转义值或一个向后引用。如果 \nm 之前至少有 nm 个获得子表达式,则 nm 为向后引用。如果 \nm 之前至少有 n 个获取,则 n 为一个后跟文字 m 的向后引用。如果前面的条件都不满足,若 n 和 m 均为八进制数字 (0-7),则 \nm 将匹配八进制转义值 nm。 | |
\nml | 如果 n 为八进制数字 (0-3),且 m 和 l 均为八进制数字 (0-7),则匹配八进制转义值 nml。 | |
\un | 匹配 n,其中 n 是一个用四个十六进制数字表示的 Unicode 字符。例如, \u00A9 匹配版权符号 (?)。 |
4、GIL(Global Interpreter Lock,全局解释器锁)
- Python中一个线程对应于C语言的一个线程(这里Python是指cpython)
4.1、关于字节码 dis.dis()
- GIL(就是一把锁)使得同一时刻只有一个线程在一个CPU上执行字节码,无法将多个线程映射到多个cpu上。保证一定程度上进程的安全,但是有时候会慢(无法并发多核)。但是也有办法去GIL。
反编码:dis库,输出字节码
import dis
def add(a):
a = a+1
return a
print(dis.dis(add))
则输出字节码:
68 0 LOAD_FAST 0 (a)
2 LOAD_CONST 1 (1)
4 BINARY_ADD
6 STORE_FAST 0 (a)
69 8 LOAD_FAST 0 (a)
10 RETURN_VALUE
None
5、threading库
- 参考:
https://www.cnblogs.com/shengulong/p/10034775.html
https://www.runoob.com/python/python-multithreading.html - Python的threading库模拟Java线程模型,可以点进去看threading库:
"""Thread module emulating a subset of Java's threading model."""
- Python通过两个标准库thread和threading提供对线程的支持。thread提供了低级别的、原始的线程以及一个简单的锁。
方法 | 含义 |
---|---|
threading.currentThread() | 返回当前的线程变量。 |
threading.enumerate() | 返回一个包含正在运行的线程的list。正在运行指线程启动后、结束前,不包括启动前和终止后的线程。 |
threading.activeCount() | 返回正在运行的线程数量,与len(threading.enumerate())有相同的结果。 |
除了使用方法外,线程模块同样提供了Thread类来处理线程,Thread类提供了以下方法:
方法 | 含义 |
---|---|
run() | 用以表示线程活动的方法。 |
start() | 启动线程活动。 |
join([time]) | 等待至线程中止。这阻塞调用线程直至线程的join() 方法被调用中止-正常退出或者抛出未处理的异常-或者是可选的超时发生。 |
isAlive() | 返回线程是否活动的。 |
getName() | 返回线程名。 |
setName() | 设置线程名。 |
5.1、简单的例子:2个线程
import threading
def add(a):
return a+1
a = 0
thread1 = threading.Thread(target=add, args=[a]) # 创建线程1
thread2 = threading.Thread(target=add,args=[a]) # 创建线程2
thread1.start() # 启动线程
thread2.start()
thread1.join() # 等待线程执行完成
thread2.join()
print(a) # 输出:0
6、ftp
建议局域网的ftp:
# 该脚本程序是运行在windows上的ftp,运行前需要安装pyftpdlib模块, pip3 install pyftpdlib
# 修改filesystems.py文件,将503 行的 "utf-8"修改成"gbk"(windows支持的gbk类型的bytes)即 yield line.encode('gbk', self.cmd_channel.unicode_errors
# 修改 handlers.py 文件,将1413行的"utf-8"修改成"gbk"(windows支持的gbk类型的bytes)即 return bytes.decode('gbk', self.unicode_errors)
# https://blog.csdn.net/CN_LiTianpeng/article/details/116855645
from pyftpdlib.authorizers import DummyAuthorizer
from pyftpdlib.handlers import FTPHandler,ThrottledDTPHandler
from pyftpdlib.servers import FTPServer
from pyftpdlib.log import LogFormatter
import logging
import socket
# 函数 gethostname() 返回当前正在执行 Python 的系统主机名
res = socket.gethostbyname(socket.gethostname()) # 局域网ip
print(res)
# 1.记录日志输出到文件和终端
logger = logging.getLogger('FTP-LOG')
logger.setLevel(logging.DEBUG)
cs = logging.StreamHandler()
cs.setLevel(logging.INFO)
fs = logging.FileHandler(filename='test.log', mode='a', encoding='utf-8')
fs.setLevel(logging.DEBUG)
formatter = logging.Formatter('[%(asctime)s] %(name)s - %(levelname)s : %(message)s')
cs.setFormatter(formatter)
fs.setFormatter(formatter)
logger.addHandler(cs)
logger.addHandler(fs)
# 2.实例化虚拟用户,这是FTP的首要条件
authorizer = DummyAuthorizer()
# 3.添加用户权限和路径,括号内的参数是(用户名、密码、用户目录、权限),可以为不同的用户添加不同的目录和权限
authorizer.add_user('user', '123456', "c:/", perm="elradfmw")
# 4.添加匿名用户,只需要路径
authorizer.add_anonymous(r"E:\资料\电影")
# 5.初始化ftp句柄
handler = FTPHandler
handler.authorizer = authorizer
# 6.添加被动端口范围
handler.passive_ports = range(2000,20033)
# 7.上传下载的速度设置
dtp_handler = ThrottledDTPHandler
dtp_handler.read_limit = 3000 * 1024 # 300 kb/s
dtp_handler.write_limit = 3000 * 1024 # 300 kb/s
handler.dtp_handler = dtp_handler
# 8.监听ip和端口 , linux里需要root用户才能使用21端口
server = FTPServer((res, 22), handler)
# 9.最大连接数
server.max_cons = 150
server.max_cons_per_ip = 15
# 10.开始服务,自带打印日志信息
server.serve_forever()
7、xml
参考:
Python XML 解析:https://www.runoob.com/python/python-xml.html
Python 标准库之 xml.etree:https://www.cnblogs.com/awakenedy/articles/9305336.html
-
xml:可扩展标记语言(eXtensible Markup Language)
-
常见的 XML 编程接口有 DOM 和 SAX,这两种接口处理 XML 文件的方式不同,当然使用场合也不同。
-
Python 有三种方法解析 XML:SAX,DOM,以及 ElementTree
解析方法 | 说明 |
---|---|
SAX (simple API for XML ) | Python 标准库包含 SAX 解析器,SAX 用事件驱动模型,通过在解析XML的过程中触发一个个的事件并调用用户定义的回调函数来处理XML文件。 |
DOM(Document Object Model) | 将 XML 数据在内存中解析成一个树,通过对树的操作来操作XML。 |
ElementTree(元素树) | ElementTree就像一个轻量级的DOM,具有方便友好的API。代码可用性好,速度快,消耗内存少。 |
Element类型是一种灵活的容器对象,用于在内存中存储结构化数据,每个element对象都具有以下属性:tag、attrib、text、tail
<tag attrib1=1>text</tag>tail
7.1、导入、常用属性/函数
try:
import xml.etree.cElementTree as ET # C语言编译的API运行会更快
except ImportError:
import xml.etree.ElementTree as ET
7.2、节点属性、读取、遍历
- 常用属性/函数(使用代码见后面):
节点属性 | tag:string | 元素代表的数据种类。 |
---|---|---|
text:string | 元素的内容。 | |
tail:string | 元素的尾形。 | |
attrib:dictionary | 元素的属性字典。 | |
针对属性的操作 | clear() | 清空元素的后代、属性、text和tail也设置为None。 |
get(key, default=None) | 获取key对应的属性值,如该属性不存在则返回default值。 | |
items() | 根据属性字典返回一个列表,列表元素为(key, value)。 | |
keys() | 返回包含所有元素属性键的列表。 | |
set(key, value) | 设置新的属性键与值 | |
针对后代的操作 | append(subelement) | 添加直系子元素。 |
extend(subelements) | 增加一串元素对象作为子元素。#python2.7新特性 | |
find(match) | 寻找第一个匹配子元素,匹配对象可以为tag或path。 | |
findall(match) | 寻找所有匹配子元素,匹配对象可以为tag或path。 | |
findtext(match) | 寻找第一个匹配子元素,返回其text值。匹配对象可以为tag或path。 | |
insert(index, element) | 在指定位置插入子元素。 | |
iter(tag=None) | 生成遍历当前元素所有后代或者给定tag的后代的迭代器。#python2.7新特性 | |
iterfind(match) | 根据tag或path查找所有的后代。 | |
itertext() | 遍历所有后代并返回text值。 | |
remove(subelement) | 删除子元素。 |
import xml.etree.cElementTree as ET
content = """
<classA>
<!--这是注释-->
<className name="myClass" direction="we win!"/>
<student name="XiaoLi" age="21">
<Chinese total="150" date="Monday">120</Chinese>
<Math total="150" date="Tuesday">149</Math>
<English total="150" date="Wednesday">129</English>
</student>abcdehhh
<student name="XiaoHong" age="19">
<Chinese total="150" date="Monday">130</Chinese>
<Math total="150" date="Tuesday">100</Math>
<English total="150" date="Wednesday">139</English>
</student>
<student name="CiCi" age="20">
<Chinese total="150" date="Monday">110</Chinese>
<Math total="150" date="Tuesday">80</Math>
<English total="150" date="Wednesday">140</English>
</student>
</classA>
"""
# 从xml文件中载入数据
tree = ET.parse(r"C:\Users\ZHUIAO\Desktop\test.xml")
root = tree.getroot()
# 解析字符串xml,也可以ET.XML(content)
root = ET.fromstring(content)
print("\n遍历节点:") # 遍历节点,获取tag和attribute
for child in root:
# <tag attrib1=1>text</tag>tail 对于student节点,text就是 '\n '
print("tag:{}\tattrib:{}\ttext:{}\ttail:{}".
format(child.tag, child.attrib, child.text, child.tail))
print("\n索引获取节点:root[1][1]=", root[1][1], "成绩:", root[1][1].text)
print("\nxpath方式 findall()进行遍历:{}".format(root.findall("./student/Math")))
print("\n处理xml流")
# 处理巨型XML,例如1GB的XML,读取完后 clear 清除内存占用
# ET.iterparse() 参考:https://blog.csdn.net/pirate945/article/details/86685598
# events选项指定您要查看的事件(此版本中的可用事件是“start”,“end”,“start-ns”和“end-ns”,其中“ns”事件用于获取详细的命名空间 信息)。 缺省值为“end”。
studentNum = 0
for event, elem in ET.iterparse(r"C:\Users\ZHUIAO\Desktop\test.xml", events=('start', 'end', "end-ns")):
if event == "end" and elem.tag == 'student':
print(event, elem)
studentNum += 1
elem.clear() # 释放内存
# 输出:
遍历节点:
tag:className attrib:{'name': 'myClass', 'direction': 'we win!'} text:None tail:
tag:student attrib:{'name': 'XiaoLi', 'age': '21'} text:
tail:abcdehhh
tag:student attrib:{'name': 'XiaoHong', 'age': '19'} text:
tail:
tag:student attrib:{'name': 'CiCi', 'age': '20'} text:
tail:
索引获取节点:root[1][1]= <Element 'Math' at 0x0000026BAF39AA90> 成绩: 149
xpath方式 findall()进行遍历:[<Element 'Math' at 0x0000026BAF39AA90>, <Element 'Math' at 0x0000026BAF39AC70>, <Element 'Math' at 0x0000026BAF3ADB80>]
处理xml流
end <Element 'student' at 0x0000026BAF3ADA90>
end <Element 'student' at 0x0000026BAF3AD4A0>
end <Element 'student' at 0x0000026BAF3AD040>
import xml.etree.cElementTree as ET
content = """
<classA>
<!--这是注释-->
<className name="myClass" direction="we win!"/>
<student name="XiaoLi" age="21">
<Chinese total="150" date="Monday">120</Chinese>myTail
<Math total="150" date="Tuesday">149</Math>
<English total="150" date="Wednesday">129</English>
</student>
</classA>
"""
# 解析字符串xml,也可以ET.XML(content)
root = ET.fromstring(content)
print("clear():\t清空元素的后代,属性、text和tail也设置为None:\n清空前:\t",
ET.tostringlist(root[1][0], encoding="utf-8", method="xml"))
root[1][0].clear()
print("清空后:\t", ET.tostringlist(root[1][0]))
print("\nget(key, default=None):获取key对应的属性值,不存在则返回default:\t", end="")
print(root[1].get("name", default="没找到节点属性")) # 输出:XiaoLi
print("\n属性键值对:\t", root[1].items()) # 属性列表: [('name', 'XiaoLi'), ('age', '21')]
print("\n属性键列表:\t", root[1].keys()) # 属性键列表: ['name', 'age']
root[1].set("newKey", "newValue")
print("\nset(key, value):\t设置新/原有属性:\t", root[1].items())
# 输出:
clear(): 清空元素的后代,属性、text和tail也设置为None:
清空前: [b'<Chinese total="150" date="Monday">120</Chinese>myTail\n ']
清空后: [b'<Chinese />']
get(key, default=None):获取key对应的属性值,不存在则返回default: XiaoLi
属性键值对: [('name', 'XiaoLi'), ('age', '21')]
属性键列表: ['name', 'age']
set(key, value): 设置新/原有属性: [('name', 'XiaoLi'), ('age', '21'), ('newKey', 'newValue')]