我正在参加中秋创意投稿大赛,详情请看:中秋创意投稿大赛
没有做梦
事情的开头是这样的,睡觉到凌晨可能被热醒了,作者习惯性的打开掘金APP,日常惯例,签到然后免费抽奖1次;
作者已经在掘金累积签到61次,连续打卡58次了,但是今天看见转盘的奖品不对啊,几个大奖都换成了中秋礼盒,
而且还不用解锁哦;这下作者懵了,掘金还能这么干。作者除去已消费的2w+钻,还有4.8+w钻呢,能不能抽中呢,
想想还有点小激动呢!
复制代码
要干大事
梦该醒了,怎么说作者也有4.8+w钻,就算是用手点,粗略算过200钻/次,都需要点200+次,OMG,手指都得断了,放弃吗?
但是中秋礼盒好诱人啊;突然想起了之前活动,各位掘友写过各种抽奖的文章,但是我想来点不一样的,我就静静的守着电脑,
它能够自动的抽奖,享受这个过程,尽管...咳咳...抽不中,那也无所谓了,希望掘金大大们高抬贵手,给个月饼盒子?
复制代码
准备工作
想法需要付诸行动,才能得到结果,不能空想!
环境部署(略)
这次作者使用java+selenium来实现这个有点刺激的自动化脚本;安装过程略了,它太基础了,不好意思拿出来凑字数^-^。
复制代码
脚本设计
本来想直接写成线性脚本,毕竟是一次性的嘛,不想花太多时间;不过最近在回顾java+selenium设计UI自动化测试框架;顺带来练练手,帮助自己提升和巩固记忆;所以采用PageFactory设计模式;为什么不用PO呢?因为它只是测试设计模式,它没有在任何工具和框架中有交互,纯粹的是selenium对业务的二次封装。
在开始写脚本之前,先抛出两个问题:
- 登录状态怎么解决,这一步要怎么绕过去?
- 业务逻辑的处理,比如动态计算抽取次数?
编码思路
第一个问题有多种解决办法,因为登录需要滑块验证码,所以可以写复杂的java代码来计算出图片凹凸的位置进行拖动,这个太麻烦,果断抛弃;
第二种,就是手动登录,然后把cookies信息保存下来;这个听起来so cool,但实现却有点繁琐<对比python而言>;所以思路是:自动获取cookie保存自动读取cookie
复制代码
第一步:自动打开浏览器获取cookie并保存到txt文件中,
第二步:读取txt中的cookie信息,再添加到driver的cookie中去并刷新页面即可。
- 代码实现:
public class IndexPage {
// 初始化驱动对象
private WebDriver driver;
// 初始化页面元素
public IndexPage(WebDriver driver) {
this.driver = driver;
PageFactory.initElements(driver, this);
// PageFactory.initElements(new AjaxElementLocatorFactory(driver,
// TIMEOUT), this);
}
@FindBy(css = ".first-line>button")
@CacheLookup
// 缓存
WebElement signIn;
@FindBy(xpath = "//div[@id='juejin']//div[contains(text(),'幸运抽奖')]")
@CacheLookup
List<WebElement> luckyLottery;
@FindBy(xpath = "//div[contains(text(),'次')]")
@CacheLookup
WebElement cost;
@FindBy(css = "span.value")
@CacheLookup
WebElement value;
@FindBy(xpath = "//button[text()='去看看']")
@CacheLookup
WebElement toSee;
@FindBy(xpath = "//button[text()='跳过']")
@CacheLookup
WebElement jumping;
/**
* 打开浏览器,中间有cookie操作,然后在刷新页面
*
* @throws Exception
*/
public void OpenBrowser() throws Exception {
driver.manage().window().maximize();
driver.manage().timeouts().implicitlyWait(15, TimeUnit.SECONDS);
driver.get(Constants.JjinUrl);
ReadCookieFromText();
driver.navigate().refresh();
}
public void CloseBrowser() {
if (driver != null) {
driver.quit();
}
}
/**
* 这一步比较复杂,就是需要判断是否有toast弹窗;然后在一步步的点击
*/
public void ClickSignIn() {
if (toSee.isDisplayed()) {
toSee.click();
jumping.click();
signIn.click();
} else {
signIn.click();
}
}
/**
* 点击幸运抽奖,因为它有一个隐藏元素,所以是list返回,然后再判断点击显示的那个元素
*/
public void ClickLottery() {
for (WebElement el : luckyLottery) {
if (el.isDisplayed()) {
el.click();
}
}
}
/**
* 需要判断,没有钻石则关闭浏览器
*/
public void ClickCostLottery() {
if (CheckIsLottery()) {
waitForClickable(cost);
cost.click();
} else {
driver.quit();
System.out.println("没有钻石抽奖了,关闭浏览器");
}
}
/**
* 写一个通用的等待元素方法
*
* @param element
* @throws Error
*/
private void waitForVisibility(WebElement element) throws Error {
new WebDriverWait(driver, 30).until(ExpectedConditions
.visibilityOf(element));
}
/**
* 可点击元素
*
* @param element
* @throws Error
*/
private void waitForClickable(WebElement element) throws Error {
new WebDriverWait(driver, 10).until(ExpectedConditions
.elementToBeClickable(element));
}
/**
* 检查是否够钻石抽奖
*
* @return
*/
public boolean CheckIsLottery() {
waitForVisibility(value);
int v = Integer.parseInt(value.getText());
System.out.println(v);
if (v < 200) {
return false;
}
return true;
}
/**
* 这个方法用来手动登录获取cookies
*
* @throws Exception
*/
public void WriteCookieToText() throws Exception {
driver.get(Constants.JjinUrl);
Thread.sleep(20000);
Set<Cookie> cookies = driver.manage().getCookies();
File cookieFile = new File("cookie.txt");
cookieFile.delete();
cookieFile.createNewFile();
FileWriter fileWriter = new FileWriter(cookieFile);
BufferedWriter bufferedWriter = new BufferedWriter(fileWriter);
for (Cookie cookie : cookies) {
bufferedWriter.write(cookie.getDomain() + ";" + cookie.getName()
+ ";" + cookie.getValue() + ";" + cookie.getExpiry() + ";"
+ cookie.getPath());
bufferedWriter.newLine();
}
bufferedWriter.flush();
bufferedWriter.close();
fileWriter.close();
driver.quit();
}
/**
* 用来读取txt中的cookie信息并加入driver
*
* @throws Exception
*/
@SuppressWarnings({ "resource", "deprecation" })
public void ReadCookieFromText() throws Exception {
BufferedReader bufferedReader;
File cookieFile = new File("cookie.txt");
FileReader fileReader = new FileReader(cookieFile);
bufferedReader = new BufferedReader(fileReader);
String line;
while ((line = bufferedReader.readLine()) != null) {
StringTokenizer stringTokenizer = new StringTokenizer(line, ";");
while (stringTokenizer.hasMoreTokens()) {
String domain = stringTokenizer.nextToken();
String name = stringTokenizer.nextToken();
String value = stringTokenizer.nextToken();
Date expiry = null;
String dt;
if (!(dt = stringTokenizer.nextToken()).equals("null")) {
expiry = new Date(dt);
}
String path = stringTokenizer.nextToken();
Cookie cookie = new Cookie(name, value, domain, path, expiry);
driver.manage().addCookie(cookie);
}
}
}
/**
* 抽奖业务流:想抽取几次都可以在这里控制
*
* @throws Exception
*/
public void Lottery() throws Exception {
OpenBrowser();
ClickSignIn();
ClickLottery();
ClickCostLottery();
Thread.sleep(10000);
CloseBrowser();
}
/**
* 程序执行入口,没有再去另起测试用例了
*
* @param args
* @throws Exception
*/
public static void main(String[] args) throws Exception {
System.setProperty("webdriver.chrome.driver", Constants.driverUrl);
WebDriver driver = new ChromeDriver();
driver.manage().window().maximize();
driver.manage().timeouts().implicitlyWait(15, TimeUnit.SECONDS);
IndexPage index = new IndexPage(driver);
index.Lottery();
}
}
复制代码
总结
同志们需要注意了:在手动获取登录cookie信息时,写入的位置和读取的cookie位置一定要匹配,不要在写入时path在domain前面,读取时domain却在path前面,这样读出来的数据是错的,即addcookie中取都是错误的,更何况还有一个date类型的参数,所以所保存cookie信息的位置很重要。
- 关于第二个问题,由于时间关系,没有引入testng测试框架去写用例了,而是在当前类中添加main方法简单实现;思路:可以获取钻石总数然后/200得到抽奖次数,先不管抽奖过程中获取66钻石,后面再去抽就是了<
但是作者还是留了两个坑,需要各位同学去奇思妙想了
>; - 关键的点:有一个小技术点,在由于操作速度过快,有些元素还没加载出来就操作了,执行可能会失败<没处理异常>,直接就pass过去关闭浏览器了<
所以按条件加入了显示等待机制!
> - 最后,希望中奖,中秋礼盒,预祝各位大佬高中!!!掘金大大们,发给礼盒吧!!!