【 轉 】http://www.cnblogs.com/FengYan/archive/2012/07/31/2614335.htmlhtml
因爲要準備測試數據,不得不大量爬取某個網站的內容。爲了防止被封,特地將爬蟲設計爲單線程同步的爬蟲。結果在爬了大約3萬個頁面的時候,對方發回Access Denied。等一段時間後再啓動爬蟲,結果仍是Access Denied。這時才明白這樣的想法太天真了,當初就應該找其它方法來避免纔對。而本文則記述了這些其它方法。apache
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)cookie
HttpGet getMethod = new HttpGet("URL"); getMethod.setHeader("User-Agent", "user agent內容");
雖然有些網站不登錄就能訪問,可是它一檢測到某IP的訪問量有異常,就會立刻提出登錄要求。若是是不帶驗證碼的,那麼果斷登錄吧。不過,在登錄以前要作些準備——查清楚POST登錄請求時要附帶哪些參數。個人作法是先用badboy錄製登錄過程,而後將這一過程導出爲jmeter文件,最後用jmeter查看登錄所需的參數。查完後,就能夠登錄,具體以下所示ide
DefaultHttpClient httpclient = new DefaultHttpClient(); HttpPost postMethod = new HttpPost("http://passport.cnblogs.com/login.aspx"); //注意用post //登錄博客園所須要的參數 List nvps = new ArrayList(); 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(new UrlEncodedFormEntity(nvps, HTTP.UTF_8)); HttpResponse response = httpclient.execute(postMethod);
因爲httpClient會自動管理cookie,因此接下來直接get或者post就好了。post
若是對方用某段時間內某IP的訪問次數來斷定爬蟲,而後將這些爬蟲的IP都封掉的話,以上假裝就失效了。對方的這個思路隱含着一個假設:爬蟲的訪問量必然比正經常使用戶的大不少,於是只要使這個假設不成立就能夠了。這時就該代理上場了。所謂代理就是介於用戶與網站之間的第三者:用戶先將請求發到代理,而後代理再發到服務器,這樣看起來就像是代理在訪問那個網站了。這時,服務器會將此次訪問算到代理頭上。同時用多個代理的話,單個IP的訪問量就降下去了,因而就有可能逃過一劫。不過,這個方法最大的問題就是找到穩定的代理(有錢買代理的,能夠無視這句話)。我目前是在無憂代理找,但找到的大部分都不能用,少部分能用的也不穩定。求分享好用的免費代理。測試
假設找到/買了N個代理,那麼要如何管理這些代理呢?個人想法是作一個相似於內存池的IP池。這樣作的好處是便於管理以及易於擴展。當只有一個代理時,其用法以下所示網站
DefaultHttpClient httpclient = new DefaultHttpClient(); //此代理不保證你看到的時候還存活 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。spa
若是說找不到又免費又穩定的代理呢?那隻好用最後一招了——下降訪問頻率。這樣作能夠達到與用代理同樣的效果——防止被對方從訪問量上看出來。固然,在抓取效率上會差不少。此外,下降訪問頻率只是一個指導思想,在這個思想下,能夠獲得不少具體作法,例如:每抓取一個頁面就休息隨機秒(我的感受比固定時間的要好);限制天天抓取的頁面數量。
防止被封的「理」是假裝成正經常使用戶,只要按着這個「理」走,總能找到具體的「法」。雖然說如此,對於一個接觸爬蟲不久的小白,例如在下,是很難知道正經常使用戶訪問一個網站時的全部細節的。所以,知道些反爬蟲的「法」,而後有針對地進行拆招仍是十分必要的。描述反爬的「法」的文章,推薦「互聯網網站的反爬蟲策略淺析」。