如何用Python投機倒把幾天“暴富”
本文涉及到的技術點有(多圖預警!):
- 1. Packet Capture和Charles抓包 ;
- 2. 安卓AccessibilityService無障礙服務實現自動下注 ;
- 3. Python爬蟲模擬下注 ;
- 4. Python Flask編寫簡單介面 ;
行吧,別做夢了,暴富了,我還這裡碼字?標題黨的感覺就是舒服,還是踏踏實實搬磚吧!關於賺大錢的捷徑都寫在刑法裡,想具體瞭解的可以到逼乎自行查閱: ofollow,noindex">都說最賺錢的方法都在刑法裡面,那麼憲法裡面到底禁止了哪些暴利的賺錢方法?這些方法到底有多賺錢呢? 我只能幫你們到這裡了。投機倒把暴富的念想是挺美好的,但我投機倒把的真實的結果是: 國慶短短几天,因為投機倒把我虧了7755塊 (截止目前虧了 9073塊 )...

編寫本文的出於下面幾個初衷:
- 1.記錄自己在投機倒把過程中運用到的一些 開發技術 ;
- 2.記錄這次投機倒把的經歷,以此警示自己以後要踏踏實實做事;
- 3.奉勸各位有這種念想的夥計 引以為戒 , 遠離投機倒把 , 保持身心健康 。
在真正開始本文之前,要嚴肅的強調幾點:
- 投機倒把 ,十有九 輸 ,或者十 輸 , 輸 !投機倒把中賺錢的永遠是極少數的幸運兒。如果只為 娛樂 , 不為贏錢 可以,參考過年家裡常有的小賭怡情,玩個幾百塊,贏了和輸了都收手。
- 本文不是讓你去投機倒把,所以不會提供任何APP下載外鏈或檔案!
本文的講解流程:
先講解一波投機倒把相關的概念,然後通過和兩個好基友的沙雕聊天記錄,引出一些開發相關的東西,分析一波投機倒把過程裡的一些貓膩,希望各位看官會喜歡這個有( 悲 )趣( 傷 )的真實故事~

第零章:名詞碎碎念
先來了解一波和投機倒把有關的名詞吧~
0.1 賭徒心理(貪慾和僥倖心理)
贏了還想贏更多的錢,輸了又想回本,反覆告訴自己贏一把就收手,回本就收手,事實上,只有當自己的前輸得精光的時候才會收手。
舉個例子:
- 1.下注,輸了10塊,想著把10塊賺回來,又壓了10塊,然後又輸,又壓20,再輸壓30...本沒有回,錢卻越輸越多。
- 2.輸了10塊,下把下注獎為5塊, 贏了 , 患得患失 ,如果剛剛那把壓多一點就贏好多了, 輸了 , 暗自慶幸 ,幸好把下注少了,不然就虧大了。
贏錢的時候也不會見好就收,畢竟對於賭徒來說,收手是最難的。
0.2 大數法則
大數不是 大叔 ,又稱“ 平均法則 ”,即: 在隨機事件的大量重複中往往出現幾乎必然的規律 。在實驗條件不變的情況下,重複實驗多次,隨機事件的頻率近似於它的概率。舉個例子,我們都知道的常識: 拋硬幣 ,正反面的概率各佔50%。但是,現在讓你去拋兩次硬幣,卻很難出現剛好一正一反的情況。只在你重複了上千上萬次後,正和反出現的次數接近持平。
看到這裡,讀者可能有這樣的疑問:
既然最後是五五開,那我一直玩,為什麼 最後還是虧了 ,難道是有 中間商 賺差價了?
講真,真的有 中間商 ,儘管從概率上來說,是五五開的,但是有兩點根本不平等。
首先,概率是建立在 大量重複實驗 的基礎上的,莊家有“用不完的錢”,可以進行無限次投機倒把,且沒有賭徒心理,而反觀賭徒的資金一般是有限的。然後呢,又有一個名詞,概率波動論。
0.3 概率波動論
具備兩面性的規律從概率來講是對半的,但在一段時間內有可能多數呈一面性表現。舉個簡單的三個骰子買大小的例子(1-9小,10-18大),10次的結果是這樣的(連續3次大):

概率波動是概率發生的必然,因此投機倒把過程中可能出現連續輸好幾把的情況。
0.4 倍投法
接著是中間商賺差價的第二點,比如有一些買大小單雙的賭局,勝率並不是 1:2 ,比如 1:1.96 ,或者 1:1.82 等,比如你一把玩1塊,第一把輸了,第二把也投1塊,你中了,只贏 1.96 塊,但是,其實你已經花了 2塊錢 ,虧4分錢。如果是1.86的,虧1毛4,看上去數額很少是吧,但是基本都不會只玩幾把吧,一天下來可能玩了上千把,一個賭客就貢獻了好幾百,賭客肯定不止你一個,而且下注也肯定不會1塊1塊的來,所以說說為什麼那麼多投機倒把類的APP。而扭轉這種虧損的一種簡單而且賺錢的做法,就是倍投法。舉個簡單的例子,假如賠率是1:2的話,下注的金額是這樣一個列表: [10,20,40,80,160,320,640,1280,2560...] ,第一把投10塊,輸了,第2把投20,贏了40,減去本金30,賺10,沒中,第3把投40,贏了80,減去本金70,賺10塊,就是保證每一輪贏10塊。我們寫個簡單的Python程式碼來生成一個倍投收益的表格。程式碼如下( prettytable 庫用於生成表格):
from prettytable import PrettyTable purchase_base = 10# 購買基數 ratio = 2# 中獎倍率 price_list = []# 投入金額 def init_purchase_list(): x = PrettyTable(['投入', '投入總額', '獲利', '淨利潤']) cost = 0 price_list.append(purchase_base) # 其他進行動態計算 print("初始化投入金額表格...") for i in range(0, 10): cost += purchase_base * (2 ** i)# 花費 bonus = purchase_base * (2 ** i) * ratio# 獎金 x.add_row([purchase_base * (2 ** i), cost, bonus, bonus - cost]) print(x) if __name__ == '__main__': init_purchase_list() 複製程式碼
執行下程式,可以得出一個倍投的結果列表:
初始化投入金額表格... +------+----------+-------+--------+ | 投入 | 投入總額 |獲利 | 淨利潤 | +------+----------+-------+--------+ |10|10|20|10| |20|30|40|10| |40|70|80|10| |80|150|160|10| | 160|310|320|10| | 320|630|640|10| | 640|1270|1280 |10| | 1280 |2550|2560 |10| | 2560 |5110|5120 |10| | 5120 |10230| 10240 |10| +------+----------+-------+--------+ 複製程式碼
每一輪才掙10塊,有點少是吧,你可以試試賺更多的3倍投,把上面的程式碼改一下:
for i in range(0, 10): cost += purchase_base * (3 ** i)# 花費 bonus = purchase_base * (3 ** i) * ratio# 獎金 x.add_row([purchase_base * (3 ** i), cost, bonus, bonus - cost]) 複製程式碼
輸出結果如下:
初始化投入金額表格... +--------+----------+--------+--------+ |投入| 投入總額 |獲利| 淨利潤 | +--------+----------+--------+--------+ |10|10|20|10| |30|40|60|20| |90|130|180|50| |270|400|540|140| |810|1210|1620|410| |2430|3640|4860|1220| |7290|10930| 14580|3650| | 21870|32800| 43740| 10940| | 65610|98410| 131220 | 32810| | 196830 |295240| 393660 | 98420| +--------+----------+--------+--------+ 複製程式碼
臥槽,刺激是吧,如果有30W的本金,三倍投,不中的次數越多,你賺的錢越多。如果連續不中到10次,中了,一輪1將近10W,這種是敢死隊倍投法,很難很難掛一次,但是一旦出現一次直接跌入18層地獄永不超生!(筆者試過好幾次...等下細講)。這種是平臺比例1:2的情況,有些中間商賺差價的平臺的賠率是1.96這樣,上面的程式碼生成的結果就變成了(2倍投,ratio改為1.96):
初始化投入金額表格... +------+----------+---------+---------------------+ | 投入 | 投入總額 |獲利|淨利潤| +------+----------+---------+---------------------+ |10|10|19.6|9.600000000000001| |20|30|39.2|9.200000000000003| |40|70|78.4|8.400000000000006| |80|150|156.8|6.800000000000011| | 160|310|313.6|3.6000000000000227 | | 320|630|627.2| -2.7999999999999545 | | 640|1270|1254.4 | -15.599999999999909 | | 1280 |2550|2508.8 |-41.19999999999982 | | 2560 |5110|5017.6 |-92.39999999999964 | | 5120 |10230| 10035.2 | -194.79999999999927 | +------+----------+---------+---------------------+ 複製程式碼
是的,普通的二倍投法越到後面反而越虧,而三倍投依舊是賺錢的,在本金不夠充裕無法三倍投的情況下,要在二倍投的基礎上加點,而且少於三倍投,保證我們每把賺的錢>購買基數,而且投入的金額最少,我們改下下我們的程式碼:
from prettytable import PrettyTable purchase_base = 10# 購買基數 ratio = 1.96# 中獎倍率 price_list = []# 投入金額 def init_purchase_list(): x = PrettyTable(['投入', '投入總額', '獲利', '淨利潤']) x.add_row([purchase_base, purchase_base, round(purchase_base * ratio, 2), round(purchase_base * ratio - purchase_base, 2)]) cost = purchase_base price_list.append(purchase_base) # 其他進行動態計算 print("初始化投入金額表格...") for i in range(10): purchase = 0 # 購買價格其實區間(2倍) start_price = purchase_base * (2 ** i) # 購買價格極限區間(3倍) end_price = purchase_base * (3 ** i) # 保證沒把不虧就行 for j in range(start_price, end_price + 1): if j * ratio - cost - j > purchase_base: x.add_row([j, cost + j, round(j * ratio, 2), round(j * ratio - cost - j, 2)]) cost += j price_list.append(j) break print(x) if __name__ == '__main__': init_purchase_list() 複製程式碼
輸出下注金額列表如下:
初始化投入金額表格... +------+----------+----------+--------+ | 投入 | 投入總額 |獲利| 淨利潤 | +------+----------+----------+--------+ |10|10|19.6|9.6| |21|31|41.16| 10.16| |43|74|84.28| 10.28| |88|162|172.48| 10.48| | 180|342|352.8|10.8| | 367|709|719.32| 10.32| | 749|1458| 1468.04| 10.04| | 1530 |2988|2998.8|10.8| | 3123 |6111| 6121.08| 10.08| | 6377 |12488| 12498.92 | 10.92| +------+----------+----------+--------+ 複製程式碼
以上就是通過Python計算倍投下注金額,關於更多的倍投方法可自行查閱: 倍投的方案全面講解如何倍投 ,關於投機倒把的名詞就科普那麼多,正式開始這個令人悲傷的故事吧~

第一章:引子
和往常一樣,下午等電梯去吃飯,好基友小A又掏出他的小錘子在那裡把玩,抱著好奇心的 我用眼角的餘光瞄了一下他在看什麼,em...竟然不是那種不堪入目的東西?

在我的威逼利誘(Y威)下全盤托出:別人介紹玩的一款賺錢APP(買彩票)。但是,我記得錯網路售彩不是從15年就開始命令禁止了嗎。行吧,看了下,私莊(私人莊家)。然後大概看了看APP:

頁面非常簡單,四個部分:
- 1.頂部最新一期下注倒計時+使用者餘額
- 2.每期開獎結果
- 3.房間裡賭客的投注記錄
- 4.下注面板
規則就是下注結果和出的結果一樣就中,根據買的種類有不同的倍率,三分鐘開一把,下注最小金額 10元寶 (10塊),資料來源 加拿大28 彩票的出獎結果。這種算是 公彩 吧,不是 私彩 。區分的最簡單依據就是 有這個專案的多個平臺 是不是開獎的結果都是一樣的,相比起私彩,公彩稍微稍微公平一點。 私彩 的話, 後臺是可以調的 !!!好吧,一向對於投機倒把不怎麼感冒的我就沒有過多的關注了。不過,他貌似從別人那裡搞了個下注的投法,慢慢從20奮鬥到102,成功引起了我的注意。

這個所謂靠譜的投法就是:
- 1.歷史記錄裡,單跟雙,多就買誰。
- 2.歷史記錄裡面,大跟小,少就買誰。
- 3.每一次虧了下一把就把下注金額在上一把基礎上翻倍,每一次賺了下一把下注金額就還原到最低值。 4.迴圈上面三步驟。如果遇到異常流:當前步驟應該買大小或者單雙時,出現歷史記錄裡他們數量相同,則停止一局。
後面果然給他擼到200了:

嘖嘖,竟然真的從20擼到200,不心動是假的...

但是作為一個從不相信天上掉餡餅的開發仔有著自己的矜持,不模擬一下,打死我也不信,於是我盤算著,自己寫個指令碼按照這個投法去模擬,模擬一段時間後,看看最後是不是真的賺了。先來抓一波APP的包看看吧,這裡用到的是手機抓包工具: Packet Capture

行吧,token和sign,後面另外抓了下注的介面,一大串,加密,所以基本可以放棄了。然後想逆向找下APP的程式碼,直接apktool反編譯後看到qihoo的包名,擦,360加固,這就觸及到我的知識盲區了,我還不會脫殼...

幸運的是,這是公彩,有很多網站會直接公佈每一期的結果,比如: www.kandandan.com/yuce/jnd.ht… ,接著開啟Chrome抓下包,先看下頁面節點:

可以,看下Network也可以找到這些節點,不是JS載入的,nice,所有要做的就是寫一個定時器,每隔3分40秒左右(延時問題)訪問一下這個站點,提取一波資料。行吧,直接用Python請求庫Requests模擬一波請求就行了,定時任務用 apscheduler 庫,最後加上投注相關的演算法就可以了,直接給程式碼,就不解釋那麼多了。
import requests as r from bs4 import BeautifulSoup from apscheduler.schedulers.blocking import BlockingScheduler current_buy = '單雙'# 當前買的型別 current_buy_type = '單'# 當前下注型別 current_buy_money = 10# 當前投注金額 is_buy_flag = False# 此輪是否購買 first_buy = True# 是否第一次下注 balance = 300# 剩餘金額 sched = BlockingScheduler() def fetch_result(): global first_buy resp = r.get('https://m.99yuce.com/yuce/jnd.html').text bs = BeautifulSoup(resp, 'lxml') tr_s = bs.find("table", attrs={'id': 'tbe'}).find_all('tr') # 獲取數字列表 num_list = [] for tr in tr_s[2:12]: num_list.append(int(tr.find('td', attrs={'class': 'tbe_3'}).text)) print(num_list) # 構建數字文字列表 num_str_list = [] for num in num_list: if num % 2 == 0: num_str_list.append('雙') else: num_str_list.append('單') if num > 13: num_str_list.append('大') else: num_str_list.append('小') if first_buy: first_buy = False print("當前餘額:", balance, " 當前投注金額:", current_buy_money, "當前買的型別:", current_buy) else: calculate(num_list[0]) predict(num_str_list) # 預判 def predict(num_str_list): global balance global current_buy_money global current_buy global current_buy_type global is_buy_flag single_count = 0# 單出現的次數 big_count = 0# 大出現的次數 for num_str in num_str_list: if num_str == '單': single_count += 1 if num_str == '大': big_count += 1 print('單:', single_count, ' 大:', big_count) # 如果當前這把應該買單雙 if current_buy == '單雙': if single_count > 5: current_buy_type = "單" balance -= current_buy_money is_buy_flag = True print("押單") elif single_count < 5: current_buy_type = "雙" balance -= current_buy_money is_buy_flag = True print("押雙") else: # 相等則跳過 print("單雙相等,跳過這一局,不買") is_buy_flag = False # 如果當前這把應該買大小 elif current_buy == '大小': if big_count < 5: current_buy_type = "大" balance -= current_buy_money is_buy_flag = True print("押大") elif big_count > 5: current_buy_type = "小" balance -= current_buy_money is_buy_flag = True print("押小 ") else: # 相等則跳過 print("大小相等,跳過這一局,不買") is_buy_flag = False # 結算,傳入最新的數字 def calculate(latest_num): global balance global current_buy_money global current_buy global current_buy_type global is_buy_flag # 如果沒有買直接跳過結算 if is_buy_flag: result_list = [] if latest_num % 2 == 0: result_list.append('雙') else: result_list.append('單') if latest_num > 13: result_list.append('大') else: result_list.append('小') print('開獎結果:', result_list) # 中了(加餘額,重置下注金額) if current_buy_type in result_list: balance += current_buy_money * 2 current_buy_money = 10 print("中了!") # 沒中(下注金額加倍) else: current_buy_money *= 2 print("沒中~") # 無論中沒中,切換下一把的下注型別 if current_buy == '單雙': current_buy = '大小' else: current_buy = '單雙' else: print("沒有下注,跳過當前輪") print("當前餘額:", balance, " 當前投注金額:", current_buy_money, "當前買的型別:", current_buy) if __name__ == '__main__': sched.add_job(fetch_result, 'interval', minutes=3, seconds=40) fetch_result() sched.start() 複製程式碼
接著把指令碼掛著模擬下注就可以,而且小A玩的時候不用自己去統計,看著投就好了。

經過兩個小時的艱辛等待,300跑到390了,臥槽,穩得不行,什麼都不幹,每小時多50塊左右,一天跑個10小時,500進口袋,搞十個號,一天不得5000?,5000*30 = 150000,臥槽,一個月什麼都不幹15W,有本金了買一堆手機,掛著跑100個號,臥槽,深圳買房不是夢。我還盤算著公司附近哪租個小地方來做機房,就一個架子然後放一堆手機就好了。 在不知不覺中我已經著道了 ,但還保留著一絲理智,無本生利的事情怎麼別人想不到,莊家都是傻逼嗎?萬一捲款跑路了,於是我谷歌搜了一波是不是騙局,在逼乎看到了這篇文章: 騰訊彩票遊戲幸運28是不是個騙局? ,現在提是能提現,最怕是做了什麼限制,比如次數,或者限額,錢根本搞不出來,抱著先試試,300而已,賺夠本金就提出來,後面的就真的是無本生利了(事實是, 我們想多了 ,這麼點錢,對人家平臺來說真的不算什麼,而且本金就沒提出來過,最後都是血本無歸...)

第二章:如火如荼
貧窮使人勤勞,一想到可以暴富,我比任何時候都勤快,真·擼起袖子加油幹。所謂的自動化就是不用自己點點點,方法也很簡單,使用的Android的 無障礙服務——AccessibilityService 實現,原生的Android頁面都可以用這個點點點,如果是內嵌網頁的就無能為力了。如果你想了解AccessibilityService可以查閱我以前寫的兩篇文章:
- 妙用AccessibilityService黑科技實現微信自動加好友拉人進群聊
- 自動搶紅包,點贊朋友圈,AccessibilityService解放你的雙手
簡單點說就是利用工具, 根據ID或者TEXT文字 , 找到對應的控制元件 , 進行一些操作 , 比如最常見的點選 , 傳入文字等 。而這個找ID的工具可以使用 android-sdk 裡自帶的 monitor 來查詢,如果你的手機有root還可以下載使用 開發者助手 ,如圖所示,可以直接拿到控制元件ID。

模擬點,進入到這個頁面,然後把Python寫的演算法搬運到Android上,用Okhttp模擬下請求。程式碼比較多,而且本節主要是將Python,就只貼下大概的樣子。

花了一早上的的時間擼出了這個自動點點點,自動下注演示:

行吧,自動下注的點點點弄好了,接著就是掛機測試了,因為覺得穩賺的,於是我註冊了兩個賬號,分別充了300,掛著執行:

在一段小測後發現挺穩定的,於是乎把指令碼打成APK,讓基友也用起來,然後開始說各種幻想暴富的騷話。


接著就是掛機,BUG修復,和不斷的程式優化了,短短几天迭代了十幾個版本。接著把基友小B也忽悠進來。

為了早日實現完全自動化,在狗東買了本逆向的書,打算利用國慶破解一波介面,把指令碼掛到伺服器上,暴富似乎指日可待了,實際上卻是一步一步在將自己推向深淵...
第三章:噩耗
2018.9.30號晚上,看著掛了一整天的指令碼餘額1380元寶,不禁心中竊喜,畢竟有什麼比得上看著錢一點點變多令人愉悅麼,一想到把指令碼掛一晚上,第二天起來就可以2000+了,夢裡都會笑醒吧。

2018.10.1號的早上,沒有像往常那樣睡懶覺到十一二點,八點多就醒了,第一件事迫不及待的去看餘額,但是等待我的,卻不是如期的2000多,而是340。怎麼會這樣???不是很穩的嗎。立馬去翻歷史下注記錄:

排查後是下注操作有問題,大概的原因可能是: APP開獎延遲,時間並不準確,爬取開獎站點存在一定的延時,有時獲到的前十期的結果並不是最新的。 一想到:掛了好幾天,錢一下子就沒了:

本金用了900,減去剩下的340,我 虧了560 啊,怎麼行,必須得回本,昨晚是因為自動掛著才會這樣,手動就可以,但是340可以 抗的波數不多,需要更多的本金,我猶豫著要不要充多940進去,湊夠1280,在支付的頁面停頓了一會兒,後面腦子一熱手指一按, 就支付了。不得不說,越來越便捷的支付方式,讓人對錢的感覺越來越沒感覺,以前沒錢了,還要跑銀行ATM排隊取,錢取出來拿在 手上還會掂量掂量,錢是有分量的。而快捷支付讓人感覺錢就是一堆數字而已,越加容易衝動消費,在不知不覺中已經負債累累。 錢充之後,依舊掛著指令碼,而基友小A則去問別人拿新策略了。一邊繼續掛著,一邊開始排查程式錯誤,一開始還是挺穩健的,然而 就一下午的事情,我剛充的錢又敗完了,而小A充1000用的新策略也輸光了。

小A敗光了就放棄了,而我像魔怔了一樣,覺得是本金還是不夠,而且抗的波數不夠,既然害怕這種連續出現的情況, 為何我不直接跟連續呢,比如出現3次連續,我才開始繼續買連續,我又充了2560進去,能夠抗十三波一直不中。 同樣,測試初期都是好的,又掛了一晚上,事實證明,我還是太naive了,早上六點多起來還是賺的,睡了個 回籠覺,八點多起,發現錢又輸光了...心情一下跌落到谷底,唉,不知不覺就輸了四千多,四千多啊!!! 難過得飯都吃不下,狗東給我打電話讓我到樓下拿書都沒接,我到底在幹嘛啊?並立下各種Flag,再碰投機 倒把的東西,是狗。

第四章:真香
正當我想著算了,花4000多買個教訓吧,以後別相信這種投機倒把的東西就好,剩下幾天好好寫書,別再折騰這些東西。 然而世間萬物都離不開『 真香定律 』。

晚上小A又給我案例了一個新的APP

相比起之前的APP,這個是可以 1塊起投 的(之前的10塊)。

行吧,我又衝了 2426 進去,只是想回本...因為還沒有出策略,所以我還是用的之前那個平臺的策略買, 下注的基數小了,能抗的波數多了,就穩了,然而結果依舊是輸得一乾二淨。

小A開始瘋狂炫富:

在我的再三催促下下,小A終於把下注的策略畫出瞭如下這個下注流程圖。

行吧,流程圖看上去很複雜,其實捋一捋就是下述這樣的策略(只買單雙):
- 1.最新兩條記錄結果不一樣,連續三次一樣,連續八次一樣,買相反。
- 2.最新兩條記錄結果一樣,連續四次一樣,連續少於七次,買相同。
行吧,策略搞到手了,接著就是抓包和用Python編寫爬蟲來模擬下注了。
第五章:神兵加持
開啟APP,開發者工具開啟佈局邊界,內容面板沒有出現邊界,行吧不是原生Android,基本就是H5了。 開啟 Charlse抓包工具 抓下包(抓包教程可自行百度),可以看到APP陸陸續續發出的請求。

點開其中一個 lotteryOpenCache 可以看到對應的響應的結果如下( 瀏覽器外掛JSON-Handle ):

行吧,大概可以確定是每一把的開獎結果的介面了,接下來是看下傳遞的引數:

行吧,就是傳遞JSON資料,但是這個開獎結果要登入後才能訪問,肯定是存在cookies或者token這裡機制的, 果不其然,點選Cookies選項可以看到:

最後看下請求頭如下:

行吧,引數啥的,都有了,我們用Python requests庫 來模擬一波請求吧,看是否能得到對應的結果。
import requests as r headers = { 'origin': 'https://m.sfcappwz4.com', 'user-agent': 'Mozilla/5.0 (Linux; Android 8.1.0; OE106 Build/OPM1.171019.026; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/62.0.3202.84 Mobile Safari/537.36', 'content-type': 'application/json', 'cookie':'JSESSIONID=BDCD384F2DAD64E83BC1A6EBFCADB479', 'referer':'https://m.sfcappwz4.com/lottery/K3/OG1K3', 'x-requested-with':'com.bright.aiwprtuon' } def get_result(): resp = r.post('https://m.sfcappwz4.com/tools/_ajax/cache/lotteryOpenCache', headers=headers, json={"requirement": ["OG1K3"]}) print(resp.json()) if __name__ == '__main__': get_result() 複製程式碼
執行後輸出結果如下:

行吧,可以正確拿到資料,接著我們就該考慮如何獲得這個Cookie了,這裡後臺比較頑皮,它不是在登入成功後 分配Cookies,而是在進去APP的時候就分配了,後續如果登入成功才會一直使用這個Cookie。


後續的所有請求都是帶著這個cookie。

行吧,就是先訪問這個 getSiteInitData 介面,拿到響應頭裡的 set-cookie ,儲存,然後去執行登入, 接著我們來看下登入介面,也是傳遞的JSON字串:

一看引數大概就能猜出什麼了,依次是:賬號,密碼,有效碼,時間,是否預設登入,除了密碼,其他引數 都比較簡單,固定字串,時間戳,密碼的話,猜測是MD5,複製下字串往網上線上MD5解密網站一丟:www.cmd5.com/,果不其然:

行吧,接著就是模擬登入了,這裡用到 hashlib庫 進行md5加密:
import hashlib hl = hashlib.md5() def auto_login(): global accountId hl.update(account_pawd.encode(encoding='utf-8')) json_params = { 'loginName': account_name, 'loginPwd': hl.hexdigest(), 'validCode': '', 'validateDate': str(int(round(time.time() * 1000))), 'isdefaultLogin': 'true' } headers['referer'] = 'https://m.sfcappwz3.com/login' headers['x-requested-with'] = 'com.bright.aiwprtuon' resp = r.post('https://m.sfcappwz3.com/tools/_ajax/login', json=json_params, headers=headers) # 獲取accountId accountId = resp.json()['data']['user']['userDetail']['accountId'] 複製程式碼
模擬登入成功後,帶著Cookie就可以去訪問所有的介面了,其他的介面也是如法炮製,設定請求頭,傳對應 的資料,這裡就不重複複述了,都是些繁瑣的操作,加上策略直接給完整指令碼。
""" 自動投指令碼 """ import hashlib import time import requests as r from apscheduler.schedulers.blocking import BlockingScheduler cookies = '' accountId = ''# 使用者id playIds = ['K3002001010', 'K3002001011']# 玩的種類,依次是大小和單雙 is_first_open = True# 是否剛開啟 result_list = []# 儲存所有結果的陣列 purchase_base = 100# 購買基數 ratio = 1.96# 中獎倍率 price_list = []# 投入金額 cur_price_pos = 0# 當前投入金額遊標 cur_purchase_type = ''# 當前下注的類別 account_name = ''# 賬號 account_pawd = ''# 密碼 cur_money = 0# 當前餘額 hl = hashlib.md5() headers = { 'origin': 'https://m.sfcappwz4.com', 'user-agent': 'Mozilla/5.0 (Linux; Android 8.1.0; OE106 Build/OPM1.171019.026; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/62.0.3202.84 Mobile Safari/537.36', 'content-type': 'application/json', 'accept': '*/*', 'accept-language': 'zh-CN,en-US;q=0.9' } # 1.模擬訪問拿到Cookies def fetch_cookies(): global cookies resp = r.post('https://m.sfcappwz4.com/tools/_ajax/getSiteInitData', headers=headers, json={"requirement": ["footerConfig", "helpConfig", "h5BannerList", "gradeList", "noticeData", "rankingList", "activityConfig", "defaultPhotoList", "lotteryConfig", "lotteryList", "rewardData", "config"], "cacheData": {}}) cookies = resp.headers['set-cookie'].split(';')[0] headers['cookie'] = cookies # 2.模擬登陸 def auto_login(): global accountId hl.update(account_pawd.encode(encoding='utf-8')) json_params = { 'loginName': account_name, 'loginPwd': hl.hexdigest(), 'validCode': '', 'validateDate': str(int(round(time.time() * 1000))), 'isdefaultLogin': 'true' } headers['referer'] = 'https://m.sfcappwz3.com/login' headers['x-requested-with'] = 'com.bright.aiwprtuon' resp = r.post('https://m.sfcappwz3.com/tools/_ajax/login', json=json_params, headers=headers) # 獲取accountId accountId = resp.json()['data']['user']['userDetail']['accountId'] # 3.獲得開獎結果 def get_result(): global is_first_open global result_list headers['origin'] = 'https://m.sfcappwz4.com' headers['cookie'] = cookies headers['referer'] = 'https://m.sfcappwz4.com/lottery/K3/OG1K3' headers['x-requested-with'] = 'com.bright.aiwprtuon' resp = r.post('https://m.sfcappwz4.com/tools/_ajax/cache/lotteryOpenCache', headers=headers, json={"requirement": ["OG1K3"]}) num_list = [] # 獲取最新期數 curissueNo = int(resp.json()['data']['backData']['lotteryOpen'][0]['issueNo']) + 1 curissueNo_str = str(curissueNo) # 判斷是不是最後一期,是的話更新為第二天的第一期 if curissueNo_str[8:len(curissueNo_str)] == '1441': curissueNo = time.strftime('%Y%m%d') + '001' print("最新期數:", curissueNo) for n in resp.json()['data']['backData']['lotteryOpen']: num_list.append(n['count']) if len(result_list) == 0: result_list.extend(num_list) elif len(result_list) == 10: result_list.insert(0, num_list[0]) result_list.pop() return curissueNo # 4.獲取當前餘額的方法 def get_money(): global cur_money headers['origin'] = 'https://m.sfcappwz4.com' headers['cookie'] = cookies headers['referer'] = 'https://m.sfcappwz4.com/userCenter/userCenterMenu' headers['x-requested-with'] = 'com.bright.aiwprtuon' resp = r.post('https://m.sfcappwz4.com/tools/_ajax//getUserBanlance', headers=headers, json={"userName": account_name}) cur_money = float(resp.json()['data']['money']) print("當前餘額:", resp.json()['data']['money']) init_purchase_list() # 5.自動下注的方法 def auto_buy(choose, curissueNo): global cur_purchase_type cur_purchase_type = choose playId = playIds[1]# 下注類別id buy_price = purchase_base # 如果遊標到達危險閥值,只投10塊 if cur_price_pos >= len(price_list) - 1: if len(price_list) == 1: buy_price = 1 else: buy_price = 10 else: buy_price = price_list[cur_price_pos] json_params = { 'accountId': accountId, 'clientTime': str(int(round(time.time() * 1000))), 'gameId': 'OG1K3', 'issue': str(curissueNo), 'item': [str({ 'methodid': 'K3002001001', 'nums': 1, 'rebate': '0.00', 'times': buy_price, 'money': buy_price, 'playId': [playId], 'mode': '1', 'issueNo': str(curissueNo), 'codes': choose })] } headers['origin'] = 'https://m.sfcappwz3.com' headers['cookie'] = cookies headers['referer'] = 'https://m.sfcappwz3.com/lottery/K3/OG1K3' headers['x-requested-with'] = 'com.bright.aiwprtuon' resp = r.post('https://m.sfcappwz3.com/tools/_ajax/OG1K3/betSingle', headers=headers, json=json_params) if resp.json()['code'] != 'success': print(resp.text) print("下注【", choose, "】 金額【", buy_price, "】") print('=' * 50) # 6.下注演算法 def predict(): global result_list global cur_price_pos global cur_purchase_type curissueNo = get_result() print("開獎數字:", result_list) size_str_list = [] single_str_list = [] for num in result_list: if num > 10: size_str_list.append('大') else: size_str_list.append('小') if num % 2 == 0: single_str_list.append('雙') else: single_str_list.append('單') print("大小結果:", size_str_list) print("單雙結果:", single_str_list) # 獲取第一項 size_first = size_str_list[0] single_first = single_str_list[0] # 判斷上一把有沒有中,中了重置投錢下標 if cur_purchase_type == '': pass elif cur_purchase_type == size_first or cur_purchase_type == single_first: cur_price_pos = 0 print("中了!!!") get_money() else: cur_price_pos += 1 print("沒中!!!") # 判斷相同記錄條數 repeat_count = 1 for single in single_str_list[1:8]: if single == single_first: repeat_count += 1 else: break if repeat_count in [1, 3, 8]: if single_first == '單': auto_buy("雙", curissueNo) else: auto_buy("單", curissueNo) elif repeat_count == 2: if single_str_list[2] == single_str_list[3]: if single_first == '單': auto_buy("雙", curissueNo) else: auto_buy("單", curissueNo) else: auto_buy(single_first, curissueNo) elif repeat_count in [4, 5, 6, 7]: auto_buy(single_first, curissueNo) # 構建倍率列表,保證贏前後收益 - 投入 > 購買基數 def init_purchase_list(): global price_list cost = purchase_base price_list = [purchase_base] # 其他進行動態計算 for i in range(10): # 購買價格其實區間(2倍) start_price = purchase_base * (2 ** i) # 購買價格極限區間(3倍) end_price = purchase_base * (3 ** i) # 保證沒把不虧就行 for j in range(start_price, end_price + 1): if j * ratio - cost - j > 1 and cost + j <= round(cur_money / 2): cost += j price_list.append(j) break print("生成投入金額列表:", price_list) # 重新登入 def login_again(): print("重新登入!") fetch_cookies() auto_login() if __name__ == '__main__': init_purchase_list() fetch_cookies() auto_login() predict() scheduler = BlockingScheduler() # 每隔60s下注一次 scheduler.add_job(predict, 'interval', max_instances=10, seconds=60) scheduler.start() 複製程式碼
接著就可以把指令碼丟到雲服務上執行了,這裡用的是nohup命令讓指令碼在後臺執行。
nohup python3 -u xiaozheng.py > xiao.out 2>&1 & 複製程式碼
然後執行 tail -f xiao.out 或者 cat xiao.out 可以檢視到日誌輸出記錄。

因為用的是官方的介面,也沒有出現介面延遲的問題...一切貌似就這樣順理成章的執行著, 然而最後的結果,依舊躲不過輸得一乾二淨。

我開始反省,為何劇本的最後結局都是輸光,人投也是採用相同的策略,人賺,指令碼投就虧。
- 1.指令碼24小時跑, 策略固定 ,而彩票規律可能是不斷變化的,人偶爾也會變通下;
- 2.人不會一直買,而且出現連續不中的時候會停止觀察,找稍微穩定一點的時間點切入;
既然這樣固定策略必涼,為何不讓別人在投的時候,順道也幫我投了呢, 愛屋及烏 !
第六章:愛屋及烏
在我輸光輸淨後小A又瘋狂的炫富:

既然小A那麼穩,為何不讓他投的時候幫我也投了呢,坐收漁翁之利。行吧,既然介面我都有了,為何不能 自己寫個APP呢?所以有了下面這樣的計劃:
- 1.先寫一個APP,小A登入的時,順帶也登入下我的賬號,然後下注的時候,我的賬號也跟著下注。
- 2.編寫API介面給APP呼叫,小A下注的時候順帶呼叫這個介面,後臺接收到下注資訊,批量下注。
正當我準備開啟自己寫好的APP截個圖的時候,發現自己寫的APP竟然登入不了了,嘖嘖,應該是昨晚 維護了,把介面改了。

行吧,頑皮,批量替換下基地址,登入成功後,竟然又自動退出來了。利用 Android Profiler 效能調優工具 抓一波包,login介面是success的,lotteryOpenCache的響應碼竟然是 nologin :

猜測是Cookie變了,開啟它們的APP登入,開啟結果頁,抓包看看:

果不其然,多了個sto-id-20480,左側搜下這個串東西,在哪裡拿到的,過濾下,只檢視響應頭:

定位下可以找到:

接著是另外一段:JSESSIONID=53287B9851CF658D255A029F1426C82A,如果炮製找出是在哪個介面 的響應頭set-cookie的,如下:

總結下就是依次:
- 1.訪問:m.suibianwanba.com/ 拿sto-id
- 2.訪問: m.suibianwanba.com/tools/_ajax… 拿JSESSIONID
後續所有訪問請求頭cookie設定為這兩個字串即可,同樣貼出大概的程式碼,顯示介面:

然後是呼叫頁面:

APP的下注頁面:

頁面簡陋,但是功能齊全,好吧,接著就把APP丟給小A先幫我投著先,接下來就是寫介面給自己的APP呼叫了, 不然怎麼弄成批量的方式,這裡用到的Python輕量級Web庫: Flask 。
直接通過pip命令進行安裝即可: pip install flask
,官方的一個最簡單示例:
# coding=utf-8 from flask import Flask app = Flask(__name__) @app.route("/") def hello(): return "Hello Flask!" if __name__ == "__main__": app.run() 複製程式碼
執行後開啟127.0.0.1:500可以看到返回了Hello Flask!

關於Flask的使用,這裡不會講解,因為也不涉及到什麼高深內容,更多內容可以自行查閱官方文件: flask.pocoo.org/docs/1.0/ 簡單說下我們要做的事情,需要兩個介面(傳參用Json字串)。
- add_account : 新增賬號 , POST ,傳參:使用者名稱與密碼,拿到資料後模擬登入,拿到對應的Cookies,存到SQL/">MySQL資料庫中;
- bet : 下注 , POST ,傳參:下注型別與下注金額,拿到資料後批量呼叫下注方法。
行吧,官方貌似又改介面了,又重新抓了一波包(其實就是改了跟地址而已)...

限於篇幅,不太過具體的去講解程式碼,顯示工程的結構:

建立了一個app目錄,下面有一個 __init__.py
檔案:
from flask import Flask from flask_sqlalchemy import SQLAlchemy app = Flask(__name__) app.config.from_object('config') db = SQLAlchemy(app) from app import models, views 複製程式碼
程式碼中初始化了一個Flask例項,讀取配置檔案,建立了一個SQLAlchemy例項,匯入了models和views檔案。 config.py內容如下:
SQLALCHEMY_DATABASE_URI = 'mysql+pymysql://root:Jay12345@localhost:3306/q9g' SQLALCHEMY_TRACK_MODIFICATIONS = True 複製程式碼
然後是定義資料結構: 賬戶名+密碼+Cookie+賬戶ID(下注用) ,這裡並不使用原生SQL語句去操作MySQL資料庫,而是使用ORM框架—— SQLAlchemy ,直接通過pip命令安裝即可: pip install flask-sqlalchemy
,model.py的程式碼如下:
from app import db class User(db.Model): __tablename = 'user' __table_args__ = {'useexisting': True} _id = db.Column(db.Integer, primary_key=True, autoincrement=True) account = db.Column(db.TEXT) pwd = db.Column(db.TEXT) cookies = db.Column(db.TEXT) accoutId = db.Column(db.TEXT) @property def id(self): return self.id if __name__ == '__main__': db.create_all() 複製程式碼
還有安裝一個 flask-script 模組,用於通過命令列來操作Flask,同樣可以通過pip命令進行安裝: pip install flask-script
,新建一個manager.py,在裡面啟用Flask專案。
from app import app from flask_script import Manager manager = Manager(app) if __name__ == '__main__': manager.run() 複製程式碼
準備工作差不多了,接著開始編寫我們的登入介面:
import hashlib import time import requests as r from flask import request, jsonify from app import app, db from app.models import User headers = { 'origin': 'https://m.suibianwanba3.com', 'user-agent': 'Mozilla/5.0 (Linux; Android 8.1.0; OE106 Build/OPM1.171019.026; wv) AppleWebKit/537.36 ' '(KHTML, like Gecko) Version/4.0 Chrome/62.0.3202.84 Mobile Safari/537.36 ', 'content-type': 'application/json', 'x-requested-with': 'com.bright.extnetiop', } @app.route("/add_account", methods=['GET', 'POST']) def add_account(): account = '' pwd = '' if request.method == 'GET': account = request.args.get("account") pwd = request.args.get("pwd") elif request.method == 'POST': account = request.json['account'] pwd = request.json['pwd'] # 密碼加密 hl = hashlib.md5() hl.update(pwd.encode(encoding='utf-8')) # 例項化使用者例項 user = User() user.account = account user.pwd = hl.hexdigest() msg = auto_login(user) return jsonify( {'code': '200', 'data': [{'account': account, 'pwd': pwd}], 'msg': (msg if msg is not None else "登入成功")}) @app.route("/bet", methods=['GET', 'POST']) def bet(): choose = '' price = '' cur_no = '' if request.method == 'GET': choose = request.args.get("choose") price = request.args.get("price") cur_no = request.args.get("cur_no") elif request.method == 'POST': choose = request.json['choose'] price = request.json['price'] cur_no = request.json['cur_no'] # 批量下注 result_list = [] users = User.query.all() for user in users: result_list.append(auto_bet(user, choose, price, cur_no)) return jsonify({'code': '200', 'data': result_list, 'msg': '訪問成功'}) # 獲取Cookie def fetch_cookies(user): if 'cookie' in headers: headers.pop("cookie") cookie_resp = r.post("https://m.suibianwanba3.com/tools/_ajax/cache/getSiteInitData", headers=headers, json={"requirement": ["footerConfig", "helpConfig", "h5BannerList", "gradeList", "noticeData", "rankingList", "activityConfig", "defaultPhotoList", "lotteryConfig", "lotteryList", "rewardData", "config"], "cacheData": {}}) if cookie_resp is not None: cookie = cookie_resp.headers.get('set-cookie') if cookie is not None: headers['cookie'] = cookie.split(';')[0] user.cookies = cookie.split(';')[0] # 模擬登入 def auto_login(user): fetch_cookies(user) json_params = { 'loginName': user.account, 'loginPwd': user.pwd, 'validCode': '', 'validateDate': str(int(round(time.time() * 1000))), 'isdefaultLogin': 'true' } resp = r.post("https://m.suibianwanba3.com/tools/_ajax/login", headers=headers, json=json_params) if resp is not None: if resp.json()['code'] == 'success': user.accoutId = resp.json()['data']['user']['userDetail']['accountId'] db.session.add(user) db.session.commit() else: print(resp.json()['msg']) return resp.json()['msg'] 複製程式碼
執行專案,然後使用瀏覽器或者PostMan模擬請求:

呼叫這個介面數次,獲取賬戶名+密碼+Cookie+賬戶ID,然後存入資料庫,存入後的結果如圖所示:

可以,都存到資料庫裡,接著我們來編寫下注介面:
playIds = ['K3002001010', 'K3002001011']# 下注種類,依次為大小和單雙 @app.route("/bet", methods=['GET', 'POST']) def bet(): choose = '' price = '' cur_no = '' if request.method == 'GET': choose = request.args.get("choose") price = request.args.get("price") cur_no = request.args.get("cur_no") elif request.method == 'POST': choose = request.json['choose'] price = request.json['price'] cur_no = request.json['cur_no'] # 批量下注 result_list = [] users = User.query.all() for user in users: result_list.append(auto_bet(user, choose, price, cur_no)) return jsonify({'code': '200', 'data': result_list, 'msg': '訪問成功'}) # 下注 def auto_bet(user, choose, price, cur_no): play_id = playIds[0] if choose in ['單', '雙']: play_id = playIds[1] json_params = { 'accountId': user.accoutId, 'clientTime': str(int(round(time.time() * 1000))), 'gameId': 'OG1K3', 'issue': str(cur_no), 'item': [str({ 'methodid': 'K3002001001', 'nums': 1, 'rebate': '0.00', 'times': price, 'money': price, 'playId': [play_id], 'mode': '1', 'issueNo': str(cur_no), 'codes': choose })] } headers['cookie'] = user.cookies resp = r.post('https://m.suibianwanba3.com/tools/_ajax/OG1K3/betSingle', headers=headers, json=json_params) print(resp.json()) return resp.json() 複製程式碼
同樣模擬請求一波,看看響應的結果:

這裡沒有做cookie過期的相關處理邏輯,做的話也比較簡單,對異常資訊進行過濾,然後呼叫登入相關的 方法重新登入,更新一下資料庫裡賬戶對應的Cookie即可。
行吧,本地介面跑通了,接下來把專案部署到伺服器上,讓外網能夠訪問我們的介面,感覺用Docker部署 就有點裝逼了,所以這裡還是隻用FTP/SFTP工具把程式碼上傳到雲伺服器上。
第七章:言聽計從
官方維護...明天再更
第八章:屠龍計劃
官方維護...明天再更
第九章:終章(填坑)
1.到底什麼是洗錢 2.彩票開獎結果真的是隨機的嗎? 等等...
Join in
歡迎大家加入小豬的Python學習交流群一起討論,可以新增下述的機器人小號 RobotPig ,驗證資訊裡包含: Python , python , py , Py , 加群 , 交易 , 屁眼 中的一個關鍵詞即可通過;傳送『加群』加入群聊。
或者在公眾號『 摳腚男孩 』中傳送加群~

參考文獻:
- 為什麼“十賭九輸”?
- 百度百科:概率波動論