UI自动化工具轻松实现微信消息的自动收发和朋友圈爬取
优采云 发布时间: 2021-08-26 18:08
UI自动化工具轻松实现微信消息的自动收发和朋友圈爬取
大家好,我是小明。昨天我在《UI自动化工具轻松自动收发微信消息和爬取好友时刻》一文中演示了三个UIAutomation的使用实例,链接:
由于昨天对UIAutomation的API理解不够全面,个别代码优化还有很大空间。今天我们的目标是实现微信PC版联系人信息列表的抓取,这将分别使用PyWinAuto和uiautomation来实现。通过对比,大家会有更深入的了解。
PyWinAuto 实现
PyWinAuto 官方文档地址:
PyWinAuto 连接桌面程序主要有两种方式:
分别是进程的pid和窗口句柄,下面我将分别演示如何获取微信的进程id和窗口句柄。
根据进程名获取进程ID:
import psutil
def get_pid(proc_name):
for proc in psutil.process_iter(attrs=['pid', 'name']):
if proc.name() == proc_name:
return proc.pid
%time get_pid("WeChat.exe")
Wall time: 224 ms
7268
根据窗口标题和类名查找窗口句柄:
import win32gui
%time hwnd = win32gui.FindWindow("WeChatMainWndForPC", "微信")
hwnd
Wall time: 0 ns
264610
耗时几乎为零,比之前的方法快了 100 多倍。
所以我用窗口句柄连接微信窗口:
import win32gui
from pywinauto.application import Application
hwnd = win32gui.FindWindow("WeChatMainWndForPC", "微信")
app = Application(backend='uia').connect(handle=hwnd)
app
自动打开*敏*感*词*:
import pywinauto
win = app['微信']
txl_btn = win.child_window(title="*敏*感*词*", control_type="Button")
txl_btn.draw_outline()
cords = txl_btn.rectangle()
pywinauto.mouse.click(button='left', coords=(
(cords.right+cords.left)//2, (cords.top+cords.bottom)//2))
随机点击好友信息详情后,通过inspect.exe查看节点信息。
然后编写如下代码,根据分析结果运行:
可以看到各种信息提取的很流畅,但是需要3.54秒,不一定比手动复制粘贴快。这也是pywinauto的一个缺点,太慢了。
接下来,我们执行批量抓取。原理大致是每次阅读信息面板时,按向下箭头键。如果发现当前读取的数据与之前的一致,则认为已经爬到了最后。
由于pywinauto的爬取速度太慢,我手动将好友列表拖到最后,然后运行如下代码:
import pandas as pd
win = app['微信']
contacts = win.child_window(title="联系人", control_type="List")
# 点击第二个可见元素
contacts.children()[1].click_input()
result = []
last = None
num = 0
while 1:
tag = win.Edit2
tmp = tag.parent().parent()
nickname = tag.get_value()
# 跳过两个官方号
if nickname in ["微信团队", "文件传输助手"]:
contacts.type_keys("{DOWN}")
continue
detail = tmp.children()[-1]
whats_up = ""
if hasattr(detail, 'get_value'):
whats_up = detail.get_value()
elif hasattr(detail, 'window_text') and detail.window_text() != "":
# 这种情况说明是企业微信,跳过
contacts.type_keys("{DOWN}")
continue
items = tmp.parent().parent().children()[2].children()
row = {"昵称": nickname, "个性签名": whats_up}
for item in items:
lines = item.children()
k = lines[0].window_text()
v = lines[1].window_text()
row[k.replace(" ", "")] = v
if row == last:
# 与上一条数据一致则说明已经爬取完毕
break
result.append(row)
num += 1
print("\r", num, row,
end=" ")
last = row
contacts.type_keys("{DOWN}")
df = pd.DataFrame(result)
df
最终结果:
可以看到最后一页11个微信账号的数据抓取耗时45秒。
ui自动化实现
接下来,我们将使用 uiautomation 来实现这种爬取。
获取微信窗口,点击*敏*感*词*按钮:
import uiautomation as auto
wechatWindow = auto.WindowControl(searchDepth=1, Name="微信", ClassName='WeChatMainWndForPC')
wechatWindow.SetActive()
txl_btn = wechatWindow.ButtonControl(Name='*敏*感*词*')
txl_btn.Click()
然后点击云朵君的好友信息,测试好友信息抽取:
(这个数字,你可以随意添加)
wechatWindow.EditControl(searchDepth=10, foundIndex=2)表示在10层节点内搜索第二个EditControl类型节点(第一个是搜索框,第二个是朋友的昵称)。
GetParentControl() 和 GetNextSiblingControl() 是昨天未使用的 API。它们用于获取父节点和下一个兄弟节点。使用这两个 API 重写昨天的文章 代码,会使得程序代码变得简单,性能也有所提升。
然后用与PyWinAuto相同的方式进行批量提取:
还要先测试最后一页的数据:
只需 11 秒即可抓取,比 PyWinAuto 快 4 倍。
这样我们就可以批量提取所有微信好友的数据了。最后,我这边的700多个朋友用了10分钟。虽然速度较慢,但与 PyWinAuto 相比完全可以接受。
代码对比
对于两者,我都试图遵循完全相同的逻辑。
win.Edit2也是获取第二个EditControl类型节点,
type_keys 是 PyWinAuto 用来模拟击键的 API,{DOWN} 代表向下箭头键。
PyWinAuto 获取的是小写的父子节点的api,没有get,而uiautomation 获取的是大写的父子节点的api,前缀为Get。
对于 PyWinAuto 中的这行代码:
items = tmp.parent().parent().children()[2].children()
使用 uiautomation:
items = tmp.GetParentControl().GetNextSiblingControl().GetNextSiblingControl().GetChildren()
有很大的不同。
这是因为我没有找到PyWinAuto获取兄弟节点的API,所以采用了先获取父节点再获取子节点的方法。
另外,判断是否是企业微信的逻辑不同。 PyWinAuto也需要在个性签名为空时处理异常,否则程序会报错退出。具体点需要大家去测试和体验。
其他地方的逻辑几乎相同。
总结
本文还演示了 PyWinAuto 和 uiautomation 读取微信好友列表信息。通过对比,我们可以更深入的了解两者的API用法。作为作者,我在实践中有着深刻的理解。只是文章中的代码并没有体现这些细节,具体的事情需要读者在分析对比的过程中得到答案。仅仅看本文的代码或许可以解决当前的需求,但是很难将本文所涉及的技术应用到其他需求上。
童鞋们,赶紧动手实践学习吧⁉️学习后,任何实现Automation Provider的桌面程序都能看到,可以爬~