摘要
APP数据获取,目前为止已介绍了两种方法,一种是APK反编译,一种是破解数据流在传输过程中的各种转换;上述两种方式,对能力和毅力都有很大的要求。本文“投机取巧”,仅关注输入输出;输入的是用户APP数据所在页面的操作,输出的是数据所在接口的源码。输出,可通过Fiddler、Charles等拦截工具进行拦截,本文不作累述;输入,涉及Appium自动化测试,本文将对此进行详解。
前期准备
手机端
软件 | 版本 |
---|---|
魅蓝 Note5 | M5 Note |
Android | 6.0 |
Xposed Installer | 3.1.5 |
Xposed Version | 89 |
Just Trust Me | 0.2 |
PC端
软件 | 版本 |
---|---|
JDK | 1.8.0_77 |
Android SDK | 25.2.5 |
Node.js | 12.0 |
Appium | 1.18.3 |
PC端通过Appium操作手机端APP截图
Maven
<dependency>
<groupId>io.appium</groupId>
<artifactId>java-client</artifactId>
<version>4.1.2</version>
</dependency>
Java APP操作代码
import io.appium.java_client.MobileElement;
import io.appium.java_client.android.AndroidDriver;
import io.appium.java_client.android.AndroidElement;
import io.appium.java_client.android.AndroidKeyCode;
import io.appium.java_client.remote.AndroidMobileCapabilityType;
import io.appium.java_client.remote.MobileCapabilityType;
import org.openqa.selenium.By;
import org.openqa.selenium.remote.DesiredCapabilities;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.WebDriverWait;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.net.URL;
public abstract class BaseCrawler {
public static final Logger LOGGER = LoggerFactory.getLogger(BaseCrawler.class);
public static final String url="http://0.0.0.0:4723/wd/hub";
protected URL serverURL;
protected DesiredCapabilities capabilities;
protected AndroidDriver<AndroidElement> driver;
public BaseCrawler(){
try {
serverURL = new URL(url);
} catch (Exception e) {
e.printStackTrace();
}
capabilities = new DesiredCapabilities();
capabilities.setCapability(MobileCapabilityType.PLATFORM_NAME, "Android");
capabilities.setCapability(MobileCapabilityType.DEVICE_NAME, "TPE9X18612W18698");
capabilities.setCapability(MobileCapabilityType.AUTOMATION_NAME, "UiAutomator2");
capabilities.setCapability(MobileCapabilityType.NO_RESET, true);
capabilities.setCapability(AndroidMobileCapabilityType.APP_PACKAGE, "com.xingin.xhs");
capabilities.setCapability(AndroidMobileCapabilityType.APP_ACTIVITY, ".activity.SplashActivity");
}
public void initDriver(){
LOGGER.info("正在启动Appium");
driver = new AndroidDriver(serverURL, capabilities);
}
public void sleep(long timeout) {
try {
Thread.sleep(timeout);
} catch (Exception e) {
;
}
}
public void goBack() {
driver.pressKeyCode(AndroidKeyCode.BACK);
sleep(3000);
}
public void click(By by, String errorMsg) {
LOGGER.debug("点击 " + by);
AndroidElement element = awaitFindElement(by,3000);
if (element != null) {
element.click();
} else {
LOGGER.error(errorMsg + " " + by);
throw new RuntimeException(errorMsg);
}
}
public void type(By by, String keyword, String errorMsg) {
LOGGER.debug("输入:" + keyword + " " + by);
AndroidElement element = awaitFindElement(by, 3000);
if (element != null) {
element.sendKeys(keyword);
} else {
LOGGER.error(errorMsg);
throw new RuntimeException(errorMsg);
}
}
public String getText(By by, String errorMsg) {
AndroidElement element = awaitFindElement(by,3000);
if (element != null) {
return element.getText();
} else {
LOGGER.error(errorMsg);
throw new RuntimeException(errorMsg);
}
}
public void waitFor(By by, long timeout) {
WebDriverWait wait = new WebDriverWait(driver, timeout);
wait.until(ExpectedConditions.visibilityOfElementLocated(by));
}
public AndroidElement awaitFindElement(By by, long timeout) {
try {
waitFor(by, timeout);
return driver.findElement(by);
} catch (Exception e) {
return null;
}
}
public List<AndroidElement> awaitFindElements(By by, long timeout) {
try {
waitFor(by, timeout);
return driver.findElements(by);
} catch (Exception e) {
return null;
}
}
public boolean isVisiable(By by) {
try {
AndroidElement element = driver.findElement(by);
return element.isDisplayed();
} catch (Exception e) {
return false;
}
}
public boolean isVisiable(AndroidElement element, By by) {
try {
MobileElement mobileElement = element.findElement(by);
return mobileElement.isDisplayed();
} catch (Exception e) {
return false;
}
}
public void swipeUp() {
try {
driver.swipe(360, 1200, 360, 400, 200);
sleep(3000);
} catch (Exception e) {
LOGGER.error("上划失败", e);
}
}
}