由於要準備測試數據,不得不大量爬取某個網站的內容。爲了防止被封,特意將爬蟲設計爲單線程同步的爬蟲。結果在爬了大約3萬個頁面的時候,對方發回Access Denied。等一段時間後再啓動爬蟲,結果還是Access Denied。這時才明白這樣的想法太天真了,當初就應該找其它方法來避免纔對。而本文則記述了這些其它方法。
1. 僞裝user agent
User agent 是HTTP協議的中的一個字段, 其作用是描述發出HTTP請求的終端的一些信息。 服務器通過這個字段就可以知道要訪問網站的是什麼人了。每個瀏覽器,每個正規的爬蟲都有其固定的user agent,因此只要將這個字段改爲這些知名的user agent,就可以成功僞裝了。不過,不推薦僞裝知名爬蟲,因爲這些爬蟲很可能有固定的IP,如百度爬蟲。與此相對的,僞裝瀏覽器的user agent是一個不錯的主意,因爲瀏覽器是任何人都可以用的,換名話說,就是沒有固定IP。推薦準備若干個瀏覽器的user agent,然後每次發送請求的時候就從這幾個user agents中隨機選一個填上去。
IE的幾個user agent如下:
Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.0)
Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.2)
Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)
設置代碼如下(假設使用JAVA + HttpClient 4.1.2):
HttpGet getMethod = new HttpGet("URL");
getMethod.setHeader("User-Agent", "user agent內容");
2.登陸網站
雖然有些網站不登陸就能訪問,但是它一檢測到某IP的訪問量有異常,就會馬上提出登陸要求。如果是不帶驗證碼的,那麼果斷登陸吧。不過,在登陸之前要做些準備——查清楚POST登陸請求時要附帶哪些參數。我的做法是先用badboy錄製登陸過程,然後將這一過程導出爲jmeter文件,最後用jmeter查看登陸所需的參數。查完後,就可以登陸,具體如下所示:
DefaultHttpClient httpclient = newDefaultHttpClient();
HttpPost postMethod= new HttpPost("http://passport.cnblogs.com/login.aspx"); //注意用post//登陸博客園所需要的參數
List<NameValuePair> nvps = new ArrayList<NameValuePair>();
nvps.add(new BasicNameValuePair("tbUserName", "風炎"));
nvps.add(new BasicNameValuePair("tbPassword", "zero"));
nvps.add(new BasicNameValuePair("btnLogin", "登 錄"));
nvps.add(new BasicNameValuePair("__EVENTTARGET", ""));
nvps.add(new BasicNameValuePair("__EVENTARGUMENT", ""));
nvps.add(new BasicNameValuePair("__VIEWSTATE", "/wEPDwULLTE1MzYzODg2NzZkGAEFHl9fQ29udHJvbHNSZXF1aXJlUG9zdEJhY2tLZXlfXxYBBQtjaGtSZW1lbWJlcm1QYDyKKI9af4b67Mzq2xFaL9Bt"));
nvps.add(new BasicNameValuePair("__EVENTVALIDATION", "/wEWBQLWwpqPDQLyj/OQAgK3jsrkBALR55GJDgKC3IeGDE1m7t2mGlasoP1Hd9hLaFoI2G05"));
nvps.add(new BasicNameValuePair("ReturnUrl", "http://www.cnblogs.com/"));
nvps.add(new BasicNameValuePair("txtReturnUrl", "http://www.cnblogs.com/"));
postMethod.setEntity(newUrlEncodedFormEntity(nvps, HTTP.UTF_8));
HttpResponse response= httpclient.execute(postMethod);
由於httpClient會自動管理cookie,所以接下來直接get或者post就行了。
3. 使用代理
如果對方用某段時間內某IP的訪問次數來判定爬蟲,然後將這些爬蟲的IP都封掉的話,以上僞裝就失效了。對方的這個思路隱含着一個假設:爬蟲的訪問量必然比正常用戶的大很多,因而只要使這個假設不成立就可以了。這時就該代理上場了。所謂代理就是介於用戶與網站之間的第三者:用戶先將請求發到代理,然後代理再發到服務器,這樣看起來就像是代理在訪問那個網站了。這時,服務器會將這次訪問算到代理頭上。同時用多個代理的話,單個IP的訪問量就降下去了,於是就有可能逃過一劫。不過,這個方法最大的問題就是找到穩定的代理(有錢買代理的,可以無視這句話)。我目前是在無憂代理找,但找到的大部分都不能用,少部分能用的也不穩定。求分享好用的免費代理。
假設找到/買了N個代理,那麼要如何管理這些代理呢?我的想法是做一個類似於內存池的IP池。這樣做的好處是便於管理以及易於擴展。當只有一個代理時,其用法如下所示:
DefaultHttpClient httpclient = newDefaultHttpClient();//此代理不保證你看到的時候還存活
HttpHost proxy = new HttpHost("u120-227.static.grapesc.cz", 8080);
httpclient.getParams().setParameter(ConnRoutePNames.DEFAULT_PROXY,proxy);//如果代理要認證,則加上以下語句//httpclient.getCredentialsProvider().setCredentials(new AuthScope("proxy adress", proxy port),//new UsernamePasswordCredentials("username", "password"));//記得將網址拆成以下形式
HttpHost targetHost = new HttpHost("www.cnblogs.com"); //網站名前面不要加http://
HttpGet httpget = new HttpGet("/FengYan/");
HttpResponse response= httpclient.execute(targetHost, httpget);
補充下,如果是ADSL撥號,那麼無需擔心被封IP,因爲一般來說,當你重新撥號時,你會得到一個不一樣的IP。
4. 降低訪問頻率
5. 總結
原文網址:http://www.verydemo.com/cj.jsp?c=33&u=dang-pa-chong-bei-ju-jue-shi-accessdenied