├── taobao ├── README.md ├── ErrorCode.java ├── TaoBaoResourceUtils.java ├── TaobaoSeleniumSlide.java ├── SlideTrailRecord.java ├── taobao.js ├── TaoBaoResource.java ├── TaobaoSeleniumSlideHelpUtils.java └── TaoBaoSeleniumUtils.java /taobao: -------------------------------------------------------------------------------- 1 | 淘宝滑块验证 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # TaobaoSeleniumSlideHelp 2 | 使用selenium暴力破解淘宝滑块验证部分 3 | 4 | 入口文件 TaobaoSeleniumSlide 5 | 6 | 本文仅供参考 7 | -------------------------------------------------------------------------------- /ErrorCode.java: -------------------------------------------------------------------------------- 1 | package com.justCopyBt.slide; 2 | 3 | import java.io.Serializable; 4 | 5 | public class ErrorCode implements Serializable { 6 | private static final long serialVersionUID = 1263178717333346398L; 7 | 8 | /***** 状态码 *****/ 9 | private int code; 10 | 11 | /***** 异常信息 *****/ 12 | private String msg; 13 | 14 | public ErrorCode(int code, String msg) { 15 | this.code = code; 16 | this.msg = msg; 17 | } 18 | 19 | public static final ErrorCode TAOBAO_SELENIUM_FAILED = new ErrorCode(100000, "初始化失败,请重新登录");//初始化driver失败 20 | 21 | public static final ErrorCode TAOBAO_LOGIN_SLIDE_FAIL = new ErrorCode(100002, "尝试登录失败,请稍候重试"); 22 | 23 | } -------------------------------------------------------------------------------- /TaoBaoResourceUtils.java: -------------------------------------------------------------------------------- 1 | package com.justCopyBt.slide; 2 | /** 3 | * 淘宝selenium滑块元素操作辅助类 4 | * @date 2019/07/05 5 | */ 6 | public class TaoBaoResourceUtils { 7 | 8 | //随机乱点的元素 9 | /** 10 | * 登录FORM 11 | */ 12 | public static final TaoBaoResource CLICK_LOGIN_FORM = TaoBaoResource.Builder.Builder().setId("J_Form").setWaitSeconds(1).build(); 13 | /** 14 | * 登录名的label 15 | */ 16 | public static final TaoBaoResource CLICK_LOGIN_NAME_LABEL = TaoBaoResource.Builder.Builder().setCssSelector(".username-field>label").setWaitSeconds(1).build(); 17 | /** 18 | * 密码的label 19 | */ 20 | public static final TaoBaoResource CLICK_PASSWORD_LABEL = TaoBaoResource.Builder.Builder().setCssSelector(".pwd-field>label").setWaitSeconds(1).build(); 21 | /** 22 | * 登录页的图片 23 | */ 24 | public static final TaoBaoResource CLICK_LOGIN_ADLINK_DIV = TaoBaoResource.Builder.Builder().setClassName("login-adlink").setWaitSeconds(1).build(); 25 | /** 26 | * 登录页的背景 27 | */ 28 | public static final TaoBaoResource CLICK_LOGIN_NEWBG_DIV = TaoBaoResource.Builder.Builder().setClassName("login-newbg").setWaitSeconds(1).build(); 29 | 30 | /** 31 | * 验证通过 32 | */ 33 | public static final TaoBaoResource LOGIN_SLIDE_NEED = TaoBaoResource.Builder.Builder().setClassName("nc-lang-cnt").setExpectDesc("请按住滑块").setWaitSeconds(5).build(); 34 | 35 | /** 36 | * 滑动块儿按钮 37 | */ 38 | public static final TaoBaoResource LOGIN_SLIDE_BUTTON = TaoBaoResource.Builder.Builder().setId("nc_1_n1z").setWaitSeconds(15).build(); 39 | 40 | /** 41 | * 验证通过 42 | */ 43 | public static final TaoBaoResource LOGIN_SLIDE_SUCCESS = TaoBaoResource.Builder.Builder().setId("nocaptcha").setExpectDesc("验证通过").setWaitSeconds(5).build(); 44 | /** 45 | * 验证失败 46 | */ 47 | public static final TaoBaoResource LOGIN_SLIDE_FAIL = TaoBaoResource.Builder.Builder().setId("nocaptcha").setExpectDesc("出错了").setWaitSeconds(5).build(); 48 | 49 | } 50 | -------------------------------------------------------------------------------- /TaobaoSeleniumSlide.java: -------------------------------------------------------------------------------- 1 | package com.justCopyBt.slide; 2 | import org.apache.commons.lang3.StringUtils; 3 | import org.openqa.selenium.WebElement; 4 | import org.openqa.selenium.interactions.Actions; 5 | import org.openqa.selenium.remote.RemoteWebDriver; 6 | import org.slf4j.Logger; 7 | import org.slf4j.LoggerFactory; 8 | 9 | 10 | /** 11 | * 淘宝selenium滑块入口 12 | * @date 2019/07/05 13 | */ 14 | 15 | public class TaobaoSeleniumSlide { 16 | 17 | private static String LOG_PREFIX = "TAOBAO SELENIUM PC ACCOUNT LOGIN SLIDE"; 18 | 19 | //不需要滑块 20 | private static final ErrorCode TAOBAO_LOGIN_NOT_NEED_SLIDE = new ErrorCode(200001, "不需要滑块"); 21 | //滑动成功 22 | private static final ErrorCode TAOBAO_LOGIN_SLIDE_SUCCESS = new ErrorCode(200002, "滑动成功"); 23 | 24 | 25 | private static final Logger logger = LoggerFactory.getLogger(TaobaoSeleniumSlide.class); 26 | 27 | 28 | /** 29 | * 检测是否存在滑动验证码并且提交 30 | * 31 | * @param remoteWebDriver 32 | * @return 33 | */ 34 | private ErrorCode checkHaveSlideAndSubmit(RemoteWebDriver remoteWebDriver) { 35 | 36 | //先随机获取一下滑动验证使用的方式 37 | String slideType = TaobaoSeleniumSlideHelpUtils.randomSlideType(); 38 | SlideTrailRecord slideTrailRecord = TaobaoSeleniumSlideHelpUtils.getSlideTrailRecord(); 39 | if (slideTrailRecord == null) { 40 | logger.info("{}, 查询滑动验证轨迹出错", LOG_PREFIX); 41 | return ErrorCode.TAOBAO_SELENIUM_FAILED; 42 | } 43 | //判断是否出现了滑动验证,并且需要进行滑动通过,如果不通过,直接提示失败 44 | ErrorCode returnErrorCode = TAOBAO_LOGIN_NOT_NEED_SLIDE; 45 | if (TaobaoSeleniumSlideHelpUtils.haveSlideCheck(remoteWebDriver)) { 46 | //随机乱移动鼠标 47 | TaobaoSeleniumSlideHelpUtils.randomClickElement(remoteWebDriver); 48 | boolean slideResult = TaobaoSeleniumSlideHelpUtils.slideCheck(remoteWebDriver, slideType, slideTrailRecord.getSlideTrail()); 49 | //滑动失败!!! 50 | returnErrorCode = slideResult ? TAOBAO_LOGIN_SLIDE_SUCCESS : ErrorCode.TAOBAO_LOGIN_SLIDE_FAIL; 51 | } 52 | return returnErrorCode; 53 | } 54 | 55 | } 56 | -------------------------------------------------------------------------------- /SlideTrailRecord.java: -------------------------------------------------------------------------------- 1 | package com.justCopyBt.slide; 2 | 3 | import org.apache.commons.lang3.StringUtils; 4 | import org.apache.poi.sl.usermodel.Slide; 5 | 6 | import javax.persistence.*; 7 | import javax.ws.rs.FormParam; 8 | 9 | /** 10 | * 滑动轨迹 11 | * @date 2019/07/05 12 | */ 13 | @Entity 14 | @Table(name = "slide_trail_record") 15 | public class SlideTrailRecord { 16 | 17 | @Id 18 | @GeneratedValue(strategy = GenerationType.AUTO) 19 | @Column(name = "id") 20 | @FormParam("id") 21 | private Integer id; 22 | /** 23 | * 所属网站 24 | */ 25 | @FormParam("site") 26 | @Column(name = "site") 27 | private String site; 28 | 29 | /** 30 | * 滑动距离 31 | */ 32 | @Column(name = "distance") 33 | @FormParam("distance") 34 | private Integer distance; 35 | 36 | /** 37 | * 滑动轨迹 38 | */ 39 | @FormParam("slide_trail") 40 | @Column(name = "slide_trail") 41 | private String slideTrail; 42 | 43 | /** 44 | * 轨迹状态 45 | */ 46 | @FormParam("status") 47 | @Column(name = "status" ,columnDefinition="INT default 1") 48 | private Integer status = 1; 49 | 50 | 51 | public Integer getId() { 52 | return id; 53 | } 54 | 55 | public void setId(Integer id) { 56 | this.id = id; 57 | } 58 | 59 | public String getSite() { 60 | return site; 61 | } 62 | 63 | public void setSite(String site) { 64 | this.site = site; 65 | } 66 | 67 | public Integer getDistance() { 68 | return distance; 69 | } 70 | 71 | public void setDistance(Integer distance) { 72 | this.distance = distance; 73 | } 74 | 75 | public String getSlideTrail() { 76 | return slideTrail; 77 | } 78 | 79 | public void setSlideTrail(String slideTrail) { 80 | this.slideTrail = slideTrail; 81 | } 82 | 83 | public Integer getStatus() { 84 | return status; 85 | } 86 | 87 | public void setStatus(Integer status) { 88 | this.status = status; 89 | } 90 | 91 | /** 92 | * 从其他的轨迹合并信息到本轨迹 93 | * @param slideTrailRecord 94 | */ 95 | public void mergeSlideTrailInfo(SlideTrailRecord slideTrailRecord){ 96 | if(StringUtils.isNotBlank(slideTrailRecord.getSite())) { 97 | this.setSite(slideTrailRecord.getSite()); 98 | } 99 | if(slideTrailRecord.getDistance()!=null && slideTrailRecord.getDistance()>0) { 100 | this.setDistance(slideTrailRecord.getDistance()); 101 | } 102 | if(StringUtils.isNotBlank(slideTrailRecord.getSlideTrail())) { 103 | this.setSlideTrail(slideTrailRecord.getSlideTrail()); 104 | } 105 | if(slideTrailRecord.getStatus() !=null) { 106 | this.setStatus(slideTrailRecord.getStatus()); 107 | } 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /taobao.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | var selectors = {}; 3 | //滑动的按钮 4 | selectors.pull_button = "#nc_1_n1z"; 5 | //默认的起始坐标 6 | var xClientStart = 103; 7 | var yClientStart = 223; 8 | 9 | var SlideBot = function () { 10 | 11 | }; 12 | 13 | SlideBot.prototype.loginSlide = function () { 14 | //获取起始坐标 15 | getBtnClientRect(); 16 | //执行滑动 17 | setTimeout(function(){ 18 | setTimeout(function(){ 19 | startPlay(selectors.pull_button) 20 | }, 500); 21 | }, 500); 22 | } 23 | 24 | //滑动轨迹 25 | var moveList = MOVE_LIST_FORMAT; 26 | 27 | //获取滑块按钮所在位置 28 | function getBtnClientRect(){ 29 | //得到滑动按钮的位置 30 | var button = document.querySelector(selectors.pull_button); 31 | var button_rect = button.getBoundingClientRect(); 32 | // console.log("current rect: " + JSON.stringify(button_rect)); 33 | //得到滑动按钮的坐标 34 | xClientStart = (button_rect.top + button_rect.bottom) / 2; 35 | yClientStart = (button_rect.left + button_rect.right) / 2; 36 | // console.log("current point: " + xClientStart + ", " + yClientStart); 37 | } 38 | 39 | //执行滑动 40 | function startPlay(selector) { 41 | var xstart = xClientStart + Math.random() * 10; 42 | var ystart = yClientStart + Math.random() * 10; 43 | 44 | var fireOnThis = document.querySelector(selector); 45 | 46 | //执行鼠标按下事件 47 | var evObj = document.createEvent('MouseEvents'); 48 | evObj.initMouseEvent('mousedown', true, true, window, 1, xstart, ystart, xstart, ystart, false, false, true, false, 0, null); 49 | evObj.isTrusted = true; 50 | fireOnThis.dispatchEvent(evObj); 51 | // console.log("moudown points: " + xstart + ", " + ystart); 52 | 53 | var currentx = xstart; 54 | var currenty = ystart; 55 | for(var i = 0; i < moveList.length; i++){ 56 | var movearr = moveList[i]; 57 | (function(movex,movey,sleeptime){ 58 | setTimeout(function(){ 59 | //得到下次坐标 60 | currentx = currentx + movex; 61 | currenty = currenty + movey; 62 | 63 | var x = currentx; 64 | var y = currenty; 65 | // console.log("current points: " + x + ", " + y); 66 | //执行移动 67 | //var evObj = document.createEvent('MouseEvents'); 68 | evObj.initMouseEvent('mousemove', true, true, window, 1, x, y, x, y, false, false, true, false, 0, null); 69 | evObj.isTrusted = true; 70 | fireOnThis.dispatchEvent(evObj); 71 | }, sleeptime); 72 | })(movearr[0],movearr[1],movearr[2]) 73 | } 74 | } 75 | 76 | window.slide_bot = new SlideBot(); 77 | slide_bot.loginSlide(); 78 | 79 | })(); -------------------------------------------------------------------------------- /TaoBaoResource.java: -------------------------------------------------------------------------------- 1 | package com.justCopyBt.slide; 2 | import net.sf.json.JSONObject; 3 | 4 | import java.lang.reflect.Field; 5 | import java.lang.reflect.Method; 6 | import java.util.ArrayList; 7 | import java.util.HashMap; 8 | import java.util.List; 9 | import java.util.Map; 10 | 11 | /** 12 | * 淘宝SELENIUM资源类,方便处理 13 | * @date 2019/07/05 14 | */ 15 | public class TaoBaoResource { 16 | 17 | private String id; 18 | 19 | private String name; 20 | 21 | private String className; 22 | 23 | private String cssSelector; 24 | 25 | private String xpath; 26 | 27 | private String text; 28 | 29 | private String url; 30 | 31 | private Integer waitSeconds = 10; 32 | 33 | private String expectDesc; 34 | 35 | private Boolean textBlank; 36 | 37 | public Boolean getTextBlank() { 38 | return textBlank; 39 | } 40 | 41 | public void setTextBlank(Boolean textBlank) { 42 | this.textBlank = textBlank; 43 | } 44 | 45 | public String getId() { 46 | return id; 47 | } 48 | 49 | public void setId(String id) { 50 | this.id = id; 51 | } 52 | 53 | public String getName() { 54 | return name; 55 | } 56 | 57 | public void setName(String name) { 58 | this.name = name; 59 | } 60 | 61 | public String getClassName() { 62 | return className; 63 | } 64 | 65 | public void setClassName(String className) { 66 | this.className = className; 67 | } 68 | 69 | public String getCssSelector() { 70 | return cssSelector; 71 | } 72 | 73 | public void setCssSelector(String cssSelector) { 74 | this.cssSelector = cssSelector; 75 | } 76 | 77 | public String getText() { 78 | return text; 79 | } 80 | 81 | public void setText(String text) { 82 | this.text = text; 83 | } 84 | 85 | public Integer getWaitSeconds() { 86 | return waitSeconds; 87 | } 88 | 89 | public void setWaitSeconds(Integer waitSeconds) { 90 | this.waitSeconds = waitSeconds; 91 | } 92 | 93 | public String getXpath() { 94 | return xpath; 95 | } 96 | 97 | public void setXpath(String xpath) { 98 | this.xpath = xpath; 99 | } 100 | 101 | public String getUrl() { 102 | return url; 103 | } 104 | 105 | public void setUrl(String url) { 106 | this.url = url; 107 | } 108 | 109 | public String getExpectDesc() { 110 | return expectDesc; 111 | } 112 | 113 | public void setExpectDesc(String expectDesc) { 114 | this.expectDesc = expectDesc; 115 | } 116 | 117 | 118 | 119 | 120 | 121 | @Override 122 | public String toString(){ 123 | Map strMap = new HashMap<>(); 124 | Field[] fields = this.getClass().getDeclaredFields(); 125 | for (Field field : fields) { 126 | String methodName = field.getName(); 127 | try { 128 | Method method = this.getClass().getMethod("get" + methodName.substring(0, 1).toUpperCase() + methodName.substring(1, methodName.length())); 129 | Object value = method.invoke(this); 130 | if(value!=null){ 131 | strMap.put(methodName,String.valueOf(value)); 132 | } 133 | }catch (Exception ex){ 134 | 135 | } 136 | } 137 | return JSONObject.fromObject(strMap).toString(); 138 | } 139 | 140 | public static final class Builder { 141 | 142 | private String id; 143 | 144 | private String className; 145 | 146 | private String name; 147 | 148 | private String url; 149 | 150 | private String cssSelector; 151 | 152 | private String xpath; 153 | 154 | private String text; 155 | 156 | private Integer waitSeconds = 10; 157 | 158 | private String expectDesc; 159 | 160 | private Boolean textBlank; 161 | 162 | private Builder() { 163 | } 164 | 165 | public static Builder Builder() { 166 | return new Builder(); 167 | } 168 | 169 | /** 170 | * css id 171 | * 172 | * @param id 173 | * @return 174 | */ 175 | public Builder setId(String id) { 176 | this.id = id; 177 | return this; 178 | } 179 | 180 | /** 181 | * css name 182 | * 183 | * @param name 184 | * @return 185 | */ 186 | public Builder setName(String name) { 187 | this.name = name; 188 | return this; 189 | } 190 | 191 | /** 192 | * current url 193 | * 194 | * @param url 195 | * @return 196 | */ 197 | public Builder setUrl(String url) { 198 | this.url = url; 199 | return this; 200 | } 201 | 202 | /** 203 | * class_name 204 | * 205 | * @param className 206 | * @return 207 | */ 208 | public Builder setClassName(String className) { 209 | this.className = className; 210 | return this; 211 | } 212 | 213 | /** 214 | * css_selector 215 | * 216 | * @param cssSelector 217 | * @return 218 | */ 219 | public Builder setCssSelector(String cssSelector) { 220 | this.cssSelector = cssSelector; 221 | return this; 222 | } 223 | 224 | /** 225 | * xpath 226 | * 227 | * @param xpath 228 | * @return 229 | */ 230 | public Builder setXpath(String xpath) { 231 | this.xpath = xpath; 232 | return this; 233 | } 234 | 235 | /** 236 | * text 237 | * 238 | * @param text 239 | * @return 240 | */ 241 | public Builder setText(String text) { 242 | this.text = text; 243 | return this; 244 | } 245 | 246 | /** 247 | * 等待时间 248 | * 249 | * @param waitSeconds 250 | * @return 251 | */ 252 | public Builder setWaitSeconds(int waitSeconds) { 253 | this.waitSeconds = waitSeconds; 254 | return this; 255 | } 256 | 257 | /** 258 | * 设置期望的内容 259 | * 260 | * @param expectDesc 261 | * @return 262 | */ 263 | public Builder setExpectDesc(String expectDesc) { 264 | this.expectDesc = expectDesc; 265 | return this; 266 | } 267 | 268 | public Builder setTextBlank(Boolean textBlank){ 269 | this.textBlank = textBlank; 270 | return this; 271 | } 272 | 273 | public TaoBaoResource build() { 274 | TaoBaoResource taoBaoResource = new TaoBaoResource(); 275 | taoBaoResource.setId(this.id); 276 | taoBaoResource.setName(this.name); 277 | taoBaoResource.setClassName(this.className); 278 | taoBaoResource.setCssSelector(this.cssSelector); 279 | taoBaoResource.setXpath(this.xpath); 280 | taoBaoResource.setText(this.text); 281 | taoBaoResource.setWaitSeconds(this.waitSeconds); 282 | taoBaoResource.setUrl(this.url); 283 | taoBaoResource.setExpectDesc(this.expectDesc); 284 | taoBaoResource.setTextBlank(this.textBlank); 285 | return taoBaoResource; 286 | } 287 | } 288 | 289 | 290 | } 291 | -------------------------------------------------------------------------------- /TaobaoSeleniumSlideHelpUtils.java: -------------------------------------------------------------------------------- 1 | package com.justCopyBt.slide; 2 | 3 | import net.sf.json.JSONArray; 4 | import org.apache.commons.lang3.StringUtils; 5 | import org.openqa.selenium.WebElement; 6 | import org.openqa.selenium.interactions.Actions; 7 | import org.openqa.selenium.remote.RemoteWebDriver; 8 | import org.slf4j.Logger; 9 | import org.slf4j.LoggerFactory; 10 | 11 | import java.util.*; 12 | 13 | /** 14 | * 淘宝selenium滑块操作公共类 15 | * @date 2019/07/05 16 | */ 17 | 18 | public class TaobaoSeleniumSlideHelpUtils { 19 | 20 | 21 | private static Random random = new Random(); 22 | 23 | protected static final Logger logger = LoggerFactory.getLogger(TaobaoSeleniumSlideHelpUtils.class); 24 | 25 | private static String loginJs = ""; 26 | 27 | static { 28 | try { 29 | loginJs = FileUtils.readFileByClassPath("taobao.js"); 30 | } catch (Exception ex) { 31 | logger.info("load taobao login js error", ex); 32 | } 33 | } 34 | 35 | /** 36 | * 淘宝的需要滑动的距离,是固定的 37 | */ 38 | private static int SLIDE_DISTANCE = 256; 39 | 40 | private static List clickResourceList = new ArrayList() { 41 | { 42 | add(TaoBaoResourceUtils.CLICK_LOGIN_ADLINK_DIV); 43 | add(TaoBaoResourceUtils.CLICK_LOGIN_FORM); 44 | add(TaoBaoResourceUtils.CLICK_LOGIN_NAME_LABEL); 45 | add(TaoBaoResourceUtils.CLICK_LOGIN_NEWBG_DIV); 46 | add(TaoBaoResourceUtils.CLICK_PASSWORD_LABEL); 47 | } 48 | }; 49 | 50 | private static final String SLIDE_BY_JS = "slide_by_js"; 51 | private static final String SLIDE_BY_DRIVER = "slide_by_driver"; 52 | 53 | public static Map slideByJsTrailMap = new HashMap() {{ 54 | put(9, "[[1,0,399],[3,-1,6],[4,-1,7],[3,-1,7],[5,-1,7],[6,-1,7],[5,-1,7],[6,-1,6],[6,0,7],[5,0,6],[3,0,7],[3,0,5],[6,0,8],[3,0,6],[4,0,7],[3,0,8],[4,0,7],[4,1,7],[5,0,7],[5,1,6],[7,0,7],[7,1,8],[9,0,6],[8,1,4],[8,1,7],[8,0,7],[8,1,7],[7,1,7],[5,1,7],[5,0,7],[4,0,7],[3,1,7],[2,0,7],[2,0,7],[2,0,5],[3,1,7],[3,0,7],[2,0,7],[3,0,7],[3,0,7],[4,0,7],[5,0,7],[4,0,7],[4,0,6],[6,-1,7],[5,-1,7],[4,-1,5],[6,-1,7],[4,-1,7],[3,-1,7],[4,-1,7],[3,0,7],[2,0,7],[2,-1,7],[2,0,7],[2,0,7],[3,0,8],[3,0,4],[2,0,7],[3,0,7],[3,0,7],[3,0,7],[3,-1,7],[3,-1,7],[5,-1,7],[3,0,7],[3,-1,7],[3,-1,7],[3,0,5],[3,-1,6],[2,0,9],[1,0,13],[1,0,125],[0,1,7],[1,0,7]]"); 55 | }}; 56 | 57 | /** 58 | * 滑动比例 59 | */ 60 | private static Map slideTypeMap = new HashMap() {{ 61 | put(SLIDE_BY_JS, 100); 62 | put(SLIDE_BY_DRIVER, 0); 63 | }}; 64 | 65 | /** 66 | * 随机获取滑动方式 67 | * 68 | * @return 69 | */ 70 | public static String randomSlideType() { 71 | Integer randomResult = random.nextInt(100); 72 | String slideType = ""; 73 | Integer currentPercent = 0; 74 | for (Map.Entry entry : slideTypeMap.entrySet()) { 75 | currentPercent += entry.getValue(); 76 | if (randomResult < currentPercent) { 77 | slideType = entry.getKey(); 78 | break; 79 | } 80 | } 81 | return slideType; 82 | } 83 | 84 | public static SlideTrailRecord getSlideTrailRecord(){ 85 | SlideTrailRecord slideTrailRecord = new SlideTrailRecord(); 86 | Integer currentIndex = randomSlideTrailIndex(); 87 | slideTrailRecord.setId(currentIndex); 88 | slideTrailRecord.setSlideTrail(slideByJsTrailMap.get(currentIndex)); 89 | return slideTrailRecord; 90 | } 91 | 92 | /** 93 | * 获取轨迹的index 94 | * 95 | * @return 96 | */ 97 | public static Integer randomSlideTrailIndex() { 98 | List trailIndexList = new ArrayList<>(); 99 | for (Map.Entry entry : TaobaoSeleniumSlideHelpUtils.slideByJsTrailMap.entrySet()) { 100 | trailIndexList.add(entry.getKey()); 101 | } 102 | return trailIndexList.get(random.nextInt(trailIndexList.size())); 103 | } 104 | 105 | /** 106 | * 随机乱移动鼠标 107 | * 108 | * @param remoteWebDriver 109 | */ 110 | public static void randomClickElement(RemoteWebDriver remoteWebDriver) { 111 | TaoBaoResource taoBaoResource = clickResourceList.get(random.nextInt(clickResourceList.size())); 112 | try { 113 | Actions actions = new Actions(remoteWebDriver); 114 | WebElement webElement = TaoBaoSeleniumUtils.waitElement(remoteWebDriver, taoBaoResource); 115 | actions.moveToElement(webElement).perform(); 116 | actions.release(); 117 | } catch (Exception ex) { 118 | logger.info("随机移动鼠标到元素异常, resource:{}", taoBaoResource.toString(), ex); 119 | } 120 | } 121 | 122 | /** 123 | * 滑动验证 124 | * 125 | * @param remoteWebDriver 126 | * @return 127 | */ 128 | public static boolean slideCheck(RemoteWebDriver remoteWebDriver, String slideType, String slideTrail) { 129 | logger.info("开始滑动, slide_type:{}", slideType); 130 | long startTime = System.currentTimeMillis(); 131 | remoteWebDriver.executeScript("window.scrollTo(document.body.scrollWidth, 0)"); 132 | if (SLIDE_BY_JS.equals(slideType)) { 133 | logger.info("开始通过JS滑动, slide_trail:{}", slideTrail); 134 | TaobaoSeleniumSlideHelpUtils.slideByJs(remoteWebDriver, slideTrail); 135 | } else { 136 | TaobaoSeleniumSlideHelpUtils.slideByDriver(remoteWebDriver, slideTrail); 137 | } 138 | 139 | logger.info("执行滑动轨迹完成, slide_type:{}, time_span:{}", slideType, (System.currentTimeMillis() - startTime)); 140 | boolean slideResult = false; 141 | logger.info("开始检测滑动结果, slide_type:{}", slideType); 142 | TaoBaoResource sliderSuccess = TaoBaoSeleniumUtils.getExistElement(remoteWebDriver, TaoBaoResourceUtils.LOGIN_SLIDE_SUCCESS.getWaitSeconds(), 143 | //滑动成功 144 | TaoBaoResourceUtils.LOGIN_SLIDE_SUCCESS, 145 | //滑动失败 146 | TaoBaoResourceUtils.LOGIN_SLIDE_FAIL 147 | ); 148 | if (sliderSuccess != null && sliderSuccess.equals(TaoBaoResourceUtils.LOGIN_SLIDE_SUCCESS)) { 149 | slideResult = true; 150 | } 151 | String logTips = slideResult ? "成功" : "失败"; 152 | logger.info("滑动验证{}, slide_type:{}", logTips, slideType); 153 | return slideResult; 154 | } 155 | 156 | /** 157 | * 根据Js执行滑动 158 | * 159 | * @param remoteWebDriver 160 | */ 161 | private static void slideByJs(RemoteWebDriver remoteWebDriver, String slideTrailStr) { 162 | String slideJs = loginJs.replace("MOVE_LIST_FORMAT", slideTrailStr); 163 | remoteWebDriver.executeScript(slideJs); // 执行登陆js 164 | CommonUtil.sleep(800); 165 | } 166 | 167 | private static void slideByDriver(RemoteWebDriver remoteWebDriver, String slideTrail) { 168 | Actions actions = new Actions(remoteWebDriver); 169 | WebElement sliderButton = TaoBaoSeleniumUtils.waitElementUntil(remoteWebDriver, TaoBaoResource.LOGIN_SLIDE_BUTTON); 170 | actions.moveToElement(sliderButton).perform(); 171 | actions.clickAndHold().perform(); 172 | //生成滑动轨迹 173 | List slideMoveObjects; 174 | if (SLIDE_BY_DRIVER.equals(slideTrail)) { 175 | slideMoveObjects = TaobaoSeleniumSlideHelpUtils.getMoveDistance(SLIDE_DISTANCE); 176 | } else { 177 | slideMoveObjects = TaobaoSeleniumSlideHelpUtils.getMoveDistance(SLIDE_DISTANCE); 178 | } 179 | logger.info("生成滑动轨迹成功 开始执行滑动轨迹, slide_type:slide_by_driver, slide:{}", JSONArray.fromObject(slideMoveObjects).toString()); 180 | //执行滑动操作 181 | TaobaoSeleniumSlideHelpUtils.slide(actions, slideMoveObjects); 182 | actions.release(); 183 | } 184 | 185 | /** 186 | * 获取提交的滑动轨迹 187 | * 1、按住 188 | * 2、一口气直接拖到底 189 | * 3、拖回来一些 190 | * 191 | * @param slideDistance 192 | * @return 193 | */ 194 | private static List getMoveDistance(Integer slideDistance) { 195 | List moveDistanceList = new ArrayList<>(); 196 | //第一次移动坐标点 197 | int moveX = 0, moveY = 0, sleepTime = 120; 198 | //按住 199 | moveDistanceList.add(createMove(moveX, moveY, sleepTime)); 200 | 201 | //拖到底,还需要拖出去一些 202 | moveX = slideDistance + random.nextInt(5) + 2; 203 | moveY = random.nextInt(10) + 2; 204 | sleepTime = random.nextInt(30) + 50; 205 | moveDistanceList.add(createMove(moveX, moveY, sleepTime)); 206 | 207 | //拖回来一些, 这个一些可以在30-300 之间随机 208 | moveX = 20 + random.nextInt(slideDistance); 209 | moveY = 5 - random.nextInt(10); 210 | sleepTime = random.nextInt(100) + 150; 211 | moveDistanceList.add(createMove(-moveX, moveY, sleepTime)); 212 | return moveDistanceList; 213 | } 214 | 215 | /** 216 | * 进行滑动操作 217 | * 218 | * @param actions 219 | */ 220 | private static void slide(Actions actions, List slideMoveObjects) { 221 | for (Object[] moveObjects : slideMoveObjects) { 222 | Integer moveX = Integer.parseInt(String.valueOf(moveObjects[0])); 223 | Integer moveY = Integer.parseInt(String.valueOf(moveObjects[1])); 224 | Long sleepTime = Long.parseLong(String.valueOf(moveObjects[2])); 225 | logger.info("{}, 执行滑动, point:[{},{}], sleep_time:{}", moveX, moveY, sleepTime); 226 | actions.moveByOffset(moveX, moveY).perform(); 227 | CommonUtil.sleep(sleepTime); 228 | } 229 | } 230 | 231 | /** 232 | * 判断是否需要进行滑块验证 233 | * String styleStr = sliderGround.getAttribute("style"); 234 | * if (styleStr.contains("block")) { 235 | * haveSlideCheck = true; 236 | * } 237 | * 238 | * @param remoteWebDriver 239 | * @return 240 | */ 241 | public static boolean haveSlideCheck(RemoteWebDriver remoteWebDriver) { 242 | boolean haveSlideCheck = false; 243 | WebElement sliderGround = TaoBaoSeleniumUtils.waitElementUntil(remoteWebDriver, TaoBaoResourceUtils.LOGIN_SLIDE_NEED); 244 | if (sliderGround != null) { 245 | String slideText = sliderGround.getText(); 246 | if (StringUtils.isNotBlank(slideText) && slideText.contains(TaoBaoResourceUtils.LOGIN_SLIDE_NEED.getExpectDesc())) { 247 | haveSlideCheck = true; 248 | } 249 | } 250 | return haveSlideCheck; 251 | } 252 | 253 | /** 254 | * 创建移动距离对象 255 | * @param X 256 | * @param Y 257 | * @param time 258 | * @return 259 | */ 260 | public static Object[] createMove(int X,int Y,long time){ 261 | return new Object[]{X,Y,time}; 262 | } 263 | } 264 | -------------------------------------------------------------------------------- /TaoBaoSeleniumUtils.java: -------------------------------------------------------------------------------- 1 | package com.justCopyBt.slide; 2 | import net.sf.json.JSONObject; 3 | import org.apache.commons.lang3.StringUtils; 4 | import org.openqa.selenium.By; 5 | import org.openqa.selenium.OutputType; 6 | import org.openqa.selenium.WebElement; 7 | import org.openqa.selenium.interactions.Actions; 8 | import org.openqa.selenium.remote.RemoteWebDriver; 9 | import org.openqa.selenium.support.ui.ExpectedConditions; 10 | import org.openqa.selenium.support.ui.WebDriverWait; 11 | import org.slf4j.Logger; 12 | import org.slf4j.LoggerFactory; 13 | 14 | import java.util.ArrayList; 15 | import java.util.List; 16 | import java.util.Random; 17 | 18 | /** 19 | * 淘宝selenium通用帮助类 20 | * 执行监测元素是否存在、点击、输入、滑动等操作 21 | * @date 2019/07/05 22 | */ 23 | public class TaoBaoSeleniumUtils { 24 | 25 | /** 26 | * 日志记录 27 | */ 28 | private static final Logger logger = LoggerFactory.getLogger(TaoBaoSeleniumUtils.class); 29 | /** 30 | * 1秒的毫秒数 31 | */ 32 | private static Integer MILLISECEND = 1000; 33 | 34 | /** 35 | * 获取UA使用的随机 36 | */ 37 | private static Random random = new Random(); 38 | 39 | /** 40 | * 输入参数 41 | * 默认逐个输入 42 | * 43 | * @param action 当前活动页面 44 | * @param inputValue 要输入的值 45 | * @param sleepMillis 休眠时间,会随机休眠 46 | */ 47 | public static void sendKeys(Actions action, String inputValue, int sleepMillis) { 48 | for (int i = 0; i < inputValue.length(); i++) { 49 | action.sendKeys(String.valueOf(inputValue.charAt(i))).perform(); 50 | Util.sleep(random.nextInt(sleepMillis)); 51 | } 52 | } 53 | 54 | /** 55 | * 等待某个存在的元素出现 56 | * 57 | * @param driver 58 | * @param taoBaoResources 59 | * @return 60 | */ 61 | public static TaoBaoResource getExistElement(RemoteWebDriver driver, Integer waitSeconds, TaoBaoResource... taoBaoResources) { 62 | long startTime = System.currentTimeMillis(); 63 | while (System.currentTimeMillis() - startTime < waitSeconds * MILLISECEND) { 64 | //挨个监测是否存在 65 | for (TaoBaoResource taoBaoResource : taoBaoResources) { 66 | //通过url监测 67 | try { 68 | if (StringUtils.isNotBlank(taoBaoResource.getUrl())) { 69 | if (driver.getCurrentUrl().startsWith(taoBaoResource.getUrl())) { 70 | return taoBaoResource; 71 | } 72 | continue; 73 | } 74 | List webElementsLists = getWebElements(driver, taoBaoResource); 75 | //如果存在就直接返回 76 | if (webElementsLists != null && webElementsLists.size() > 0) { 77 | //判断元素的text是否为空 78 | if (taoBaoResource.getTextBlank() != null) { 79 | for (WebElement webElement : webElementsLists) { 80 | if (StringUtils.isBlank(webElement.getText()) == taoBaoResource.getTextBlank()) { 81 | return taoBaoResource; 82 | } 83 | } 84 | continue; 85 | } 86 | // 判断getExpectDesc是否为空,如果为空则continue 87 | if (StringUtils.isNotBlank(taoBaoResource.getExpectDesc())) { 88 | for (WebElement webElement : webElementsLists) { 89 | if (webElement.getText().contains(taoBaoResource.getExpectDesc())) { 90 | return taoBaoResource; 91 | } 92 | } 93 | continue; 94 | } 95 | return taoBaoResource; 96 | } 97 | } catch (Exception ex) { 98 | logger.warn("getExistElement exception,element:{},message:{}", taoBaoResource.toString(), ex.getMessage(), ex); 99 | } 100 | } 101 | CommonUtil.sleep(200); 102 | } 103 | return null; 104 | } 105 | 106 | /** 107 | * 等待元素出现并执行点击操作 108 | * 109 | * @param action 110 | * @param driver 111 | * @param taoBaoResource 112 | * @return 113 | */ 114 | public static WebElement waitElementClick(Actions action, RemoteWebDriver driver, TaoBaoResource taoBaoResource) { 115 | WebElement webElement = waitElement(driver, taoBaoResource); 116 | if (null != webElement) { 117 | action.moveToElement(webElement).perform(); 118 | action.click(webElement).perform(); 119 | } 120 | return webElement; 121 | } 122 | 123 | /** 124 | * 等待元素出现并执行点击操作 125 | * 126 | * @param driver 127 | * @param taoBaoResource 128 | * @return 129 | */ 130 | public static WebElement waitElementClick(RemoteWebDriver driver, TaoBaoResource taoBaoResource) { 131 | return waitElementClick(driver, taoBaoResource, taoBaoResource.getWaitSeconds()); 132 | } 133 | 134 | /** 135 | * 等待元素出现并执行点击操作 136 | * 137 | * @param driver 138 | * @param taoBaoResource 139 | * @return 140 | */ 141 | public static WebElement waitElementClick(RemoteWebDriver driver, TaoBaoResource taoBaoResource, Integer waitSeconds) { 142 | WebElement webElement = waitElement(driver, taoBaoResource, waitSeconds); 143 | if (null != webElement) { 144 | webElement.click(); 145 | } 146 | return webElement; 147 | } 148 | 149 | /** 150 | * driver在超时时间内等待某元素加载出现 151 | * 152 | * @param driver 153 | * @return 找到则返回元素节点,未找到则抛出相关异常 154 | */ 155 | public static WebElement waitElement(RemoteWebDriver driver, TaoBaoResource taobaoResource) { 156 | return waitElement(driver, taobaoResource, taobaoResource.getWaitSeconds()); 157 | } 158 | 159 | /** 160 | * driver在超时时间内等待某元素加载出现 161 | * 162 | * @param driver 163 | * @return 找到则返回元素节点,未找到则抛出相关异常 164 | */ 165 | public static WebElement waitElement(RemoteWebDriver driver, TaoBaoResource taobaoResource, Integer waitSeconds) { 166 | List webElementsLists = waitElements(driver, taobaoResource, waitSeconds); 167 | if (webElementsLists != null && webElementsLists.size() > 0) { 168 | return webElementsLists.get(0); 169 | } 170 | return null; 171 | } 172 | 173 | /** 174 | * driver在超时时间内等待某元素加载出现 175 | * 176 | * @param driver 177 | * @return 找到则返回元素节点,未找到则抛出相关异常 178 | */ 179 | public static WebElement waitElementUntilEnableClick(RemoteWebDriver driver, TaoBaoResource taobaoResource) { 180 | WebElement webElement = waitElementUntil(driver, taobaoResource); 181 | if (webElement != null) { 182 | if (!webElement.isEnabled()) { 183 | CommonUtil.sleep(3000); 184 | webElement = waitElementUntilEnableClick(driver, taobaoResource); 185 | } else { 186 | webElement.click(); 187 | } 188 | } 189 | return webElement; 190 | } 191 | 192 | /** 193 | * driver在超时时间内等待某元素加载出现 194 | * 195 | * @param driver 196 | * @return 找到则返回元素节点,未找到则抛出相关异常 197 | */ 198 | public static WebElement waitElementUntilClick(RemoteWebDriver driver, TaoBaoResource taobaoResource) { 199 | WebElement webElement = waitElementUntil(driver, taobaoResource); 200 | if (webElement != null) { 201 | webElement.click(); 202 | } 203 | return webElement; 204 | } 205 | 206 | /** 207 | * driver在超时时间内等待某元素加载出现 208 | * 209 | * @param driver 210 | * @return 找到则返回元素节点,未找到则抛出相关异常 211 | */ 212 | public static WebElement waitElementUntilClick(Actions actions, RemoteWebDriver driver, TaoBaoResource taobaoResource) { 213 | WebElement webElement = waitElementUntil(driver, taobaoResource); 214 | if (webElement != null) { 215 | actions.moveToElement(webElement).perform(); 216 | actions.click(webElement).perform(); 217 | } 218 | return webElement; 219 | } 220 | 221 | /** 222 | * driver在超时时间内等待某元素加载出现 223 | * 224 | * @param driver 225 | * @return 找到则返回元素节点,未找到则抛出相关异常 226 | */ 227 | public static WebElement waitElementUntil(RemoteWebDriver driver, TaoBaoResource taobaoResource) { 228 | return waitElementUntil(driver, taobaoResource, taobaoResource.getWaitSeconds()); 229 | } 230 | 231 | /** 232 | * driver在超时时间内等待某元素加载出现 233 | * 234 | * @param driver 235 | * @return 找到则返回元素节点,未找到则抛出相关异常 236 | */ 237 | public static WebElement waitElementUntil(RemoteWebDriver driver, TaoBaoResource taobaoResource, int waitSeconds) { 238 | try { 239 | By by = getBy(taobaoResource); 240 | if (by != null) { 241 | new WebDriverWait(driver, waitSeconds).until( 242 | ExpectedConditions.presenceOfElementLocated(by)); 243 | return driver.findElement(by); 244 | } 245 | } catch (Exception ex) { 246 | } 247 | return null; 248 | } 249 | 250 | /** 251 | * driver在超时时间内等待某元素加载出现 252 | * 253 | * @param driver 254 | * @return 找到则返回元素节点,未找到则抛出相关异常 255 | */ 256 | public static List waitElements(RemoteWebDriver driver, TaoBaoResource taobaoResource) { 257 | return waitElements(driver, taobaoResource, taobaoResource.getWaitSeconds()); 258 | } 259 | 260 | /** 261 | * driver在超时时间内等待某元素加载出现 262 | * 263 | * @param driver 264 | * @return 找到则返回元素节点,未找到则抛出相关异常 265 | */ 266 | public static List waitElements(RemoteWebDriver driver, TaoBaoResource taobaoResource, Integer waitSeconds) { 267 | long startTime = System.currentTimeMillis(); 268 | while (System.currentTimeMillis() - startTime < waitSeconds * MILLISECEND) { 269 | List webElementsLists = getWebElements(driver, taobaoResource); 270 | if (webElementsLists != null && webElementsLists.size() > 0) { 271 | return webElementsLists; 272 | } 273 | CommonUtil.sleep(200); 274 | } 275 | return null; 276 | } 277 | 278 | /** 279 | * 根据选择方式获取元素 280 | * 281 | * @param driver 282 | * @param taobaoResource 283 | * @return 284 | */ 285 | public static List getWebElements(RemoteWebDriver driver, TaoBaoResource taobaoResource) { 286 | try { 287 | By by = getBy(taobaoResource); 288 | if (by != null) { 289 | return driver.findElements(by); 290 | } 291 | } catch (Exception ex) { 292 | } 293 | return null; 294 | } 295 | 296 | /** 297 | * 获取BY 298 | * 299 | * @param taobaoResource 300 | * @return 301 | */ 302 | public static By getBy(TaoBaoResource taobaoResource) { 303 | By by = null; 304 | if (StringUtils.isNotBlank(taobaoResource.getId())) { 305 | by = By.id(taobaoResource.getId()); 306 | } else if (StringUtils.isNotBlank(taobaoResource.getName())) { 307 | by = By.name(taobaoResource.getName()); 308 | } else if (StringUtils.isNotBlank(taobaoResource.getClassName())) { 309 | by = By.className(taobaoResource.getClassName()); 310 | } else if (StringUtils.isNotBlank(taobaoResource.getXpath())) { 311 | by = By.xpath(taobaoResource.getXpath()); 312 | } else if (StringUtils.isNotBlank(taobaoResource.getCssSelector())) { 313 | by = By.cssSelector(taobaoResource.getCssSelector()); 314 | } 315 | return by; 316 | } 317 | 318 | /*** 319 | * 等待页面加载完成 320 | * @param driver 321 | * @param times 次数 322 | * @return 323 | */ 324 | public static boolean waitForDocumentReady(final RemoteWebDriver driver, int times) { 325 | Object o; 326 | for (int i = 1; i <= times; i++) { 327 | o = driver.executeScript("return window.document.readyState"); 328 | if ("complete".equals(o)) { 329 | return true; 330 | } 331 | Util.sleep(1000); 332 | } 333 | return false; 334 | } 335 | 336 | /** 337 | * 后退 338 | * 339 | * @param remoteWebDriver 340 | */ 341 | public static boolean back(RemoteWebDriver remoteWebDriver) { 342 | try { 343 | remoteWebDriver.navigate().back(); 344 | return true; 345 | } catch (Exception ex) { 346 | logger.warn("没有监测兼容的错误提示:{}", ex.getMessage(), ex); 347 | } 348 | return false; 349 | } 350 | 351 | /** 352 | * 保存截图和当前源码 353 | * 354 | * @param remoteWebDriver 355 | * @return 356 | */ 357 | public static String takeScreenShotAndSaveHBase(RemoteWebDriver remoteWebDriver) { 358 | return takeScreenShotAndSaveHBase(remoteWebDriver, true); 359 | } 360 | 361 | /** 362 | * 保存截图和当前源码 363 | * 364 | * @param remoteWebDriver 365 | * @return 366 | */ 367 | public static String takeScreenShotAndSaveHBase(RemoteWebDriver remoteWebDriver, boolean isSaveScreen) { 368 | List hbaseKeyList = new ArrayList<>(); 369 | try { 370 | //保存源码 371 | String pageSource = remoteWebDriver.getPageSource(); 372 | if(isSaveScreen) { 373 | //保存截图 374 | byte[] bytes = remoteWebDriver.getScreenshotAs(OutputType.BYTES); 375 | String base64 = Base64Util.encode(bytes); 376 | } 377 | } catch (Exception ex) { 378 | logger.warn("保存当前页面源码和截屏异常, message:{}", ex.getMessage()); 379 | } 380 | } 381 | } --------------------------------------------------------------------------------