• 伪装豆瓣爬虫程序——如何解决豆瓣禁用爬虫程序?


    最近因为业务需要,要将豆瓣上的图书以及影视信息抓取下来,网页抓取其实很简单,很快就完成,但是系统还没上线就发现了问题,豆瓣会根据请求分析客户的行为,判断是否是机器人,如果判断为机器人,将要求输入验证码,最终导致抓取不到数据。

    要解决这个问题,主要就两个思路,一个是识别验证码,当要求输入验证码时,识别图片中的验证码;另外一方案是伪装成正常访问,绕过豆瓣的分析程序。前一种方案受限于验证码识别率太低,很难满足需求,最终只能考虑第二种方案了。

    通过分析豆瓣的分析判断机器人的实际情况,发现豆瓣是根据ip 以及cookie信息统计访问频率来确定是否为“机器人”,有以下几种实际情况,1.不带cookie信息访问,快速访问一段时间,ip会被禁掉;2.带 cookie访问,快速访问一段时间,请求会被禁掉,这时候清掉cookie,可以恢复正常访问。基于这个测试结果,可以采用一个简单的方案:

           1.第一次请求没有cookie信息,但记录下返回的cookie.

           2.后面每个请求都带上这个cookie信息.

           3.如果请求被跳转到验证码页面,就不带cookie重试,并记录返回的cookie信息

           4.下一次请求带上新的cookie信息。

    重复2到4步骤。

           代码:

           1.简单的cookie manager:

    Java代码  收藏代码
    1. import java.io.Serializable;  
    2. import java.util.Map;  
    3. import java.util.concurrent.ConcurrentHashMap;  
    4. /** 
    5.  * 简单的Cookie Manager,按照顶级域名管理Cookie信息 
    6.  * @author <a href="mailto:jingyu@taohua.com">惊羽</a> 
    7.  * 
    8.  */  
    9. class CookieManager implements Serializable{  
    10.     private static final long serialVersionUID = 292218695837624307L;  
    11.     private static CookieManager cookieManager = new CookieManager();  
    12.     private Map<String,Map<String,String>> cookies = new ConcurrentHashMap<String, Map<String,String>>();  
    13.       
    14.     private CookieManager(){}  
    15.     /** 
    16.      * 根据域名获取对应的Cookie 
    17.      * @param domain 
    18.      * @return 
    19.      */  
    20.     public String getCookies(String domain){  
    21.         Map<String, String> domainCookies = cookies.get(getTopLevelDomain(domain));  
    22.         if(domainCookies != null){  
    23.             StringBuilder sb = new StringBuilder();  
    24.             boolean isFirst = true;  
    25.             for(Map.Entry<String, String> cookieEntry : domainCookies.entrySet()){  
    26.                 if(!isFirst){  
    27.                     sb.append("; ");  
    28.                 }else{  
    29.                     isFirst = false;  
    30.                 }  
    31.                 sb.append(cookieEntry.getKey())  
    32.                   .append("=")  
    33.                   .append(cookieEntry.getValue());  
    34.             }  
    35.             return sb.toString();  
    36.         }  
    37.         return "";  
    38.     }  
    39.       
    40.     /** 
    41.      * 
    42.      * 设置Cookie值 
    43.      * @param domain 
    44.      * @param cookiesString 
    45.      */  
    46.     public void setCookies(String domain,String cookiesString){  
    47.         Map<String, String> domainCookies = cookies.get(getTopLevelDomain(domain));  
    48.         if(domainCookies == null){  
    49.             domainCookies = new ConcurrentHashMap<String, String>();  
    50.             cookies.put(getTopLevelDomain(domain), domainCookies);  
    51.         }  
    52.         String[] cookies = cookiesString.split("; ");  
    53.         for (String cookie : cookies) {  
    54.             if(cookie != null && !cookie.trim().isEmpty()  
    55.                     && cookie.indexOf("=") > 0){  
    56.                 int equalMarkIndex = cookie.indexOf("=");  
    57.                 String key = cookie.substring(0,equalMarkIndex);  
    58.                 String value = cookie.substring(equalMarkIndex+1);  
    59.                 domainCookies.put(key, value);  
    60.             }  
    61.         }  
    62.     }  
    63.     /** 
    64.      * 删除域名下所有的Cookie 
    65.      * @param domain 
    66.      */  
    67.     public void removeCookies(String domain){  
    68.         cookies.remove(getTopLevelDomain(domain));  
    69.     }  
    70.       
    71.     /** 
    72.      * 获取CookieManager的实例 
    73.      * @return 
    74.      */  
    75.     public static CookieManager getInstance(){  
    76.         return cookieManager;  
    77.     }  
    78.       
    79.     /** 
    80.      * 获取域名的顶级域名 
    81.      * @param domain 
    82.      * @return 
    83.      */  
    84.     public String getTopLevelDomain(String domain){  
    85.         if(domain == null){  
    86.             return null;  
    87.         }  
    88.         if(!domainToTopLevelDomainMap.containsKey(domain)){  
    89.             String[] splits = domain.split("\.");  
    90.             domainToTopLevelDomainMap.put(domain, (splits[splits.length-2] + "." + splits[splits.length -1]));  
    91.         }  
    92.         return domainToTopLevelDomainMap.get(domain);  
    93.     }  
    94.     /** 
    95.      * 存储域名与其顶级域名之间映射关系,避免重复的计算顶级域名 
    96.      */  
    97.     private Map<String,String> domainToTopLevelDomainMap = new ConcurrentHashMap<String, String>();  
    98. }  

     

           2. 包装HttpURLConnection,实现请求失败自动清除cookie并重试的功能。

    Java代码  收藏代码
      1. public class HttpURLConnectionWrapper extends HttpURLConnection {  
      2.       
      3.     HttpURLConnection httpURLConnection;  
      4.     //简单的CookieManager  
      5.     CookieManager cookieManager = CookieManager.getInstance();  
      6.       
      7.     public HttpURLConnectionWrapper(URL u) throws IOException {  
      8.         super(u);  
      9.         httpURLConnection = (HttpURLConnection) u.openConnection();  
      10.         setFollowRedirects(false);  
      11.         fillRequestHeadField();  
      12.     }  
      13.     /** 
      14.      * 填充Request Header信息 
      15.      */  
      16.     private void fillRequestHeadField(){  
      17.         httpURLConnection.setRequestProperty("User-Agent""Mozilla/5.0 (Windows NT 5.1; rv:5.0) Gecko/20100101 Firefox/5.0");  
      18.         httpURLConnection.setRequestProperty("Accept""text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8");  
      19.         httpURLConnection.setRequestProperty("Accept-Language""zh-cn,zh;q=0.5");  
      20.         httpURLConnection.setRequestProperty("Accept-Encoding""GB2312,utf-8;q=0.7,*;q=0.7");  
      21.         httpURLConnection.setRequestProperty("Referer""http://movie.douban.com/");  
      22.         httpURLConnection.setRequestProperty("Cache-Control""max-age=0");  
      23.         httpURLConnection.setRequestProperty("Cookie", cookieManager.getCookies(url.getHost()));  
      24.           
      25.     }  
      26.   
      27.     @Override  
      28.     public InputStream getInputStream() throws IOException {  
      29.         InputStream is = httpURLConnection.getInputStream();  
      30.         //取到输入流中后处理Cookie信息  
      31.         resolveCookies();  
      32.         int responseCode = getResponseCode();  
      33.         if(responseCode != 200 && responseCode != 404 ){  
      34.             //清除cookie并重新发请求  
      35.             CookieManager.getInstance().removeCookies(url.getHost());  
      36.             try{  
      37.                 httpURLConnection.disconnect();  
      38.                 is.close();  
      39.             }catch (Exception e) {  
      40.             }  
      41.             httpURLConnection = (HttpURLConnection) this.getURL().openConnection();  
      42.             setFollowRedirects(false);  
      43.             fillRequestHeadField();  
      44.             is = httpURLConnection.getInputStream();  
      45.         }  
      46.         return is;  
      47.     }  
      48.     private void resolveCookies(){  
      49.         List<String> setCookies = getHeaderFields().get("Set-Cookie");  
      50.         if(setCookies != null && !setCookies.isEmpty()){  
      51.             for (String setCookie : setCookies) {  
      52.                 cookieManager.setCookies(this.url.getHost(), setCookie);  
      53.             }     
      54.         }  
      55.     }  
      56. .........  

  • 相关阅读:
    利用Clojure统计代码文件数量和代码行数
    Workflow:添加工作流存储功能
    MongoDB:最简单的增删改查(Oops,可能太简单了)
    《WF in 24 Hours》读书笔记
    推荐一个学习python的网站
    Inter系列处理器名称浅析
    [Android1.5]TextView跑马灯效果
    Code::Blocks 的配色方案
    PuTTY + Xming 远程使用 Linux GUI
    Linux下查看文件和文件夹大小
  • 原文地址:https://www.cnblogs.com/xuxiaoshuan/p/3628935.html
Copyright © 2020-2023  润新知