詳細分析小米搶購軟件的實現步驟


    不知道是飢餓營銷還是真的供不應求,小米的火熱真的是無法阻擋。眾多產品一一亮相,着實吸引眼球,但是一機難求的局面沒有改善,讓眾多米粉敗興而歸。我們來實現一個簡單的小米搶購軟件,讓搶購之路多上那么一點點希望。

    首先要說明的是小米搶購過程中的很多頁面和請求地址都是在開放搶購當天時間點到了之后才開放,搶購結束會關閉,所以你在按照博客的內容自己實現的過程中有請求地址不能訪問的,請在搶購開始之后測試,樓主解決不了這個問題。我是在第一次搶購的時候記錄請求了哪些地址,做好簡單的邏輯之后第二次搶購的時候驗證。軟件是兩個月前完成,當時是可用的。小米搶購的邏輯經常改變,最近這幾次沒搶,不確定中間請求的地址是否失效。

    下面開始分析實現的過程吧。

本文作者 russellwang,轉載請標明出處

    第一步,模擬登陸。在小米首頁點擊登陸之后可以看到登陸頁面,要求輸入郵箱/ID、密碼,小米的登陸沒有驗證碼,相對簡單了很多。點擊登陸按鈕可以看到請求的地址是 https://account.xiaomi.com/pass/serviceLoginAuth2,請設置好對應的Request Header,請求參數見下圖:

    user 和 pwd 是你的用戶名和密碼,其他的保持不變應該就可以,返回的結果里 desc 是“成功”說明登陸成功,這個時候能拿到一個返回的cookie,這個cookie不是我們最后需要的cookie,還需要一步請求之后拿到的cookie才能用,從登陸頁面請求的抓包結果來看確實還有一步。從上一步的返回的結果里獲得notificationUrl的值,形如:/auth/service?userId=xxxxxxxx&_sign=xxxxxxxx&nonce=xxxxxxxx,完整地址為 https://account.xiaomi.com/auth/service?userId=xxxxxxxx&_sign=xxxxxxxx&nonce=xxxxxxxx,帶着上一步拿到的cookie訪問這個頁面,拿到返回的cookie保存起來,這個cookie就是可用的,模擬登陸到這里就完成了。

本文作者 russellwang,轉載請標明出處

    第二步,獲得手機版本選擇頁面的地址。請求地址形如:http://tp.hd.mi.com/getpath/cn?m=1&jsonpcallback=getpath&_=1416887144988,最后一個參數為當前時間的時間戳,這個地址只有到開放購買的時候才有效,平時訪問是不通的。從返回的結果中拿到path對應的值,形如:http://s1.mi.com/open/3612A6B4D011142E4D533DE85CCBA5D1/choosePhone.html?_20141125,這個頁面是我們在搶購的時候看到的手機版本選擇的頁面,獲取頁面內容,里面有很多有用的信息。

    先看一下這個頁面里的部分js內容。

 

 1 init: function() {
 2         this.inTheQueue = !1,
 3         this.phoneSku = "",
 4         this.phoneType = "",
 5         this.hdinfoData = null,
 6         this.startTime = new Date("2014/12/9 12:00:00").getTime() / 1e3,
 7         this.nextDate = "12月16日",
 8         this.showMod = !0,
 9         this.modType = null,
10         this.fkNum = 0,
11         this.isReg = "true",
12         this.hdget_date_tmp = "{{M}}md{{Y}}y47d15s",
13         this.cookies = {
14             isStart: "XM_Hd_Start",
15             buySucc: "XM_Buy_Succ",
16             userId: "userId",
17             login: "xm_order_btauth"
18         },
19         this.home = "http://s1.mi.com/open/index.html",
20         this.hdgetUrl = "http://tp.hd.mi.com/hdget/cn?product={{SKU}}&addcart=1&m=1&fk={{FK}}&uagent={{TODAY}}",
21         this.hdinfoUrl = "http://tp.hd.mi.com/hdinfo/cn",
22         this.timestampUrl = "http://tp.hd.mi.com/gettimestamp",
23         this.getmodeUrl = "http://tp.hd.mi.com/getmode/cn/?product=",
24         this.nextBookUrl = "http://a.hd.mi.com/productv2/book/a/18#MIPHONE",
25         this.ordeSite = "http://order.mi.com",
26         this.shopCartUrl = this.ordeSite + "/cart/add/{{SKU}}?source=bigtap&token={{TOKEN}}",
27         this.addCartNext = this.ordeSite + "/event/success?goodsid={{SKU}}",
28         this.loginUrl = "http://s1.mi.com/zt/xm_account/limitfacade.html?third=http%253A%252F%252Forder.mi.com%252Flogin%252Fcallback%253Ffollowup%253Dhttp%25253A%25252F%25252Fs1.mi.com%25252Fopen%25252Findex.html%2526sign%253DNjEzYmU3ZTJkOWRlY2FiZDQ5NDEwNzEyZjNiMjg0NDA0MGYxYWY3Mg%252C%252C%26sid%3Dmi_eshop&sid=mi_eshop&callback=http%253A%252F%252Forder.mi.com%252Flogin%252Fcallback%253Ffollowup%253Dhttp%25253A%25252F%25252Fs1.mi.com%25252Fopen%25252Findex.html%2526sign%253DNjEzYmU3ZTJkOWRlY2FiZDQ5NDEwNzEyZjNiMjg0NDA0MGYxYWY3Mg%252C%252C&sign=dK3nqW%252FKhFM3Tl7Jyt9%252FGt3jOI8%253D",
29         this.noPresaleGoods = [],
30         this.noBookGoods = [],
31         this.isHm = ["2143300001", "2143400005", "2143200006", "2141600007", "2140700031"];
32         var a = this;
33         return xmCookie(a.cookies.userId) && xmCookie(a.cookies.login) || (location.href = a.loginUrl),
34         xmCookie(a.cookies.isStart) ? a.getHdInfo() : a.checkTime(),
35         $("[data-close-target]").on("click",
36         function() {
37             var b = $(this).attr("data-close-target");
38             return a.hideBox(b),
39             !1
40         }),
41         $("#submitBtn").on("click",
42         function() {
43             return ! a.phoneSku || $(this).hasClass("btn-disabled") ? (alert("請選擇您要購買的手機"), !1) : $.inArray(a.phoneSku, a.isHm) >= 0 ? void(location.href = "http://order.mi.com/event/selectPacket/goodsid/" + a.phoneSku) : "true" !== a.isReg ? (a.showBox("Tip",
44             function() {
45                 a.getTipMsg("reg")
46             }), !1) : void(a.showMod ? (a.getmode(), a.showBox("Fk")) : (a.startQueue(), a.getDmSys()))
47         }),
48         $("#boxCacheBtn").on("click",
49         function() {
50             a.hideBox("all"),
51             $("#submitBtn").trigger("click")
52         }),
53         "undefined" != typeof HDOVER && HDOVER === !0 ? (location.href = a.home, !1) : ($(".J_nextDate").html(a.nextDate), $(".J_bookBtn").attr("href", a.nextBookUrl), $(".J_fkLoading").on("click", ".J_reloadFk",
54         function() {
55             $(this).parent().html('<img src="http://img03.mifile.cn/webfile/images/2014/cn/loading.gif">'),
56             a.getmode()
57         }), void $("#fkNum").on("keyup",
58         function() {
59             $(this).val().length;
60             $("#boxFkBtn").removeClass("btn-disabled").off().on("click",
61             function() {
62                 a.checkFk()
63             })
64         }))
65     }
View Code

 

    仔細分析一下這段js,發現搶購后續的請求地址和參數格式在這里都能找到,hdgetUrl,hdinfoUrl,getmodeUrl,shopCartUrl 這幾個后面都會用到,抓包分析后面幾步的過程可以和這里的地址驗證,先保存下來。下面要做的工作是計算js中{{TODAY}}對應的值,將hdgetUrl中的值替換,計算邏輯可用下面的,

Pattern dateTmpPattern=Pattern.compile("hdget_date_tmp=\"(.*?)\""); Matcher dateTmpMatcher=dateTmpPattern.matcher(result); if(dateTmpMatcher.find()) { dateTmp=dateTmpMatcher.group(1); dateTmp=dateTmp.replace("{{Y}}",YEAR+"").replace("{{M}}",MONTH).replace("{{D}}",DAY); hdGetUrl=hdGetUrl.replace("{{TODAY}}",dateTmp); }
View Code

    YEAR、MONTH、DAY 為當前日期。

本文作者 russellwang,轉載請標明出處

    第三步,獲得產品狀態信息。這里用到了 hdinfoUrl,請求地址為http://tp.hd.mi.com/hdinfo/cn?jsonpcallback=hdinfo&_=1416888070538(下面不說明的時候比較長的數字應該就是指當前的時間戳)。請求返回的數據格式如下:

{"stime":1416979023,"pmstart":false,"status":{"2141700014":{"hdstart":false,"hdstop":true,"reg":true},"2143000004":{"hdstart":false,"hdstop":true,"reg":true},"2143400001":{"hdstart":false,"hdstop":true,"reg":true},"2143400004":{"hdstart":false,"hdstop":true,"reg":true},"2143600001":{"hdstart":false,"hdstop":true,"reg":true},"2144000012":{"hdstart":false,"hdstop":true,"reg":true},"2144100013":{"hdstart":false,"hdstop":true,"reg":true},"2144100014":{"hdstart":false,"hdstop":true,"reg":true}},"dbe5a2":false}
View Code

    包含了每種產品的可售狀態,可以根據這個信息判斷是否能購買,在這里可以過濾掉不能買的信息。

    產品代號和名稱的對應關系我做了一個對應,這個信息比較老了,請根據每次購買的情況更新:

2143000004----小米4聯通3G版 亮白16GB 1999
2143400001----小米4聯通3G版 亮白64GB 2499
2143100007----小米4聯通4G版 亮白16GB 1999
2143400004----小米4電信3G版 亮白16GB 1999
2144000012----小米4移動4G版 雅黑16GB 1999
2144100014----小米4移動4G版 雅黑64GB 2499
2144800007----小米4移動4G版 黑色(金屬原色框)16GB 1999
2143600001----小米4移動4G版 亮白16GB 1999
2144100013----小米4移動4G版 亮白64GB 2499
2143200006----紅米Note 移動4G增強版 899
2143400005----紅米Note 聯通4G增強版 899
2141600007----紅米1S聯通3G版 金屬灰 799
2140700031----紅米1S電信3G版 金屬灰 799
2143300001----紅米1S移動4G版 金屬灰 599
2141700014----小米路由器 mini 129
2143000001----小米手環 79
View Code

    要購買哪種產品只需要產品代號信息(應該是sku)就夠了。

本文作者 russellwang,轉載請標明出處

    第四步,獲取驗證碼或驗證問題。用到了getmodeUrl地址,請求路徑為 http://tp.hd.mi.com/getmode/cn/?product=xxxxxxxxxx&jsonpcallback=getmode&_=1416888070539,將product值換成你要去買的產品的sku值。根據返回response頭的Content-Type值判斷是驗證碼圖案還是驗證問題,image/jpeg 類型的返回信息應該是驗證碼,text/html 應該是驗證問題。根據不同類型,輸入答案后進入第五步。

本文作者 russellwang,轉載請標明出處

    第五步,驗證答案。地址和第四步中是類似的,多了一個答案參數,http://tp.hd.mi.com/getmode/cn/?product=2144000012&vecode=89&jsonpcallback=getmode&_=1416888070540,返回結果中code值是“1”說明驗證成功,可以進行下一步,否則重新輸入驗證答案。

本文作者 russellwang,轉載請標明出處

    第六步,最后獲得產品信息,獲得下一步TOKEN對應的hdurl。這一步用到了hdgetUrl,http://tp.hd.mi.com/hdget/cn?product={{SKU}}&addcart=1&m=1&fk={{FK}}&uagent={{TODAY}},SKU為要購買產品的sku,FK為第四步出現驗證碼或問題的答案,TODAY已在前面替換成一個和日期有關動態的參數,拿到請求這個地址的返回結果,

hdcontrol({"d22a51":10,"login":true,"pmstart":false,"status":{"2144800007":{"hdstart":false,"hdstop":true,"hdurl":""}}})

    其中hdurl有值的時候說明這一步是成功的,拿到這個hdurl進入第七步,如果你得到的結果和我一樣沒有hdurl的值,那么不好意思,你沒法加入購物車,后面會提示出錯的,這一步可以多試幾次,看看能不能運氣好就能買了。

本文作者 russellwang,轉載請標明出處

    第七步,加入到購物車。用到了shopCartUrl ,地址 http://order.mi.com/cart/add/2144000012?source=bigtap&token={{TOKEN}}&jsonpcallback=getdata,將這里的 TOKEN 替換成第六步獲得的 hdurl , 根據這個請求的返回結果判斷是否添加成功,

下面是返回數據的格式,

{"code":-1,"message":"\u6dfb\u52a0\u8d2d\u7269\u8f66\u9700\u8981\u767b\u5f55\uff0c\u8bf7\u5148\u767b\u5f55\uff01","msg":"\u6dfb\u52a0\u8d2d\u7269\u8f66\u9700\u8981\u767b\u5f55\uff0c\u8bf7\u5148\u767b\u5f55\uff01"}
View Code

    可以根據code的值判斷是否成功,后面的為提示信息。

    搶購過程中的關鍵點都分析完了,再次強調一下,搶購的邏輯經常改變,不保證這個過程還適應現在的邏輯,需要自己在開放購買的時候實測。

    上一張截圖

 

    博文作者:russellwang
    博文出處:http://www.cnblogs.com/russellwang
    本文版權歸作者和博客園共有,歡迎轉載,但須保留此段聲明,並給出原文鏈接,謝謝合作!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM