引言:
其實這一篇本來不是寫這個的,在週一開始想做的時候就想好了標題:
《Android與Python的巧妙結合抓取公眾號所有歷史文章》,
搜狗僅顯示最近10條群發,想做爬公號歷史文章的應該都瞭解過,
而且文章也越來越不好抓了,而利用無障礙服務+Python實現的思路很簡單:
- 1.寫一個無障礙服務,在輸入完公眾號名字儲存後,跳轉到公眾號歷史資訊
- 2.然後獲取每條記錄,點選進入,右上角複製連結,儲存起來
- 3.當前頁面的獲取完,滾動載入更多,繼續獲取
- 4.直到獲取到已無更多的節點,代表獲取完畢
- 5.把這個存有所有連結的檔案傳送到PC電腦
- 6.然後多執行緒+代理ip訪問連結,新建一個文章對應的資料夾,儲存為HTML檔案
- 7.利用以前寫過的抓取微信文章裡圖片,音訊,視訊的指令碼把資料爬到本地
- 8.替換HTML對應資源處的href連結,指向本地資源
之所以要執行第八步的原因是儲存到本地的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了~
附:關鍵程式碼(都可以在:github.com/coder-pig/W… 找到):
class HelperService : AccessibilityService() {
private val TAG = "HelperService"
private val handler = Handler()
private var nameList = mutableListOf<String>()
override fun onInterrupt() {
}
override fun onAccessibilityEvent(event: AccessibilityEvent) {
val eventType = event.eventType
val classNameChr = event.className
val className = classNameChr.toString()
Log.d(TAG, event.toString())
when (eventType) {
AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED -> {
if (Hawk.get(Constant.RED_PACKET, false)) {
when (className) {
"com.tencent.mm.ui.LauncherUI" -> openRedPacket()
"com.tencent.mm.plugin.luckymoney.ui.LuckyMoneyReceiveUI" -> clickRedPacket()
"com.tencent.mm.plugin.luckymoney.ui.LuckyMoneyDetailUI" -> performBackClick()
}
//com.tencent.mm:id/ad8
}
if (Hawk.get(Constant.ADD_FRIENDS, false) && Hawk.get(Constant.GROUP_NAME, "") != "") {
when (className) {
"com.tencent.mm.plugin.subapp.ui.friend.FMessageConversationUI" -> addFriends()
"com.tencent.mm.plugin.profile.ui.SayHiWithSnsPermissionUI" -> verifyFriend()
"com.tencent.mm.plugin.profile.ui.ContactInfoUI" -> contactInfo()
"com.tencent.mm.ui.LauncherUI" -> openGroup()
"com.tencent.mm.ui.contact.ChatroomContactUI" -> {
if (nameList.size > 0) searchGroup() else performBackClick()
}
"com.tencent.mm.ui.chatting.ChattingUI" -> openGroupSetting()
"com.tencent.mm.plugin.chatroom.ui.ChatroomInfoUI" -> {
if (nameList.size > 0) addToGroup() else performBackClick()
}
"com.tencent.mm.ui.base.i" -> dialogClick()
}
}
if (Hawk.get(Constant.FRIEND_SQUARE,false)) {
if (className == "com.tencent.mm.plugin.sns.ui.SnsTimeLineUI") {
autoZan()
}
}
}
AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED -> {
if (event.parcelableData != null && event.parcelableData is Notification) {
val notification = event.parcelableData as Notification
val content = notification.tickerText.toString()
if (content.contains("[微信紅包]")) {
val pendingIntent = notification.contentIntent
try {
pendingIntent.send()
} catch (e: PendingIntent.CanceledException) {
e.printStackTrace()
}
}
}
}
//滾動的時候也去監聽紅包,不過有點卡
// AccessibilityEvent.TYPE_VIEW_SCROLLED -> {
// if (className == "android.widget.ListView") {
// openRedPacket()
// }
// }
}
}
//新增好友
private fun addFriends() {
val nodeInfo = rootInActiveWindow
if (nodeInfo != null) {
val list = nodeInfo.findAccessibilityNodeInfosByText("接受")
if (list != null && list.size > 0) {
list[0].performAction(AccessibilityNodeInfo.ACTION_CLICK)
val nameText: List<AccessibilityNodeInfo>? = list[0].parent.findAccessibilityNodeInfosByViewId("com.tencent.mm:id/b8s")
nameList.add(nameText?.get(0)?.text.toString())
} else {
performBackClick()
}
}
}
//完成驗證
private fun verifyFriend() {
val nodeInfo = rootInActiveWindow
if (nodeInfo != null) {
val finishNode = nodeInfo.findAccessibilityNodeInfosByViewId("com.tencent.mm:id/hh")[0]
finishNode.performAction(AccessibilityNodeInfo.ACTION_CLICK)
}
}
//好友詳細資料頁
private fun contactInfo() {
val nodeInfo = rootInActiveWindow
if (nodeInfo != null) {
val nameNode = nodeInfo.findAccessibilityNodeInfosByViewId("com.tencent.mm:id/q0")[0]
Log.i(TAG, nameNode.toString())
if (nameList.contains(nameNode.text.toString().trim())) performBackClick()
}
}
//開啟群聊
private fun openGroup() {
if (nameList.size > 0) {
val nodeInfo = rootInActiveWindow
if (nodeInfo != null) {
val tabNodes = nodeInfo.findAccessibilityNodeInfosByViewId("com.tencent.mm:id/c9f")
for (tabNode in tabNodes) {
if (tabNode.text.toString() == "通訊錄") {
tabNode.parent.performAction(AccessibilityNodeInfo.ACTION_CLICK)
handler.postDelayed({
val newNodeInfo = rootInActiveWindow
if (newNodeInfo != null) {
val tagNodes = newNodeInfo.findAccessibilityNodeInfosByViewId("com.tencent.mm:id/jk")
for (tagNode in tagNodes) {
if (tagNode.text.toString() == "群聊") {
tagNode.parent.parent.performAction(AccessibilityNodeInfo.ACTION_CLICK)
break
}
}
}
}, 500L)
}
}
}
}
}
//搜尋群聊
private fun searchGroup() {
val nodeInfo = rootInActiveWindow
if (nodeInfo != null) {
val nodes = nodeInfo.findAccessibilityNodeInfosByViewId("com.tencent.mm:id/a9t")
for (info in nodes) {
if (info.text.toString() == Hawk.get(Constant.GROUP_NAME)) {
info.parent.performAction(AccessibilityNodeInfo.ACTION_CLICK)
break
}
}
}
}
//開啟群聊設定
private fun openGroupSetting() {
if (nameList.size > 0) {
val nodeInfo = rootInActiveWindow
if (nodeInfo != null) {
nodeInfo.findAccessibilityNodeInfosByViewId("com.tencent.mm:id/hi")[0].performAction(AccessibilityNodeInfo.ACTION_CLICK)
}
}
}
//新增到群聊裡
private fun addToGroup() {
if (nameList.size > 0) {
val nodeInfo = rootInActiveWindow
if (nodeInfo != null) {
val listNodes = nodeInfo.findAccessibilityNodeInfosByViewId("android:id/list")
if (listNodes != null && listNodes.size > 0) {
val listNode = listNodes[0]
listNode.performAction(AccessibilityNodeInfo.ACTION_SCROLL_FORWARD)
listNode.performAction(AccessibilityNodeInfo.ACTION_SCROLL_FORWARD)
val scrollNodeInfo = rootInActiveWindow
if (scrollNodeInfo != null) {
handler.postDelayed({
val nodes = scrollNodeInfo.findAccessibilityNodeInfosByViewId("com.tencent.mm:id/cz1")
for (info in nodes) {
if (info.contentDescription.toString() == "新增成員") {
info.parent.performAction(AccessibilityNodeInfo.ACTION_CLICK)
break
}
}
}, 1000L)
handler.postDelayed({
val editNodes = rootInActiveWindow.findAccessibilityNodeInfosByViewId("com.tencent.mm:id/arx")
if (editNodes != null && editNodes.size > 0) {
val editNode = editNodes[0]
val arguments = Bundle()
arguments.putCharSequence(AccessibilityNodeInfo.ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE, nameList[0])
editNode.performAction(AccessibilityNodeInfo.ACTION_SET_TEXT, arguments)
nameList.removeAt(0)
}
}, 2300L)
handler.postDelayed({
val cbNodes = rootInActiveWindow.findAccessibilityNodeInfosByViewId("com.tencent.mm:id/l7")
if (cbNodes != null) {
var cbNode: AccessibilityNodeInfo? = null
if (cbNodes.size == 1) {
cbNode = cbNodes[0]
} else if (cbNodes.size == 2) {
cbNode = cbNodes[1]
}
if (cbNode != null) {
cbNode.parent.performAction(AccessibilityNodeInfo.ACTION_CLICK)
val sureNode = rootInActiveWindow.findAccessibilityNodeInfosByViewId("com.tencent.mm:id/hh")[0]
sureNode.performAction(AccessibilityNodeInfo.ACTION_CLICK)
}
}
}, 3000L)
}
}
}
}
}
//對話方塊處理
private fun dialogClick() {
val inviteNode = rootInActiveWindow.findAccessibilityNodeInfosByViewId("com.tencent.mm:id/all")[0]
inviteNode.performAction(AccessibilityNodeInfo.ACTION_CLICK)
handler.postDelayed({
val sureNodes = rootInActiveWindow.findAccessibilityNodeInfosByViewId("com.tencent.mm:id/all")
if (sureNodes != null && sureNodes.size > 0) {
val sureNode = sureNodes[0]
sureNode.performAction(AccessibilityNodeInfo.ACTION_CLICK)
}
}, 1000L)
}
//自動點贊
private fun autoZan() {
val nodeInfo = rootInActiveWindow
if (nodeInfo != null) {
while (true) {
val rootNode = rootInActiveWindow
if (rootNode != null) {
val listNodes = rootNode.findAccessibilityNodeInfosByViewId("com.tencent.mm:id/ddn")
if (listNodes != null && listNodes.size > 0) {
val listNode = listNodes[0]
val zanNodes = listNode.findAccessibilityNodeInfosByViewId("com.tencent.mm:id/dao")
for (zan in zanNodes) {
zan.performAction(AccessibilityNodeInfo.ACTION_CLICK)
Thread.sleep(300)
val zsNodes = rootInActiveWindow.findAccessibilityNodeInfosByViewId("com.tencent.mm:id/d_m")
Thread.sleep(300)
if (zsNodes != null && zsNodes.size > 0) {
if (zsNodes[0].findAccessibilityNodeInfosByText("贊").size > 0) {
zsNodes[0].performAction(AccessibilityNodeInfo.ACTION_CLICK)
}
}
Thread.sleep(300)
}
listNode.performAction(AccessibilityNodeInfo.ACTION_SCROLL_FORWARD)
}
} else {
break
}
}
}
}
//遍歷獲得未開啟紅包
private fun openRedPacket() {
val rootNode = rootInActiveWindow
if(rootNode != null) {
val listNode = rootNode.findAccessibilityNodeInfosByViewId("com.tencent.mm:id/a_c")
if (listNode != null && listNode.size > 0) {
val msgNodes = listNode[0].findAccessibilityNodeInfosByViewId("com.tencent.mm:id/ad8")
if (msgNodes != null && msgNodes.size > 0) {
for(rpNode in msgNodes) {
val rpStatusNode = rpNode.findAccessibilityNodeInfosByText("領取紅包")
if (rpStatusNode != null && rpStatusNode.size > 0) {
rpNode.performAction(AccessibilityNodeInfo.ACTION_CLICK)
break
}
}
}
}
}
}
//開啟紅包
private fun clickRedPacket() {
val nodeInfo = rootInActiveWindow
val clickNode = nodeInfo.findAccessibilityNodeInfosByViewId("com.tencent.mm:id/c31")
if (clickNode != null && clickNode.size > 0) {
clickNode[0].performAction(AccessibilityNodeInfo.ACTION_CLICK)
} else {
performBackClick()
}
}
private fun performBackClick() {
handler.postDelayed({ performGlobalAction(AccessibilityService.GLOBAL_ACTION_BACK) }, 300L)
}
}
複製程式碼
來啊,Py交易啊
想加群一起學習Py的可以加下,智障機器人小Pig
驗證通過後會自動傳送群聊連結加群連結,點選加入即可 (不要和機器人聊天=-=,就掛著拉人的,有問題到群裡講!)
歡迎各種像我一樣的Py初學者,Py大神加入,一起愉快地交流學♂習,van♂轉py。