真是很慚愧。看到網上那多哥們如何如何用自己寫的工具、腳本,買到了回家的車票至少還是都是卧鋪的。但是我水平不行,只買到了硬座的(已完成支付,收到了12306的短信通知但未換紙質車票)。既然只買到硬座的而以還有啥好說的呢?更何況這是博客園首頁啊!其實我只想說我其實也做了不少工作,可能算是技術不行、也可能是繞了很多彎路、還有可能就是12306或95105105雖然很爛但其實他們也是在不斷的改進的,今天找到的方法說不定明天效果就不好。
首先講講做了哪些工作:
12306.cn
第一次訪問了該網站之后我就想應該可以寫一個自動化的工具,可以自動幫我去進行查票、訂票。最開始的時候想的前面幾步都是手工實現的,然后通過構造發送最終的訂票數據。來模擬提交。但不是很理想,雖然把本地所有的cookie信息都模擬提交了但還是返回不了正確的訂票信息。
於是就想寫WinForm之類的工具,遇到了驗證碼。考慮過用人工的方式但還是想着如果能夠破解圖形驗證碼的話效果應該更好。無奈花了一、兩天的時間來搞驗證碼,做一過比如把圖片信息變為0和1分布的矩陣,通過查找相對位置的辦法來識別,但是識別效果並不好,因此放棄。
最近這兩天在網站方面已經不考慮再寫什么工具了,注冊了三個賬戶,把要買的人員全部加為聯系人,在查票的時候掛上如下簡單的腳本,進行自動刷新而以。
setInterval(" this.document.getElementById('main').contentWindow.document.getElementById('submitQuery').click();",10000)
95105105
雖然電話訂票的渠道知道的時間要比網上稍晚點,但網上看了一些文章好像用電話訂票的成功率要高點似的。剛開始想的也是自動來實現的,但是訂票環節中加了個驗證碼,全流程的自動化暫時是行不通了。但是由於之前接觸過Skype相關的API,因此還是寫了一個簡單的外掛,進行自動化重撥和身份證輸入等功能。寫這個外掛過程中我還特決購買了800多分鍾的skype套餐(還有一半未使用完)。
Skype通話錄音與盡快重撥代碼(需要安裝Skype4COM)

/// 忙重撥
/// </summary>
/// <param name="phoneNum"> 電話號碼,例如:01095105105 </param>
public void ReDial( string phoneNum)
{
try
{
objSkpe = new Skype();
objSkpe.Attach( 7, true);
objCall = objSkpe.PlaceCall(phoneNum);
// 設置聲音文件輸出目錄
string outPutFolder = System.Environment.CurrentDirectory + " \\audio\\ ";
if (!Directory.Exists(outPutFolder))
{
Directory.CreateDirectory(outPutFolder);
}
string waveFile = outPutFolder + phoneNum + " _ " + DateTime.Now.ToString( " yyyyMMdd_hhmmss ") + " .wav ";
// 錄音
objCall.set_OutputDevice(TCallIoDeviceType.callIoDeviceTypeFile, waveFile);
// WriteLog("撥號中...");
TimeSpan startTime = new TimeSpan(DateTime.Now.Ticks);
// 循環直到通話中
while (objCall.Status != TCallStatus.clsInProgress)
{
// 判斷撥號超時
if (objCall.Status == TCallStatus.clsRouting)
{
Thread.Sleep( 1000);
TimeSpan stopTime = new TimeSpan(DateTime.Now.Ticks);
TimeSpan val = stopTime.Subtract(startTime).Duration();
if (val.TotalSeconds > 30)
{
objCall.Finish();
}
continue;
}
// 忙或關閉等各種狀態重播
if (objCall.Status == TCallStatus.clsBusy
|| objCall.Status == TCallStatus.clsFailed
|| objCall.Status == SKYPE4COMLib.TCallStatus.clsFinished
|| objCall.Status == SKYPE4COMLib.TCallStatus.clsCancelled
|| objCall.Status == SKYPE4COMLib.TCallStatus.clsEarlyMedia
|| objCall.Status == SKYPE4COMLib.TCallStatus.clsRinging
)
{
// 如果是忙音,則需要先掛斷再重撥
if (objCall.Status == SKYPE4COMLib.TCallStatus.clsEarlyMedia || objCall.Status == SKYPE4COMLib.TCallStatus.clsRinging)
{
objCall.Finish();
}
// WriteLog("忙重撥...");
Thread.Sleep( 1000);
ReDial(phoneNum);
}
}
// WriteLog("通話中...");
}
catch (Exception exp)
{
// WriteLog("Error:" + exp.Message);
}
}
雖然流程自動化系統比較難實現,但其實我也在這方面做了一些努力,比如動用了微軟的SpeechSDK來參與我的搶票工作,以下是調用SppechSDK識別聲音到文本的結果展示。
但是由於目前還未找到直接將Skype的流轉為SpeechSDK可以識別的流,因此該方案目前暫無進展。
效果:
12306.cn 本文標題所指的硬座其實寫的工具都沒有派上用場,最后還是通過“人工”的方法來訂到的,可惜提交了不少於50次的卧鋪訂單但還是無法成功訂到。
95105105 開發了不少功能,但是最實是實用的還是自動重播那一項,該渠道於昨天(1月18日)幫一朋友買到了臨客的卧鋪,也還算是聊以慰籍。本來今天還想給我自己訂的無奈很不給力兩個Skype從7:58開始撥到8:30才撥通不說,好幾次都確認訂票信息了,系統直接給我提示“系統繁忙,訂票請按1”,崩潰了。。。。(不更早一點撥號是因為之前發現7點多撥的號到8點以后還是訂不了最新預售期內的票,網站好像也有這個問題。他是根據登錄時間來定的而不是當前時間,所以早撥也沒用。)
總結一下:
網站和電話其實這兩個方向我都了相應的探索但基本都屬於無功而返,反思這次做為一個程序員的的搶票行動,我覺得有以下幾點是需要考慮的:
1. 開發工作脫離目標
目標雖然很清晰,但是在實際開發過程中很容易進了死胡同里,特別是感覺如果解決了很有成就感的技術問題。當然還有可能是由於技術水平有限,應該有高手早就解決了我遇到的這些問題了。
2.少即是多,准備的東西太多,搶票時手忙腳亂的。看看我的工作台快照
無論怎樣。我覺得此次搶票行動比起在車站排除買票,還是意思的多的。最后,祝大家都能買到中意的車票平安回家,新年快樂!