大概在2年零兩個月前,我寫過一個論壇灌水機,前幾天,因為朋友要網絡投票,又拿出來用了一次,改了下配置檔案,又可以當投票機用了。
由于工作的關系,現在在UNIX上做開發了,我嘗試了用UNIX重寫了一個投票機,深深的體會到,什麼叫“一行shell相當于1萬行C代碼了”。UNIX天生就是為程式員服務,來幫助他們完成看似不可能的任務的。
下面我以我重寫的這個投票機為例,來闡述下UNIX的做事風格。
一個投票機,或曰灌水機,需要具備這麼些特性:get/post、資料生成、流程控制、并發支援、代理支援、驗證碼識别等等。這些特性基本上是越往後越難的,看看UNIX是怎麼用簡單的方式來實作他們的:
get/post
UINX提倡用短小精悍的小程式組合來完成複雜的任務,對于使用HTTP協定通路網絡,沒有什麼比wget更合用的啦。有UNIX環境的可以直接在shell下開始嘗試,隻有windows的也可以裝一個cygwin的模拟UNIX環境來試驗。
在提示符下直接輸入wget,回車
$ wget
wget: missing URL
Usage: wget [OPTION]... [URL]...
Try `wget --help' for more options.
它提示你缺少URL參數,用--help來獲得更多資訊,注意UNIX絕大多數指令都可以用--help來獲得幫助,而不是dos傳統的/?。
如果你輸入wget --help後,你會得到所有支援的參數清單。這些參數清單對于不怎麼會得人來說,可能還是太簡略了,輸入man wget可以獲得更加詳細的解釋及示例。許多UNIX程式都可以用man加上指令名來得到詳細的使用說明。
man的簡要使用如下:
用上下左右光标鍵浏覽,q鍵退出。g到首行,G到末行,/開始查找,例如/proxy再回車,表示查找含有proxy處,n向下查找下一個,N向上查找下一個。
不看幫助,讓我們來猜想最簡單的用法,沒錯,就是這樣
$wget www.csdn.net
--22:10:01-- http://www.csdn.net/
=> `index.html'
Resolving www.csdn.net... 211.100.21.179
Connecting to www.csdn.net|211.100.21.179|:80... connected.
HTTP request sent, awaiting response... 200 OK
Length: 128,038 (125K) [text/html]
100%[====================================>] 128,038 4.29K/s ETA 00:00
22:10:46 (2.90 KB/s) - `index.html' saved [128038/128038]
這是不帶選項的用法,網頁自動儲存到檔案,詳細過程顯示在終端上。好了,基本的get完成了,算算C代碼要幾行?
資料生成
對于灌水來說,上節自然是不夠用的,至少也需要發送POST資料的功能吧?
這個簡單,輸入man wget,搜尋post,就能找到答案,這裡給個例子:
$wget www.xxx.com --post-data='a=1&b=2'
這裡有個地方需要注意的就是,對于指令行中有&的,一定要用''把它引起來,否則會被shell解釋成在背景運作,如上面的例子,如果沒有'',那麼shell認為是在背景執行"wget www.xxx.com --post-data=a=1",然後又執行b=2這樣一個指令。
可以傳遞post資料了,讀者一定還不滿意,因為如果想讓資料變化,例如a要從1變化到1000,總不能手工輸入吧?
流程控制
好了,這要用到基本的shell程式設計了。我這裡就簡要的給出例子:
編輯一個shell檔案start.sh,内容如下
#! /bin/sh
counter=0
while [ $counter -lt $1 ]
do
counter=`expr $counter + 1`
wget "www.xxx.com" "--post-data=a=$counter&b=2"
done
儲存後,在shell下執行chmod +x start.sh, 使這個腳本可執行
然後執行./start.sh 1000,就達到了我們的目的。
(這裡沒什麼好解釋的,我也不喜歡shell的這種循環、比較與指派的文法,不過為了它提供的其他好處,隻好忍忍啦,畢竟任何事物都不是完美的,需要參考shell文法時可以查一本書:好像叫<<UNIX和LINUX shell程式設計指南>>,有電子版)
也許讀者看到這裡,還是覺得UNIX沒什麼好的,就剛才這些例子來說,無非是有了一個用C寫好了的wget程式,我用socket或者用WININET來寫,從功能上來說,哪樣都不少,而且寫熟練了,速度也不慢呀,那我們來看看
并發支援
UNIX重來都不考慮多線程這種煩人的東西,諸位把多線程的東西搞清楚了,又調試出了幾個多線程的Bug,估計頭上的白發沒少生。UNIX下把上一節那個腳本在背景多運作幾次,就實作并發了,簡單吧,煩人的事情就該交給作業系統去做,而不是在windows中提倡的去搞一個複雜無比的多線程程式。
代理支援
我知道上面的話一定不能讓人心服,現在我舉這個例子,我覺得能征服至少一半讀者。考慮這樣的任務,你的論壇會限制你灌水的IP,同一個IP有發帖間隔限制,或者你要投票的位址限制每個IP隻能投一票,你怎麼辦,找代理。那麼你覺得需要多長時間能把這個功能完成呢?例如說從www.proxycn.com中取大量的代理位址,1天?1000行代碼?有興趣的讀者可以先不往下看,看看在windows的思維方式下,能否在五分鐘内完成這個任務。
我在剛嘗試這件事時,我也沒考慮要多快,但是我還真就五分鐘内做成了。
第一分鐘,我用浏覽器打開www.proxycn.com,找到左下角的代理綜合搜尋,類型選HTTP,狀态選活動,點選搜尋按鈕,出來3149個結果,共63頁,就是它們了。
第二分鐘,我複制第2頁的快捷方式作為wget的參數
$wget -O result 'http://www.proxycn.com/proxysearch.php?action=proxys&searchsubmit=yes&address=&port=&AddDateBefore=&AddDateAfter=&status=1'/
'&isedu=&type=HTTP&type2=&location=&page=2'
很快,第二頁被下載下傳下來存入result檔案了。
我開始嘗試
$cat result
輸出了整個檔案的内容,翻動一下螢幕,發現了一個好東西onDblClick="clip('219.165.115.186:3128'),運氣還不錯。
接着
$cat result | grep /d{1.3}
無結果
$cat result | grep '/d{1,3}'
無結果
$grep --help | grep perl
-P, --perl-regexp PATTERN is a Perl regular expression
$cat result | grep -P '/d{1,3}'
好了,一堆結果
$ cat result | grep -P '/d{1,3}/./d{1,3}'
這下結果很靠譜,把它比對完
$ cat result | grep -P '/d{1,3}/./d{1,3}/./d{1,3}/./d{1,3}:/d{2,5}'
這下比對了整個IP位址加端口了,但是grep是輸出整行的呀,有多餘的輸出,
第三分鐘
$grep --help
翻看Output control那一部分,找到一行
-o, --only-matching show only the part of a line matching PATTERN
試一下
$ cat result | grep -P '/d{1,3}/./d{1,3}/./d{1,3}/./d{1,3}:/d{2,5}' -o
203.160.1.47:554
66.240.123.59:3128
221.112.144.13:8080
165.228.129.10:3128
203.198.69.124:3128
203.160.1.44:554
125.247.118.226:3128
203.160.1.43:554
203.160.1.52:554
195.55.133.76:8080
202.146.67.238:8080
...成功了,現在把指令組合一下
第四分鐘
編輯proxy.sh檔案如下
#! /bin/sh
counter=0
while [ $counter -lt $1 ]
do
counter=`expr $counter + 1`
wget -O - "http://www.proxycn.com/proxysearch.php?action=proxys&searchsu
bmit=yes&address=&port=&AddDateBefore=&AddDateAfter=&status=1&isedu=&type=HTTP&t
ype2=&location=&page=$counter" 2>/dev/null | grep -P '/d{1,3}/./d{1,3}/./d{1,3}/
./d{1,3}:/d{2,5}' -o
done
然後執行chmod +x proxy.sh
試一下
$./proxy.sh 1
沒有問題,現在正式取了
第五分鐘
$ ./proxy.sh 63 > result &
[1] 1680
這表示任務在背景執行了,
我可以用這個指令來觀察結果
$ tail -f result
大約過了一分鐘,背景的任務執行完了,我統計一下行數
$ wc -l result
1749 result
還真準,剛好3149個結果