[关闭]
@coder-pig 2018-04-13T07:49:13.000000Z 字数 9973 阅读 2247

自动抢红包,点赞朋友圈,AccessibilityService解放你的双手

2018


引言

其实这一篇本来不是写这个的,在周一开始想做的时候就想好了标题:
《Android与Python的巧妙结合抓取公众号所有历史文章》
搜狗仅显示最近10条群发,想做爬公号历史文章的应该都了解过,
而利用无障碍服务+Python实现的思路很简单:

之所以要执行第八步的原因是保存到本地的HTML直接打开很多图片都会裂开(链接失效)
然后呢,我花了三天都没折腾出来,最终卡在无法获得微信x5浏览器网页节点 这个节骨眼
上,无法再进分毫...

无障碍服务利用的是UI Automation,然后呢,这玩意是不支持WebView的!!!、
尽管能遍历打印出结点的信息,但是模拟点击一点用都没有...
webview里的东西,AccessibilityService就无能为力了。我...

后来群里童鞋说移动端自动化测试框架Appuim支持webview,我立马花了半天
去撸文档(不得不说相比起AccessibilityService只能依靠res-id和text来查找
节点,appium支持xpath等方式,找节点,不能爽太多,不过缺点的话是
反应会慢一些,特别是在edittext输入的时候),但是这里有个坎又把我给卡住了
appuim每次运行自己的脚本,微信都需要重新登录,每次运行都要登录的结果
就是:我的三个小号因为登录过于频繁,当天都无法登录了...
(有知道如何规避每次运行脚本都要重新的登录的务必告知下~)
放弃了,太耗费时间了!

另外还有一个套路是微信利用的x5浏览器,可以在微信里打开这个链接:
debugx5.qq.com
接着选择信息,勾选:

接着打开谷歌浏览器,输入:chrome://inspect/#devices
接着随手打开一篇文章,就会有这样的东西:

点击inspect,然后可以得到整个页面的结构,可以拿到和
搜狗获取的一样类型的文章链接,链接很长,而且失效都是12小时

如果是手机用户点击查看文章,然后复制链接,是这样的短连接,
时效还不知道是多久(Maybe永久):
http://mp.weixin.qq.com/s/O00w469tkOrr507drGln9A

关于如何获得微信公众号历史文章目前知道的比较简单的套路就这些,
至于抓包破解之类的,目前level还不够,折腾不过来,先放一放,
后面说不定get√了新的姿势点,问题一下子就解了呢?嘿嘿~

另外爬取微信文章里图片,音频视频可见之前写过的:
小猪的Python学习之旅 —— 10.三分钟上手Requests库

上节利用AccessibilityService实现了自动加好友和拉人进群聊,
感觉还没用起来,这不,又加了自动抢红包和朋友圈自动点赞~

另外,在AccessibilityService除了Webview这个坑外,通过red-id
查找控件,在每个版本的APP中都可能是不一样的,比如微信就是,
本节代码仅保证在6.6.6版本上是可用的,其他版本需要你自行去
适配啦!另外,项目是用Kotlin写的哈,顺道复习一波语法~


1.自动抢红包

这个肯定是没有xposed抢得快的,就是监听Notification信息,如果有
微信红包的字眼,自动打开聊天页面,遍历列表,找到未打开的红包,
自动点开,领取后自动关闭。

运行效果图

啧啧,三个红包几下子就抢完了,速度还是杠杠的,了解了原理,以后换其他
应用,比如钉钉之类的,另外这个脚本目前只支持抢页面可见的红包,
可以开启listview滚动监听,滑动的时候去判断页面有无红包,然后抢。
不过监听了这个玩意的话,页面会挺卡的,不建议开启,自己点红包,
直接就能开了~


2.朋友圈自动点赞

这个功能就不说啦,及时点赞,拉近同事朋友感情,每天点赞那么勤,
别人还以为你对她有意思呢?啧啧,有兴趣的还可以自行扩展,对别
人的说说内容进行下过滤,根据不同的内容发送不同的评论~
比如:

附:

PS:说想接入机器人自动回复评论的,只利用AccessibilityService的话就
别想了,你们怕是忘了AccessibilityService是用来优化残障人士的使用体验的吧...
如果要接机器人的话,考虑appuim自动化测试吧~

运行效果图

是的,就是这么屌~


小结

本节开始讲了下利用类似于按键精灵的套路获得公众号所有历史文章的套路,
接着写了两个很实用的实例,抢红包和朋友圈点赞,关于AccessibilityService
的东东基本也就那么多了,后续再折腾这些就该上xposed了~


附:关键代码(都可以在:https://github.com/coder-pig/WechatHelper 找到):

  1. class HelperService : AccessibilityService() {
  2. private val TAG = "HelperService"
  3. private val handler = Handler()
  4. private var nameList = mutableListOf<String>()
  5. override fun onInterrupt() {
  6. }
  7. override fun onAccessibilityEvent(event: AccessibilityEvent) {
  8. val eventType = event.eventType
  9. val classNameChr = event.className
  10. val className = classNameChr.toString()
  11. Log.d(TAG, event.toString())
  12. when (eventType) {
  13. AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED -> {
  14. if (Hawk.get(Constant.RED_PACKET, false)) {
  15. when (className) {
  16. "com.tencent.mm.ui.LauncherUI" -> openRedPacket()
  17. "com.tencent.mm.plugin.luckymoney.ui.LuckyMoneyReceiveUI" -> clickRedPacket()
  18. "com.tencent.mm.plugin.luckymoney.ui.LuckyMoneyDetailUI" -> performBackClick()
  19. }
  20. //com.tencent.mm:id/ad8
  21. }
  22. if (Hawk.get(Constant.ADD_FRIENDS, false) && Hawk.get(Constant.GROUP_NAME, "") != "") {
  23. when (className) {
  24. "com.tencent.mm.plugin.subapp.ui.friend.FMessageConversationUI" -> addFriends()
  25. "com.tencent.mm.plugin.profile.ui.SayHiWithSnsPermissionUI" -> verifyFriend()
  26. "com.tencent.mm.plugin.profile.ui.ContactInfoUI" -> contactInfo()
  27. "com.tencent.mm.ui.LauncherUI" -> openGroup()
  28. "com.tencent.mm.ui.contact.ChatroomContactUI" -> {
  29. if (nameList.size > 0) searchGroup() else performBackClick()
  30. }
  31. "com.tencent.mm.ui.chatting.ChattingUI" -> openGroupSetting()
  32. "com.tencent.mm.plugin.chatroom.ui.ChatroomInfoUI" -> {
  33. if (nameList.size > 0) addToGroup() else performBackClick()
  34. }
  35. "com.tencent.mm.ui.base.i" -> dialogClick()
  36. }
  37. }
  38. if (Hawk.get(Constant.FRIEND_SQUARE,false)) {
  39. if (className == "com.tencent.mm.plugin.sns.ui.SnsTimeLineUI") {
  40. autoZan()
  41. }
  42. }
  43. }
  44. AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED -> {
  45. if (event.parcelableData != null && event.parcelableData is Notification) {
  46. val notification = event.parcelableData as Notification
  47. val content = notification.tickerText.toString()
  48. if (content.contains("[微信红包]")) {
  49. val pendingIntent = notification.contentIntent
  50. try {
  51. pendingIntent.send()
  52. } catch (e: PendingIntent.CanceledException) {
  53. e.printStackTrace()
  54. }
  55. }
  56. }
  57. }
  58. //滚动的时候也去监听红包,不过有点卡
  59. // AccessibilityEvent.TYPE_VIEW_SCROLLED -> {
  60. // if (className == "android.widget.ListView") {
  61. // openRedPacket()
  62. // }
  63. // }
  64. }
  65. }
  66. //添加好友
  67. private fun addFriends() {
  68. val nodeInfo = rootInActiveWindow
  69. if (nodeInfo != null) {
  70. val list = nodeInfo.findAccessibilityNodeInfosByText("接受")
  71. if (list != null && list.size > 0) {
  72. list[0].performAction(AccessibilityNodeInfo.ACTION_CLICK)
  73. val nameText: List<AccessibilityNodeInfo>? = list[0].parent.findAccessibilityNodeInfosByViewId("com.tencent.mm:id/b8s")
  74. nameList.add(nameText?.get(0)?.text.toString())
  75. } else {
  76. performBackClick()
  77. }
  78. }
  79. }
  80. //完成验证
  81. private fun verifyFriend() {
  82. val nodeInfo = rootInActiveWindow
  83. if (nodeInfo != null) {
  84. val finishNode = nodeInfo.findAccessibilityNodeInfosByViewId("com.tencent.mm:id/hh")[0]
  85. finishNode.performAction(AccessibilityNodeInfo.ACTION_CLICK)
  86. }
  87. }
  88. //好友详细资料页
  89. private fun contactInfo() {
  90. val nodeInfo = rootInActiveWindow
  91. if (nodeInfo != null) {
  92. val nameNode = nodeInfo.findAccessibilityNodeInfosByViewId("com.tencent.mm:id/q0")[0]
  93. Log.i(TAG, nameNode.toString())
  94. if (nameList.contains(nameNode.text.toString().trim())) performBackClick()
  95. }
  96. }
  97. //打开群聊
  98. private fun openGroup() {
  99. if (nameList.size > 0) {
  100. val nodeInfo = rootInActiveWindow
  101. if (nodeInfo != null) {
  102. val tabNodes = nodeInfo.findAccessibilityNodeInfosByViewId("com.tencent.mm:id/c9f")
  103. for (tabNode in tabNodes) {
  104. if (tabNode.text.toString() == "通讯录") {
  105. tabNode.parent.performAction(AccessibilityNodeInfo.ACTION_CLICK)
  106. handler.postDelayed({
  107. val newNodeInfo = rootInActiveWindow
  108. if (newNodeInfo != null) {
  109. val tagNodes = newNodeInfo.findAccessibilityNodeInfosByViewId("com.tencent.mm:id/jk")
  110. for (tagNode in tagNodes) {
  111. if (tagNode.text.toString() == "群聊") {
  112. tagNode.parent.parent.performAction(AccessibilityNodeInfo.ACTION_CLICK)
  113. break
  114. }
  115. }
  116. }
  117. }, 500L)
  118. }
  119. }
  120. }
  121. }
  122. }
  123. //搜索群聊
  124. private fun searchGroup() {
  125. val nodeInfo = rootInActiveWindow
  126. if (nodeInfo != null) {
  127. val nodes = nodeInfo.findAccessibilityNodeInfosByViewId("com.tencent.mm:id/a9t")
  128. for (info in nodes) {
  129. if (info.text.toString() == Hawk.get(Constant.GROUP_NAME)) {
  130. info.parent.performAction(AccessibilityNodeInfo.ACTION_CLICK)
  131. break
  132. }
  133. }
  134. }
  135. }
  136. //打开群聊设置
  137. private fun openGroupSetting() {
  138. if (nameList.size > 0) {
  139. val nodeInfo = rootInActiveWindow
  140. if (nodeInfo != null) {
  141. nodeInfo.findAccessibilityNodeInfosByViewId("com.tencent.mm:id/hi")[0].performAction(AccessibilityNodeInfo.ACTION_CLICK)
  142. }
  143. }
  144. }
  145. //添加到群聊里
  146. private fun addToGroup() {
  147. if (nameList.size > 0) {
  148. val nodeInfo = rootInActiveWindow
  149. if (nodeInfo != null) {
  150. val listNodes = nodeInfo.findAccessibilityNodeInfosByViewId("android:id/list")
  151. if (listNodes != null && listNodes.size > 0) {
  152. val listNode = listNodes[0]
  153. listNode.performAction(AccessibilityNodeInfo.ACTION_SCROLL_FORWARD)
  154. listNode.performAction(AccessibilityNodeInfo.ACTION_SCROLL_FORWARD)
  155. val scrollNodeInfo = rootInActiveWindow
  156. if (scrollNodeInfo != null) {
  157. handler.postDelayed({
  158. val nodes = scrollNodeInfo.findAccessibilityNodeInfosByViewId("com.tencent.mm:id/cz1")
  159. for (info in nodes) {
  160. if (info.contentDescription.toString() == "添加成员") {
  161. info.parent.performAction(AccessibilityNodeInfo.ACTION_CLICK)
  162. break
  163. }
  164. }
  165. }, 1000L)
  166. handler.postDelayed({
  167. val editNodes = rootInActiveWindow.findAccessibilityNodeInfosByViewId("com.tencent.mm:id/arx")
  168. if (editNodes != null && editNodes.size > 0) {
  169. val editNode = editNodes[0]
  170. val arguments = Bundle()
  171. arguments.putCharSequence(AccessibilityNodeInfo.ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE, nameList[0])
  172. editNode.performAction(AccessibilityNodeInfo.ACTION_SET_TEXT, arguments)
  173. nameList.removeAt(0)
  174. }
  175. }, 2300L)
  176. handler.postDelayed({
  177. val cbNodes = rootInActiveWindow.findAccessibilityNodeInfosByViewId("com.tencent.mm:id/l7")
  178. if (cbNodes != null) {
  179. var cbNode: AccessibilityNodeInfo? = null
  180. if (cbNodes.size == 1) {
  181. cbNode = cbNodes[0]
  182. } else if (cbNodes.size == 2) {
  183. cbNode = cbNodes[1]
  184. }
  185. if (cbNode != null) {
  186. cbNode.parent.performAction(AccessibilityNodeInfo.ACTION_CLICK)
  187. val sureNode = rootInActiveWindow.findAccessibilityNodeInfosByViewId("com.tencent.mm:id/hh")[0]
  188. sureNode.performAction(AccessibilityNodeInfo.ACTION_CLICK)
  189. }
  190. }
  191. }, 3000L)
  192. }
  193. }
  194. }
  195. }
  196. }
  197. //对话框处理
  198. private fun dialogClick() {
  199. val inviteNode = rootInActiveWindow.findAccessibilityNodeInfosByViewId("com.tencent.mm:id/all")[0]
  200. inviteNode.performAction(AccessibilityNodeInfo.ACTION_CLICK)
  201. handler.postDelayed({
  202. val sureNodes = rootInActiveWindow.findAccessibilityNodeInfosByViewId("com.tencent.mm:id/all")
  203. if (sureNodes != null && sureNodes.size > 0) {
  204. val sureNode = sureNodes[0]
  205. sureNode.performAction(AccessibilityNodeInfo.ACTION_CLICK)
  206. }
  207. }, 1000L)
  208. }
  209. //自动点赞
  210. private fun autoZan() {
  211. val nodeInfo = rootInActiveWindow
  212. if (nodeInfo != null) {
  213. while (true) {
  214. val rootNode = rootInActiveWindow
  215. if (rootNode != null) {
  216. val listNodes = rootNode.findAccessibilityNodeInfosByViewId("com.tencent.mm:id/ddn")
  217. if (listNodes != null && listNodes.size > 0) {
  218. val listNode = listNodes[0]
  219. val zanNodes = listNode.findAccessibilityNodeInfosByViewId("com.tencent.mm:id/dao")
  220. for (zan in zanNodes) {
  221. zan.performAction(AccessibilityNodeInfo.ACTION_CLICK)
  222. Thread.sleep(300)
  223. val zsNodes = rootInActiveWindow.findAccessibilityNodeInfosByViewId("com.tencent.mm:id/d_m")
  224. Thread.sleep(300)
  225. if (zsNodes != null && zsNodes.size > 0) {
  226. if (zsNodes[0].findAccessibilityNodeInfosByText("赞").size > 0) {
  227. zsNodes[0].performAction(AccessibilityNodeInfo.ACTION_CLICK)
  228. }
  229. }
  230. Thread.sleep(300)
  231. }
  232. listNode.performAction(AccessibilityNodeInfo.ACTION_SCROLL_FORWARD)
  233. }
  234. } else {
  235. break
  236. }
  237. }
  238. }
  239. }
  240. //遍历获得未打开红包
  241. private fun openRedPacket() {
  242. val rootNode = rootInActiveWindow
  243. if(rootNode != null) {
  244. val listNode = rootNode.findAccessibilityNodeInfosByViewId("com.tencent.mm:id/a_c")
  245. if (listNode != null && listNode.size > 0) {
  246. val msgNodes = listNode[0].findAccessibilityNodeInfosByViewId("com.tencent.mm:id/ad8")
  247. if (msgNodes != null && msgNodes.size > 0) {
  248. for(rpNode in msgNodes) {
  249. val rpStatusNode = rpNode.findAccessibilityNodeInfosByText("领取红包")
  250. if (rpStatusNode != null && rpStatusNode.size > 0) {
  251. rpNode.performAction(AccessibilityNodeInfo.ACTION_CLICK)
  252. break
  253. }
  254. }
  255. }
  256. }
  257. }
  258. }
  259. //打开红包
  260. private fun clickRedPacket() {
  261. val nodeInfo = rootInActiveWindow
  262. val clickNode = nodeInfo.findAccessibilityNodeInfosByViewId("com.tencent.mm:id/c31")
  263. if (clickNode != null && clickNode.size > 0) {
  264. clickNode[0].performAction(AccessibilityNodeInfo.ACTION_CLICK)
  265. } else {
  266. performBackClick()
  267. }
  268. }
  269. private fun performBackClick() {
  270. handler.postDelayed({ performGlobalAction(AccessibilityService.GLOBAL_ACTION_BACK) }, 300L)
  271. }
  272. }

来啊,Py交易啊

想加群一起学习Py的可以加下,智障机器人小Pig

验证通过后会自动发送群聊链接加群链接,点击加入即可
(不要和机器人聊天=-=,就挂着拉人的,有问题到群里讲!)

欢迎各种像我一样的Py初学者,Py大神加入,一起愉快地交流学♂习,van♂转py。


添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注