本來想用Python模擬請求,但是!!經過抓包分析,它的請求裏面有兩個參數eid和fp是用javaScript動態生成的。順藤摸瓜,找到javaScript代碼,結果!!我看不懂……不僅看不懂,人家是專門做了混淆的…算了,就醬吧,還是乖乖用Selenium操作。
JDLogin:
- 登錄(有人說登錄次數多了會彈驗證碼,不過我從logout頁面登錄進去,多次,也沒有彈驗證碼)
- 到達我的豆豆頁面
- 抓取豆豆信息(總數+近三個月的明細),抓明細的時候,頁面上有做分頁,老老實實挨個分頁button點過去
- 獲取到的明細信息處理:隱去商品號…
時間着實有限,所以只拿了自己的帳號(有多頁豆豆明細數據的)和一個空白帳號(豆豆數量爲0,沒有豆豆交易明細數據)做測試,結果都是通過的。如下所示:
JDLoginTest:
- 登陸之後判斷是否登錄成功(頁面是否包含”我的**”字符串)
- 到達豆豆頁面,要判斷是否包含”可用*豆”字符串
- 抓完豆豆明細數據,判斷三個list長度是否相等,正常情況下應該相等(每一條豆豆明細數據包含:日期,增加或減少多少豆豆,交易名稱)
JDLogin Code:
package pkg_JDLogin;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.firefox.FirefoxDriver;
import org.openqa.selenium.interactions.Actions;
import java.util.List;
import java.util.ArrayList;
import java.util.Scanner;
import java.util.concurrent.TimeUnit;
/**
* Created by Sophie on 03/20/2016
*/
public class JDLogin {
private WebDriver dr;
private String url;
private String account;
private String pwd;
private String myJDXPATH;
private String myBeanXPATH;
private String datesXPATH;
private String transacDetailsXPATH;
private String transacNameXPATH;
private String pageNextXPATH;
private String total;
private List dateDetail;
private List transacDetail;
private List transacName;
public List retrieveJDBeanDatesDetails(){
return dateDetail;
}
public List retrieveJDBeanTransacDetail(){
return transacDetail;
}
public List retrieveJDBeanTransacName(){
return transacName;
}
public JDLogin(String url, String account, String pwd,String myJDXPATH, String myBeanXPATH,
String datesXPATH, String transacDetailsXPATH, String transacNameXPATH, String pageNextXPATH){
this.url = url;
this.dr = new FirefoxDriver();
this.dr.manage().timeouts().implicitlyWait(20, TimeUnit.SECONDS);
this.account = account;
this.pwd = pwd;
this.myJDXPATH = myJDXPATH;
this.myBeanXPATH = myBeanXPATH;
this.datesXPATH = datesXPATH;
this.transacDetailsXPATH = transacDetailsXPATH;
this.transacNameXPATH = transacNameXPATH;
this.pageNextXPATH = pageNextXPATH;
}
public WebDriver login() throws Exception{
dr.get(url);
dr.findElement(By.id("loginname")).clear();
dr.findElement(By.id("loginname")).sendKeys(account);
dr.findElement(By.id("nloginpwd")).clear();
dr.findElement(By.id("nloginpwd")).sendKeys(pwd);
dr.findElement(By.id("loginsubmit")).click();
//System.out.println(dr.getPageSource());
Thread.sleep(2000);
return dr;
}
public WebDriver JDBeanPage(){
WebElement dropDown = dr.findElement(By.xpath(myJDXPATH));
Actions act = new Actions(dr);
act.click(dropDown).perform();
dr.get(dr.findElement(By.xpath(myBeanXPATH)).getAttribute("href"));
return dr;
}
public List getDatesDetail(WebDriver dr){
List<WebElement> e = dr.findElements(By.xpath(datesXPATH));
List result = new ArrayList<String>();
for (int i = 0; i < e.size(); i ++){
result.add(e.get(i).getText());
}
return result;
}
public List getTransacDetail(WebDriver dr){
List<WebElement> e = dr.findElements(By.xpath(transacDetailsXPATH));
List result = new ArrayList<String>();
for (int i = 0; i < e.size(); i ++){
result.add(e.get(i).getText());
}
return result;
}
public List getTransacName(WebDriver dr){
List<WebElement> e = dr.findElements(By.xpath(transacNameXPATH));
List result = new ArrayList<String>();
for (int i = 0; i < e.size(); i ++){
result.add(e.get(i).getText());
}
return result;
}
public void getJDBeanDetails(){
total = dr.findElement(By.cssSelector("strong.ftx01.num")).getText();
//dates that have transactions
dateDetail = getDatesDetail(dr);
//gain or lost JDBean
transacDetail = getTransacDetail(dr);
//name of transac
transacName = getTransacName(dr);
Actions act = new Actions(dr);
//get pages size
List<WebElement> pageHref = dr.findElements(By.xpath("//div[@class='pagin fr']/a[not(@class) and @href]"));
if(pageHref.size() != 0) {//該用戶京豆交易明細至多隻有一頁的情況
act.moveToElement(pageHref.get(0));
act.click().perform();
}
//System.out.print(dr.getPageSource());
for (int i = 1; i < pageHref.size(); i ++) {
WebElement pageIcon = dr.findElement(By.xpath(pageNextXPATH));
act.moveToElement(pageIcon);
act.click().perform();
//date
dateDetail.addAll(getDatesDetail(dr));
//gain or lost
transacDetail.addAll(getTransacDetail(dr));
//name of transac
transacName.addAll(getTransacName(dr));
}
maskItemNumber(transacName);
System.out.println("共有京豆:" + total + ",明細如下:\n==============================================================================");
for(int i = 0; i < transacName.size(); i ++){
System.out.println(dateDetail.get(i)+"\t\t\t\t\t\t"+transacDetail.get(i)+"\t\t\t\t\t\t"+transacName.get(i));
}
}
public void maskItemNumber(List<String> transacName){
StringBuffer s ;
for (int i = 0; i < transacName.size(); i ++){
if(transacName.get(i).toString().contains("商品")){
s = new StringBuffer(transacName.get(i).toString());
for(int j = 0; j < s.length(); j ++){
if (Character.isDigit(s.charAt(j)))
s.setCharAt(j,'*');
}
transacName.set(i,s.toString());
}
}
}
public void tearDown() throws Exception{
dr.quit();
}
public static void main(String[] args) throws Exception {
String url = "https://passport.jd.com/uc/login?ltype=logout";
Scanner s = new Scanner(System.in);
System.out.println("請輸入帳號:");
String account = s.nextLine();
System.out.println("請輸入密碼:");
String pwd = s.nextLine();
String myJDXPATH = "//li[@id='ttbar-myjd']/div[1]/i[@class='ci-right']/s";
String myBeanXPATH = "//li[@id='ttbar-myjd']/div[2]/div[@class='otherlist']/div[@class='fore2']/div[2]/a";
String datesXPATH = "//span[@class='ftx03']";
String transacDetailsXPATH = "//table[@class='tb-void']/tbody/tr/td[2]/span";
String transacNameXPATH = "//table[@class='tb-void']/tbody/tr/td[3]";
String pageNextXPATH = "//div[@class='pagin fr']/a[@class='current']/following-sibling::a[1]";
JDLogin jd = new JDLogin(url, account, pwd, myJDXPATH, myBeanXPATH, datesXPATH, transacDetailsXPATH, transacNameXPATH, pageNextXPATH);
jd.login();
jd.JDBeanPage();
jd.getJDBeanDetails();
jd.tearDown();
}
}
JUnit測試代碼:
package pkg_JDLogin;
import org.junit.*;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import static org.junit.Assert.*;
import java.util.Scanner;
import static org.hamcrest.Matchers.*;
import java.util.List;
/**
* Created by Sophie on 16/3/20.
*/
public class JDLoginTest {
private JDLogin jd;
private WebDriver dr;
@Before
public void setUpBefore(){
String url = "https://passport.jd.com/uc/login?ltype=logout";
//Scanner s = new Scanner(System.in);
//System.out.println("請輸入帳號:");
String pwd = "***";
//System.out.println("請輸入密碼:");
String account = "***";
String myJDXPATH = "//li[@id='ttbar-myjd']/div[1]/i[@class='ci-right']/s";
String myBeanXPATH = "//li[@id='ttbar-myjd']/div[2]/div[@class='otherlist']/div[@class='fore2']/div[2]/a";
String datesXPATH = "//span[@class='ftx03']";
String transacDetailsXPATH = "//table[@class='tb-void']/tbody/tr/td[2]/span";
String transacNameXPATH = "//table[@class='tb-void']/tbody/tr/td[3]";
String pageNextXPATH = "//div[@class='pagin fr']/a[@class='current']/following-sibling::a[1]";
jd = new JDLogin(url, account, pwd, myJDXPATH, myBeanXPATH, datesXPATH, transacDetailsXPATH, transacNameXPATH, pageNextXPATH);
}
@Test
public void test() throws Exception {
//login
dr = jd.login();
org.junit.Assert.assertThat( dr.getPageSource(),containsString("我的京東"));
//JD bean page
dr = jd.JDBeanPage();
org.junit.Assert.assertThat(dr.getPageSource(), containsString("可用京豆"));
//JD bean details
jd.getJDBeanDetails();
List<String> dates= jd.retrieveJDBeanDatesDetails();
List<String> transacDetails = jd.retrieveJDBeanTransacDetail();
List<String> transacName = jd.retrieveJDBeanTransacName();
//System.out.println("Dates Size: "+dates.size() + "TransacName Size: "+ transacName.size() + "Transac Details Size: "+ transacDetails.size() );
org.junit.Assert.assertEquals("抓取京豆明細異常:數據條數不匹配", dates.size(),transacDetails.size());
org.junit.Assert.assertEquals("抓取京豆明細異常:數據條數不匹配", dates.size(),transacName.size());
for(int i = 0; i < transacName.size(); i ++){
for (int j = 0; j < transacName.get(i).length(); j ++){
org.junit.Assert.assertFalse("敏感信息未隱藏: 第 " + i+1 + "條數據", Character.isDigit(transacName.get(i).charAt(j)));
}
}
}
@After
public void testTearDown() throws Exception {
jd.tearDown();
}
}