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的桌面程序都能看到,可以爬~

0 个评论

要回复文章请先登录注册


官方客服QQ群

微信人工客服

QQ人工客服


线