【转载请注明出处】: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 : 815438426@qq.com
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