天天看點

使用mitmproxy + appium + python 全自動抓取公衆号文章

【轉載請注明出處】: https://segmentfault.com/a/1190000022667615

1. 先使用mitmproxy代理抓微信公衆号曆史文章清單

實作思路:

在電腦上安裝 mitmproxy代理,手機和電腦連同一wifi,手機配置目前電腦為代理伺服器,然後手動檢視公衆号曆史文章清單,這樣電腦上就可以獲得清單,然後再根據清單中的詳情url拿到公衆号文章詳情。

1.1 安裝mitmproxy

需要先安裝Python 3.6 (或更高版本)和 pip3.6 ,之後執行以下指令安裝

sudo pip3.6 install -U pip
sudo pip3.6 install mitmproxy           
1.2 準備代理腳本

wechat_proxy.py

更多的例子可以參考

官網simple
# -*- coding: utf-8 -*-
# @Time    : 2019/7/11
# @Author  : zl

from mitmproxy import http
from mitmproxy import ctx
from urllib import parse
import json


def response(flow: http.HTTPFlow):
    try:
        info = ctx.log.info
        req = flow.request
        resp = flow.response
        url = req.url
        purl = parse.urlparse(url)
        param_dict = parse.parse_qs(purl.query)
        host = purl.hostname
        path = purl.path

        if host == 'mp.weixin.qq.com' and path == '/mp/profile_ext':
            action = param_dict['action'][0]
            if action in ['home', 'getmsg']:
                msg = {}
                msg['url'] = url
                msg['action'] = action
                msg['req_header'] = dict(req.headers)
                msg['resp_header'] = dict(resp.headers)
                if action == 'getmsg':
                    msg['resp_body'] = json.loads(resp.content)
                info('msg : %s----' % msg)

    except Exception as e:
        ctx.log.error('wechatproxy error, %s' % e)
           

執行下面的腳本啟動:

mitmdump -p 8888 -s ${script_home}/wechat_proxy.py           

手機浏覽器通路

http://mitm.it/

安裝證書并啟用,然後配置手機代理,翻閱公衆号文章清單既可以看到消息日志,文章詳情的抓取這裡不再介紹

mitmproxy其他的用法可以參考

官方文檔

2. 使用appium實作自動化

2.1 前提

2.1.1 安裝JDK 并設定環境變量

1. 到Java官網下載下傳相應的JDK并安裝
2. 設定環境變量
   - 添加JAVA_HOME 
   - 在PATH變量末尾追加 ;%JAVA_HOME%/bin;
   - 添加CLASSPATH,設定值為%JAVA_HOME%\lib;%JAVA_HOME%\lib\tools.jar
           

2.1.2 安裝Android SDK 并設定環境變量

注意:安裝Android SDK需要更新你的SDK repository

1.  到[Android 官網](http://developer.android.com/sdk/index.html#Other)下載下傳并安裝SDK
2.  設定環境變量
    - 添加`ANDROID_HOME`
    - 在`path`環境變量值末尾追加:`;%ANDROID_HOME%\tools;%ANDROID_HOME%\platform-tools;`
           

2.1.3 安裝Nodejs

#先安裝一個node版本管理軟體 
brew install n 
#或者
curl -L https://git.io/n-install | bash
#安裝node8.6
sudo n 8.6.0
#後面安裝的時候有可能沒權限
sudo chown -R $(whoami):admin /usr/local/**           

注:不要安裝太高版本,否則後面安裝某些依賴會不相容。

2.2 安裝appium

安裝appium有兩種方式:

  • 使用 npm (Node js 的管理與分發工具) 安裝Appium
  • 使用Appium官方安裝包安裝

下面隻介紹npm安裝方式,如果要裝desktop,直接去

官網下載下傳

最新版安裝即可。

将npm的下載下傳位址更改為淘寶的NPM鏡像源

npm config set registry=https://registry.npm.taobao.org
npm config get registry           

确認鏡像源替換成功之後執行下面指令安裝Appium

npm install -g appium           
2.3 安裝其他依賴

2.3.1 安裝opencv4nodejs

如果已經安裝openCV,則需要在安裝opencv4nodejs之前設定

export OPENCV4NODEJS_DISABLE_AUTOBUILD=1
export OPENCV_INCLUDE_DIR=/usr/local/include/opencv4
export OPENCV_LIB_DIR=/usr/local/lib
export OPENCV_BIN_DIR=/usr/local/bin           

npm安裝

npm install -g opencv4nodejs           

2.3.2 安裝idevicelocation

brew install libzip libplist openssl libimobiledevice
git clone https://github.com/JonGabilondoAngulo/idevicelocation.git
cd idevicelocation
make
sudo make install           
  • 遇到問題
    checking for libimobiledevice-1.0 >= 1.2.1... no
    configure: error: Package requirements (libimobiledevice-1.0 >= 1.2.1) were not met:
    Package 'openssl', required by 'libimobiledevice-1.0', not found           
    解決方法
    ln -s /usr/local/Cellar/openssl/${YOUR_OPENSSL_VERSION}/lib/pkgconfig/* /usr/local/lib/pkgconfig/           

2.3.3 安裝idb 及 idb_companion

brew tap facebook/fb
brew install idb-companion
brew tap cartr/qt4
brew tap-pin cartr/qt4
brew install cartr/qt4/qt@4
pip3.6 install fb-idb
brew install cmake usbmuxd libimobiledevice           

執行

$ idb list-targets
...
iPhone X | 569C0F94-5D53-40D2-AF8F-F4AA5BAA7D5E | Shutdown | simulator | iOS 12.2 | x86_64 | No Companion Connected
iPhone Xs | 2A1C6A5A-0C67-46FD-B3F5-3CB42FFB38B5 | Shutdown | simulator | iOS 12.2 | x86_64 | No Companion Connected
iPhone Xs Max | D3CF178F-EF61-4CD3-BB3B-F5ECAD246310 | Shutdown | simulator | iOS 12.2 | x86_64 | No Companion Connected
iPhone Xʀ | 74064851-4B98-473A-8110-225202BB86F6 | Shutdown | simulator | iOS 12.2 | x86_64 | No Companion Connected
...           
  • 遇到問題1
    Error: An unexpected error occurred during the `brew link` step
    The formula built, but is not symlinked into /usr/local
    Permission denied @ dir_s_mkdir - /usr/local/Frameworks
    Error: Permission denied @ dir_s_mkdir - /usr/local/Frameworks           
    解決
    sudo chown -R $(whoami):admin /usr/local/**
    sudo mkdir /usr/local/Frameworks
    sudo chown $(whoami):admin /usr/local/Frameworks           
  • 遇到問題2

    執行idb指令報錯

    TypeError: __init__() got an unexpected keyword argument 'serialized_options'           
    pip3.6  install -U protobuf           

2.3.4 安裝bundletool

cd ${ANDROID_HOME}
mkdir bundle-tools
wget https://github.com/google/bundletool/releases/download/0.10.1/bundletool-all-0.10.1.jar
mv bundletool-all-0.10.1.jar bundletool.jar
sudo chmod 755 bundletool.jar
sudo chown $(whoami):admin bundletool.jar           

2.3.5 安裝剩餘依賴

npm install -g ffmpeg
npm install -g mjpeg-consumer
brew tap wix/brew
brew install applesimutils
brew install ios-deploy
brew install ios-webkit-debug-proxy
brew cask install osxfuse
brew install ifuse --HEAD 
brew install carthage
brew install fbsimctl --HEAD
gem install xcpretty           
2.4 驗證安裝

通過appium-doctor的指令來檢查目前appium安裝是否完善,目前的JDK、SDK等環境是否配置正确。

npm install -g appium-doctor
appium-doctor           
2.5 運作測試demo準備

2.5.1 下載下傳

appium源碼

git clone https://github.com/appium/appium.git           

2.5.2 啟動appium 服務

appium           

2.5.3 安裝用戶端驅動程式

cd到

${code_dir}/appium/sample-code/python

目錄下,運作下面指令就可以運作示例了,appium本身支援多種語言的用戶端,我這裡語言選擇的是python

npm install -g appium-uiautomator2-driver
npm install -g webdriverio
npm install -g app-inspector
pip3.6 install -r requirements.txt
pip3.6 install -U facebook-wda           
2.6 在模拟器和真機上運作測試腳本

2.6.1 用Android 模拟器進行測試

py.test test/test_android_selectors.py            

報錯

E  WebDriverException: Message: An unknown server-side error occurred while processing the command. Original error: Unable to find an active device or emulator with OS 8.0. The following are available: emulator-5554 (9)           

這裡已經提示的很清楚了,就是沒有找到Android 8.0版本的裝置或者模拟器,發現了可用的裝置,Android平台版本是9,裝置版本是emulator-5554,根據提示修改

helpers.py

的預設版本或者設定環境變量,

ANDROID_BASE_CAPS = {
    'app': os.path.abspath('../apps/ApiDemos-debug.apk'),
    'automationName': 'UIAutomator2',
    'platformName': 'Android',
    'platformVersion': os.getenv('ANDROID_PLATFORM_VERSION') or '9',
    'deviceName': os.getenv('ANDROID_DEVICE_VERSION') or 'emulator-5554',
}           

或者

export ANDROID_PLATFORM_VERSION=9
export ANDROID_DEVICE_VERSION=emulator-5554           

注釋掉

helpers.py

中上報到saucelabs的調用

def fin():
            # report_to_sauce(driver.session_id)           

也可以去

https://saucelabs.com新增賬號

,然後設定環境變量

export SAUCE_LABS=
export SAUCE_USERNAME=
export SAUCE_ACCESS_KEY=           

然後再執行測試腳本

2.6.2 用Android真機測試

2.6.2.1 連接配接手機

  • 使用資料線通過USB接口将手機與電腦連接配接,在終端中執行如下指令:
    adb devices             

表示資料線已經連接配接成功,但是使用資料線有時候不是很友善,也不能準确測試耗電量,下面介紹無線連接配接方式

  • 使用無線連接配接(在同一網段)

    一般在

    設定-關于手機-狀态-IP位址

    可以找到手機的IP,或者執行指令:
    adb shell ip -f inet addr show wlan0           
    adb tcpip 5555
    //多個裝置的話需要指定具體的裝置
    //adb -s f489be9c tcpip 5555
    adb connect 192.168.1.5:5555           

這時候就可以拔掉資料線了。

注:要斷開連接配接,執行:

adb disconnect

2.6.2.2 執行測試腳本

export ANDROID_PLATFORM_VERSION=6.0.1
export ANDROID_DEVICE_VERSION=192.168.1.5:5555
py.test test/test_android_selectors.py            

2.6.3 用ios 模拟器測試

2.6.3.1 ios 模拟器基本指令

列出可用的裝置、裝置類型、運作時或裝置對。

simctl list [-j | --json] [-v] [devices|devicetypes|runtimes|pairs] [<search term>|available]
//列出可用的裝置
xcrun simctl list devices           

其他指令

#啟動iPhone XR模拟器
open "/Applications/Xcode.app/Contents/Developer/Applications/Simulator.app/"
xcrun simctl boot "D9309483-64F9-499F-B295-CD2C5FAA75C8"
#重置iPhone XR模拟器
xcrun simctl erase "D9309483-64F9-499F-B295-CD2C5FAA75C8"
#關閉iPhone XR模拟器
xcrun simctl shutdown "D9309483-64F9-499F-B295-CD2C5FAA75C8"
#關閉所有模拟器
xcrun simctl shutdown booted           

2.6.3.2 調試WebDriverAgent

$ which appium
/usr/local/bin/appium
$ ls -l /usr/local/bin/appium
lrwxr-xr-x  1 eyison  admin  44  7 19 01:34 /usr/local/bin/appium -> ../lib/node_modules/appium/build/lib/main.js
$ cd /usr/local/lib/node_modules/appium/node_modules/appium-xcuitest-driver/WebDriverAgent
# 用 Xcode 打開 WebDriverAgent.xcodeproj
$ open WebDriverAgent.xcodeproj           

WebDriverAgentLib->Build Setting->Runpath Search Paths->添加變量:

$(SRCROOT)/../Carthage/Build/iOS
$(PROJECT)/Carthage/Build/iOS           

直接在iPhone XR上運作IntegrationApp進行測試,執行沒有報錯就OK。

  • 遇到的錯誤1
'interfaceOrientation' is deprecated: first deprecated in iOS 8.0           

解決:

輕按兩下錯誤定位,将

self.interfaceOrientation

換成

[[UIApplication sharedApplication] statusBarOrientation]

if (UIInterfaceOrientationIsPortrait(self.interfaceOrientation)) {
    ...           

改成

if (UIInterfaceOrientationIsPortrait([[UIApplication sharedApplication] statusBarOrientation])) {
    ...             
  • 遇到的錯誤2:
/usr/local/lib/node_modules/appium/node_modules/appium-xcuitest-driver/WebDriverAgent/WebDriverAgent.xcodeproj User-supplied CFBundleIdentifier value 'com.facebook.wda.integrationApp' in the Info.plist must be the same as the PRODUCT_BUNDLE_IDENTIFIER build setting value 'com.facebook.IntegrationApp23'.           

Product Bundle Identifier

的值改成和Info.plist 一緻即可

2.6.3.3 執行測試腳本

export IOS_PLATFORM_VERSION=12.0
export IOS_DEVICE_NAME=iPhone XR
export UDID=D9309483-64F9-499F-B295-CD2C5FAA75C8
py.test test/test_ios_selectors.py           
  • 遇到的問題1:
[WD Proxy] Got response with status 200: {"value":"-[XCUIElementQuery elementSnapshotForDebugDescription]: unrecognized selector sent to instance 0x600000b80eb0\n\n(\n\t0   CoreFoundation   ...           

解決方法:

參考github
$ cd /usr/local/lib/node_modules/appium/node_modules/appium-xcuitest-driver
sudo rm -fr WebDriverAgent
#或者直接下載下傳zip,然後解壓
git clone https://github.com/appium/WebDriverAgent.git           

更新WebDriverAgent的代碼之後按照2.6.3.2 重新調試

  • 遇到的問題2:
=================================== FAILURES   ===================================
  _____________ TestIOSSelectors.test_should_find_elements_by_xpath   ______________
  
  self = <test_ios_selectors.TestIOSSelectors object at 0x10ab56290>
  driver = <appium.webdriver.webdriver.WebDriver (session="dddb8afc-6146-4c3a-8079-952eb333586c")>
  
    def test_should_find_elements_by_xpath(self, driver):
        action_bar_container_elements = driver.find_elements_by_xpath('//XCUIElementTypeWindow//XCUIElementTypeButton')
         assert 7 == len(action_bar_container_elements)
  E       assert 7 == 8
  E        +  where 8 = len([<appium.webdriver.webelement.WebElement (session="dddb8afc-6146-4c3a-8079-952eb333586c", element="10000000-0000-0000-...ment.WebElement (session="dddb8afc-6146-4c3a-8079-952eb333586c", element="1B000000-0000-0000-AC7F-010000000000")>, ...])
  
  test/test_ios_selectors.py:52: AssertionError
  ===================== 1 failed, 4 passed in 150.54 seconds    =====================           

修改

test_ios_selectors.py

test_should_find_elements_by_xpath

方法,将

assert 7 == len(action_bar_container_elements)

assert 8 == len(action_bar_container_elements)

2.6.4 用ios真機測試

2.6.4.1 擷取手機的udid

将手機通過usb連上電腦,然後打開itunes,然後選中連結的iPhone,點選一下序列号,然後會彈出 udid 。

或者執行下面指令擷取

echo $(idevice_id -l | head -n1)           

2.6.4.2 調試WebDriverAgent

真機的調試需要開發者賬号和證書,可以參考

Appium官網配置說明

蘋果開發者賬号說明

(不上傳App Store不需要交錢)

然後分别為WebDriverAgentLib、WebDriverAgentRunner和IntegrationApp在

Signing & Cababilites

設定開發者賬号和證書

WebDriverAgentRunner另外還需要修改

Product Bundle Identifier

Build Settings

頁籤,将

Product Bundle Identifier

的值

com.facebook.WebDriverAgentRunner

更改成Xcode能接受的值就行,

Info

頁籤中也要保持一緻。

然後再設定開發者賬号和證書

IntegrationApp的需要配置的地方和WebDriverAgentRunner一樣,這裡不再介紹。

分别編譯WebDriverAgentLib和WebDriverAgentRunner不報錯,然後試着安裝IntegrationApp看是否能夠安裝和運作。

這時候應該會報一個錯

Install claimed to have succeeded, but application could not be found on device. bundleId = com.eyison.wda.integrationApp           

需要在手機上信任證書:

設定>通用>描述檔案與裝置管理

再次執行應該可以安裝并啟動IntegrationApp。

$ which appium
/usr/local/bin/appium
$ ls -l /usr/local/bin/appium
lrwxr-xr-x  1 eyison  admin  44  7 19 01:34 /usr/local/bin/appium -> ../lib/node_modules/appium/build/lib/main.js
$ cd /usr/local/lib/node_modules/appium/node_modules/appium-xcuitest-driver/WebDriverAgent
$ xcodebuild -project WebDriverAgent.xcodeproj -scheme WebDriverAgentRunner -destination 'id={udid}' test
           

如果你看到下面這個就說明WebDriverAgent已經啟動成功了,在手機上也同時多了一個WebDriverAgent的app

Test Case '-[UITestingUITests testRunner]' started.
    t =     0.00s Start Test at 2019-07-30 23:12:25.997
    t =     0.00s Set Up
2019-07-30 23:12:26.043077+0800 WebDriverAgentRunner-Runner[1710:445441] Built at July  30 2019 22:46:51
2019-07-30 23:12:26.057947+0800 WebDriverAgentRunner-Runner[1710:445441] ServerURLHere->http://172.18.32.226:8100<-ServerURLHere
2019-07-30 23:12:26.058656+0800 WebDriverAgentRunner-Runner[1710:445615] Using singleton test manager
           

執行以下指令,将 iOS 裝置上 WebDriverAgentRunner 監聽的 8100 端口映射到 macOS 本地的 8100 端口

iproxy 8100 8100  {udid}           

接下來驗證WebDriverAgent Server的狀态

$ curl 'http://localhost:8100/status'
{
  "value" : {
    "state" : "success",
    "os" : {
      "name" : "iOS",
      "version" : "12.4",
      "sdkVersion" : "13.0"
    },
    "ios" : {
      "simulatorVersion" : "12.4",
      "ip" : "172.18.32.226"
    },
    "build" : {
      "time" : "July  30 2019 22:46:52",  
      "productBundleIdentifier" : "com.facebook.WebDriverAgentRunner"
    }
  },
  "sessionId" : "99D81C1E-F9A8-4126-94CE-0A9E768892A2",
  "status" : 0
}           

不通過代理直接請求

curl 'http://172.18.32.226:8100/status'

一直是逾時,不知道什麼原因。

官網上測試代碼的app包一直沒在我的iphone8 plus上安裝成功,最後放棄了,感覺掉坑裡了,不應該在這上面浪費時間的,自己的包就沒有問題,而且用腳本直接調用WDA的api也是沒有問題的。報錯大緻有下面這些,最後也沒有細究就放棄了

問題1:

WebDriverAgentRunner-Runner[1747:453117] [User Defaults] Couldn't write value for key KeyboardAutocorrection in CFPrefsPlistSource<0x2829c3780> (Domain: com.apple.Preferences, User: kCFPreferencesCurrentUser, ByHost: No, Container: (null), Contents Need Refresh: No): setting preferences outside an application's container requires user-preference-write or file-write-data sandbox access           

問題2:

Error: Could not install app: 'Command 'ios-deploy --id exited with code 253'           

問題3:

ios-deploy[27399:2413371] [ !! ] Error 0xe8008014: The executable contains an invalid signature. AMDeviceSecureInstallApplication(0, device, url, options, install_callback, 0)           
2.7 微信公衆号全自動化抓取

為了調試友善可以安裝一個桌面版的Appium,

記得配置WebDriverAgent的簽名等,目錄在

/Applications/Appium.app/Contents/Resources/app/node_modules/appium/node_modules/appium-xcuitest-driver/WebDriverAgent

,配置方式同上面

ios真機測試

介紹的步驟。

然後啟動appium服務

然後建立會話并填寫配置,填完之後點選

啟動會話

按鈕來啟動微信

{
  "platformName": "iOS",
  "platformVersion": "12.4",
  "deviceName": "iPhone",
  "bundleId": "com.tencent.xin",
  "udid": "",
  "xcodeSigningId": "iPhone Developer",
  "xcodeOrgId": ""
}           

根據需求添加必要的參數,常用參數如下:

  • automationName:

    "Appium", -- 可選,搜尋引擎,appium(預設)還是Selendroid

  • platformName:

    "iOS", -- 必填,應用平台,iOS, Android, or FirefoxOS

  • platformVersion:

    "10.3", -- 必填,測試的系統版本,設定兩位就可以了

  • deviceName:

    "iPhone", -- 必填,使用的手機或模拟器類型,iPhone Simulator, Android Emulator, Galaxy S4

  • app:

    "com.apple.calculator", -- Android可選,iOS必填,應用的絕對路徑,與browserName屬性沖突

  • browserName:

    "Chromium", -- 做自動化時使用的浏覽器名字, 與app 沖突

  • bundleId:

    "io.appium.TestApp", -- iOS未填app參數必填,用于在真實裝置中啟動測試,使用其他需要 bundle ID 的關鍵字啟動測試。

  • udid:

    " ", -- 多用戶端連結多台手機時必填,連接配接真機的唯一裝置号,iOS通過iTunes拷貝過來,IOS真機測試必填

  • noReset:

    "true" -- 在目前 session 下不會重置應用的狀态, 是否清除應用緩存。預設值為false,

  • fullReset:

    "false" -- (iOS)删除所有的模拟器檔案夾。(Android) 要清除 app 裡的資料,請将應用解除安裝才能達到重置應用的效果。在 Android, 在 session 完成之後也會将應用解除安裝掉。預設值為false

  • xcodeOrgId:

    "123ADE4Y56", -- 十位字元的組織ID是蘋果開發證書的組織機關,appium可以通過十位組織機關ID找到相應的組織,IOS真機測試必填(注意: 連接配接真機時,測試app的打包簽名證書必須要與xcodeOrgId裡的一緻,否則會報證書錯誤,錯誤代碼是65)

  • xcodeSigningId:

    "iPhone Developer", -- iOS真機測試必填,這個參數是固定的

  • wdaLocalPort:

    "8100" -- 預設手機連接配接MAC本使用的端口,預設是8100,非必填

  • unicodeKeyboard:

    "true" -- 使用 Unicode字元,輸入中文必須參數

  • resetKeyboard"

    "true" -- 重置鍵盤,輸入中文必須參數

會話連接配接成功後就會展示 The Inspector,它是應用程式狀态的可視化表示。

編寫腳本,代碼太長,貼出部分,回頭上傳到github分享出來

# -*- coding: utf-8 -*-
# @Time    : 2019/8/5 
# @Author  : zl  
# @Email   : [email protected]

from threading import Thread
from appium import webdriver
from appium.webdriver.common.touch_action import TouchAction

class AppiumWechatSpider(Thread):
    __bundle_id = 'com.tencent.xin'

    __IOS_BASE_CAPS = {
        'automationName': 'XCUITest',
        'platformName': 'iOS',
        'platformVersion': '12.4',
        'deviceName': 'iPhone',
        "bundleId": __bundle_id,
        'udid': '',
        'xcodeSigningId': 'iPhone Developer',
        'xcodeOrgId': '',
        'showIOSLog': False,
        'unicodeKeyboard': True,
        'resetKeyboard': True,
        'newCommandTimeout': 3000,
        'sendKeyStrategy': 'setValue'
    }
    __EXECUTOR = 'http://127.0.0.1:4723/wd/hub'

    def __init__(self):
        super().__init__()
        self._running = False
        self.name = 'AppiumWechatSpider'
        self.driver = webdriver.Remote(
            command_executor=self.__EXECUTOR,
            desired_capabilities=self.__IOS_BASE_CAPS
        )
        self.forward_step = 0
        self.focused_acount = 0

    def run(self):
        if self._running:
            return
        self._running = True
        print('微信已經啟動')
        self.driver.find_element_by_xpath('//XCUIElementTypeSearchField[@name="搜尋"]').click()
        print('點選搜尋框')
        self.driver.find_element_by_xpath('//XCUIElementTypeButton[@name="公衆号"]').click()
        print('點選公衆号按鈕')
        input = self.driver.find_element_by_xpath('(//XCUIElementTypeSearchField[@name="搜尋公衆号"])[1]')
        input.click()
        input.send_keys(wechat_name)

    ...
            

然後配置手機連上代理讓腳本自動翻頁公衆号的文章清單即可抓取

官方參數介紹:

Appium Desired Capabilities AppiumXcuitestDriver Desired Capabilities