Java+Selenium--页⾯反爬⾍机制,安全滑块踩过的坑
最近⼯作中,要在淘宝四级页下订单并⽀付。淘宝的页⾯对⾃动化脚本识别控制还是挺多,短时间重复登录、下单并⽀付操作,会被后台检测,会在登录,四级页,订单提交页⾯出现安全滑块拦截。以下为最近遇到的问题踩到的坑和解决办法。
1.关于页⾯识别window.navigator.webdirver属性值的问题
当我们没有使⽤⾃动化脚本时,本地打开⾕歌浏览器,在控制台输⼊window.navigator.webdirver时,返回的是undefined。
当我们使⽤webDriver调⽤本地浏览器时,在控制台输⼊window.navigator.webdirver时,可能返回True被服务端判定为爬⾍,会登录产⽣滑块或者登录失败。
此时需要在代码中加⼊CdpCommand,同时还有ChromeOptions操作,代码⼀并附上
public RemoteWebDriver init() {
//设置property
//driverpath为本地的的路径
System.setProperty("webdriver.chrome.driver", driverpath);
System.out.println("准备实例化ChromeOpen类");
//设置浏览器options
ChromeOptions options = new ChromeOptions();
// 关闭界⾯上的---Chrome正在受到⾃动软件的控制
options.addArguments("disable-infobars");
// 允许重定向
//options.addArguments("--disable-web-security");
// 最⼤化
//options.addArguments("--start-maximized");
//options.addArguments("--no-sandbox");
/
/设置ExperimentalOption
List<String> excludeSwitches = wArrayList("enable-automation");
options.setExperimentalOption("excludeSwitches", excludeSwitches);
options.setExperimentalOption("useAutomationExtension", false);
ChromeDriver driver = new ChromeDriver(options);
//修改window.navigator.webdirver=undefined,防机器⼈识别机制
Map<String, Object> command = new HashMap<>();
command.put("source", "Object.defineProperty(navigator, 'webdriver', {get: () => undefined})");
return driver;
}
2.判断页⾯元素是否存在,主要⽤于检测是不是有安全滑块(这个是真的烦,狗头ORZ)
//判断是否存在某元素
public boolean isJudgingElement(RemoteWebDriver remoteWebDriver, By by) {
try {
remoteWebDriver.findElement(by);
return true;
} catch (Exception e) {
System.out.println("不存在此元素");
return false;
}
}
3.淘宝登录滑块,此处需要使⽤action操作,且 action.dragAndDropBy(moveButton, 258, 0).perform()为重点操作。
//点击登录按钮
Thread.sleep(2000);
WebElement login = remoteWebDriver.findElement(By.xpath("//form[@id=\"login-form\"]//div[@class=\"fm-btn\"]/button"));
login.click();
//判断是否有滑块
Thread.sleep(2000);
if (isJudgingElement(remoteWebDriver, By.id("baxia-dialog-content"))) {
remoteWebDriver.switchTo().frame("baxia-dialog-content");
if (isJudgingElement(remoteWebDriver, By.id("nc_2_n1z"))) {
Actions action = new Actions(remoteWebDriver);
WebElement moveButton = remoteWebDriver.findElement(By.id("nc_2_n1z"));
// 移到滑块元素并悬停,不能超出框的长度,否则异常
action.clickAndHold(moveButton);
action.dragAndDropBy(moveButton, 258, 0).perform();
}
remoteWebDriver.switchTo().defaultContent();
login.click();
}
//校验登录成功
Thread.sleep(2000);
WebElement userid = remoteWebDriver.findElement(By.xpath("//div[@class=\"site-nav-user\"]/a"));
System.out.Text());
if (Text().equals(username)) {
System.out.println("淘宝⽹登录成功");
}
4.代码执⾏失败截图浏览器,并打印⽇志,退出浏览器,防⽌浏览器进程过多未关闭
catch (Exception e) {
e.printStackTrace();
SimpleDateFormat df = new SimpleDateFormat("yyyyMMddHHmmss");//设置⽇期格式
String currentTime = df.format(new Date());
System.out.println(currentTime);// new Date()为获取当前系统时间
String path = "C:\\Users\\Desktop\\pic\\Exception_" + currentTime + ".png";
File src = ((TakesScreenshot) remoteWebDriver).getScreenshotAs(OutputType.FILE);  // 调⽤截图⽅法
System.out.println("登录失败!");
Thread.sleep(2000);
remoteWebDriver.quit();
}
//浏览器退出
public void webDriverQuit(RemoteWebDriver remoteWebDriver) {
remoteWebDriver.quit();
System.out.println("浏览器退出成功");
}
5.四级页安全滑块问题,⽬前四级页的安全滑块和确认订单页的安全滑块,没有⽐较好的⽅法,使⽤了登录的滑块⽅法,但是并没有效果。不过好在四级页还可以通过关闭滑块弹框,等待页⾯加载控件,点击⽴即购买,到确认订单页⾯。
//打开四级页
<(Level4PageUrl);
Thread.sleep(2000);
//判断是否有安全滑块,点击关闭
if (tblogin.isJudgingElement(remoteWebDriver, By.id("sufei-dialog-content"))) {
/*remoteWebDriver.switchTo().frame("sufei-dialog-content");*/
remoteWebDriver.findElement(By.id("sufei-dialog-close")).click();
/*remoteWebDriver.switchTo().defaultContent();*/
//页⾯缓存
Thread.sleep(10000);
}
//点击⽴即购买按钮
remoteWebDriver.findElement(By.id("J_LinkBuy")).click();
Thread.sleep(2000);
6.确认订单页⾯的安全滑块(参照账号登录⽅法,不过滑块验证失败,待解决,有解决⽅案的⼤佬多多指教,感谢)
//订单页⾯滑块判断
if (tblogin.isJudgingElement(remoteWebDriver, By.id("nc_1_n1z"))) {
Actions action = new Actions(remoteWebDriver);
WebElement moveButton = remoteWebDriver.findElement(By.id("nc_1_n1z"));
// 移到滑块元素并悬停,不能超出框的长度,否则异常
action.clickAndHold(moveButton);
action.dragAndDropBy(moveButton, 258, 0).perform();
Thread.sleep(2000);
if(!tblogin.isJudgingElement(remoteWebDriver, By.linkText("提交订单"))){
//试图⼆次刷新页⾯,操作安全滑块
String currentUrl = CurrentUrl();
<(currentUrl);
Thread.sleep(2000);
action = new Actions(remoteWebDriver);java浏览器下载
moveButton = remoteWebDriver.findElement(By.id("nc_1_n1z"));
// 移到滑块元素并悬停,不能超出框的长度,否则异常
action.clickAndHold(moveButton);
action.dragAndDropBy(moveButton, 258, 0).perform();
Thread.sleep(2000);
}
}
7.重头戏!好家伙,这玩意卡了我两天,⽀付页⾯,输⼊密码提交⽀付。开始的思路,页⾯的密码输⼊框不可点击,想通过js语句去修改html的css属性,达到元素可以定位的效果。⽆奈,不论怎么修改,都⽆法在页⾯⽣效,这⾥不得不给⽀付宝的开发⼤⼤点赞。
这个思路不⾏,就想别的解决⽅案。发现从订单提交页⾯提交成功后,跳转到⽀付页⾯,光标是⾃动定位到密码的第⼀个输⼊框的,⽽且元素的属性是visibility: visible,并且输⼊第⼀个数字后,光标⾃动移到第⼆个输⼊框。然后就想,是不是有⽅法能够获取当前光标,直
接去模拟键盘操作。在⽹上了很多帖⼦,奈何基本都是python的帖⼦,⽤的是keyboard类的⽅法。就在绝望准备推倒java,拿起python⼯具之前,google了⼀下,发现可以使⽤action的思路。
//输⼊⽀付密码
for (int i = 0; i < payPassword.length(); i++) {
char c = payPassword.charAt(i);
String password = String.valueOf(c);
Actions action = new Actions(remoteWebDriver);
action.sendKeys(password).build().perform();
action.clickAndHold();
/*String path = picpath + i + ".png";
File src = ((TakesScreenshot) remoteWebDriver).getScreenshotAs(OutputType.FILE);  // 调⽤截图⽅法
}
Thread.sleep(1000);
remoteWebDriver.findElement(By.id("J_authSubmit")).click();
Thread.sleep(5000);
//付款成功校验
String resp = remoteWebDriver.findElement(By.xpath("//div[@id=\"J_AmountList\"]/h2")).getText();
if ("您已成功付款".equals(resp)) {
System.out.println("订单⽀付成功");
String currentUrl = CurrentUrl();
System.out.println(currentUrl);
String[] orderIdArray1 = currentUrl.split("bizOrderId=");
String orderIdString = orderIdArray1[1];
String[] orderIdArray2 = orderIdString.split("&");
orderId = orderIdArray2[0];
System.out.println("订单编号orderId:" + orderId);
SimpleDateFormat df = new SimpleDateFormat("yyyyMMddHHmmss");//设置⽇期格式
currentTime = df.format(new Date());
String path = picpath + currentTime + "_orderCreate_" + orderId + ".png";
File src = ((TakesScreenshot) remoteWebDriver).getScreenshotAs(OutputType.FILE);  // 调⽤截图⽅法                pyFile(src, new File(path));
tblogin.webDriverQuit(remoteWebDriver);
}
8.将订单号,下单时间等信息保存到excel中
public static void createExcelxlsx(String path) throws Exception {
//创建excel对象
XSSFWorkbook wb = new XSSFWorkbook();
//⽤⽂件对象创建sheet对象
XSSFSheet sheet = wb.createSheet("sheet1");
//创建单元格样式
CellStyle cellStyle = wb.createCellStyle();
//设置表头
XSSFRow rowInit = ateRow(0);
Cell cell1 = ateCell(0);
Cell cell2 = ateCell(1);
Cell cell3 = ateCell(2);
cell1.setCellValue("⾏号");
cell2.setCellValue("订单编号");
cell3.setCellValue("创建时间");
FileOutputStream outputInit = new FileOutputStream(path);
wb.write(outputInit);
outputInit.flush();
outputInit.close();
}
public static void saveExcelxlsx(String path, int i, String value, String time) throws Exception {
XSSFWorkbook wb = new XSSFWorkbook(new FileInputStream(path));
XSSFSheet sheet = wb.getSheet("sheet1");
//写订单数据
//⽤sheet对象创建⾏对象
XSSFRow row = ateRow(i + 1);
//构造数据
List<Object> list = new ArrayList<>();
list.add(i + 1);
list.add(value);
list.add(time);
int length = list.size();
for (int n = 0; n < length; n++) {
FileOutputStream output = new FileOutputStream(path);
//⽤⾏对象创建单元格对象Cell
Cell cell = ateCell(n);
//⽤cell对象读写。设置excel⼯作表值
cell.(n).toString());
wb.write(output);
output.flush();
output.close();
}
}
public static void main(String[] args) throws Exception {
for (int i = 0; i < 10; i++) {
HashMap<String, String> hashMap = tbOrderCreate();
Iterator it = hashMap.keySet().iterator();
String v = "";
String t = "";
while (it.hasNext()) {
v = it.next().toString();
t = (v);
}
if (!("".equals(v))) {
File file = new File(excelpath);
if (!ists()) {
createExcelxlsx(excelpath);
saveExcelxlsx(excelpath, i, v, t);                } else {
saveExcelxlsx(excelpath, i, v, t);                }
}
}
}