자바 파충류 서버 차단 솔 루 션

이것 은 자바 파충류 시리즈 박문 의 네 번 째 편 입 니 다.전편자바 파충류 데이터 비동기 로드 어떻게 해결이 두 가지 방법 을 시험 해 보 세 요!이 가운데,우 리 는 내 장 된 브 라 우 저 커 널 과 역방향 해석 법 두 가지 측면 에서 데이터 비동기 로드 문 제 를 간단하게 이야기 했다.이 글 에서 우 리 는 간단하게 파충류 에 대해 이야기 할 때 자원 사 이 트 는 사용자 의 방문 행위 에 따라 파충류 프로그램 과 그 에 대응 하 는 해결 방법 을 차단 합 니 다.
파충류 차단 프로그램 은 자원 사이트 의 보호 조치 로 가장 자주 사용 하 는 반 파충류 전략 은 사용자 의 방문 행 위 를 바탕 으로 해 야 한다.예 를 들 어 각 서버 가 일정 시간 X 회 만 접근 할 수 있 도록 제한 하고,이 횟수 를 초과 하면 파충류 프로그램 이 접근 한 것 으로 간주 하 며,사용자 접근 행 위 를 바탕 으로 파충류 프로그램 인지 아 닌 지 를 판단 하 는 것 도 방문 횟수 에 따 른 것 이 아니 라 매번 요청 한 User Agent 요청 헤더,매번 방문 한 간격 등에 따 른 것 이다.전반적 으로 여러 요인 에 의 해 결정 되 는데 그 중에서 방문 횟수 를 위주 로 한다.
반 파충 류 는 모든 자원 사이트 가 스스로 보호 하 는 조치 로 자원 이 파충 프로그램 에 의 해 점용 되 지 않도록 보호 하 는 데 목적 을 둔다.예 를 들 어 우리 가 앞에서 사용 한 두 판 망 은 사용자 의 방문 행위 에 따라 파충류 프로그램 을 차단 합 니 다.모든 IP 는 분당 방문 횟수 가 일정 횟수 에 이 르 면 뒤의 요청 은 403 오류 로 돌아 갑 니 다.이 페이지 에 접근 할 수 있 는 권한 이 없다 고 생각 합 니 다.그래서 우 리 는 오늘 다시 두 판 망 을 예 로 들 어 프로그램 으로 이 현상 을 모 의 했다.다음은 내 가 만 든 두 판 영 화 를 채집 하 는 프로그램 이다.

/**
 *       
 */
public class CrawlerMovie {

  public static void main(String[] args) {
    try {
      CrawlerMovie crawlerMovie = new CrawlerMovie();
      //       
      List<String> movies = crawlerMovie.movieList();
      //  10       
      ExecutorService exec = Executors.newFixedThreadPool(10);
      for (String url : movies) {
        //    
        exec.execute(new CrawlMovieThread(url));
      }
      //    
      exec.shutdown();
    } catch (Exception e) {
      e.printStackTrace();
    }
  }

  /**
   *         
   *        
   *
   * @return
   */
  public List<String> movieList() throws Exception {
    //   100     
    String url = "https://movie.douban.com/j/search_subjects?type=movie&tag=  &sort=recommend&page_limit=200&page_start=0";
    CloseableHttpClient client = HttpClients.createDefault();
    List<String> movies = new ArrayList<>(100);
    try {
      HttpGet httpGet = new HttpGet(url);
      CloseableHttpResponse response = client.execute(httpGet);
      System.out.println("        ,     :" + response.getStatusLine().getStatusCode());
      if (response.getStatusLine().getStatusCode() == 200) {
        HttpEntity entity = response.getEntity();
        String body = EntityUtils.toString(entity, "utf-8");
        //          json
        JSONObject jsonObject = JSON.parseObject(body);
        JSONArray data = jsonObject.getJSONArray("subjects");
        for (int i = 0; i < data.size(); i++) {
          JSONObject movie = data.getJSONObject(i);
          movies.add(movie.getString("url"));
        }
      }
      response.close();
    } catch (Exception e) {
      e.printStackTrace();
    } finally {
      client.close();
    }
    return movies;
  }
}
/**
 *         
 */
class CrawlMovieThread extends Thread {
  //      
  String url;

  public CrawlMovieThread(String url) {
    this.url = url;
  }
  public void run() {
    try {
      Connection connection = Jsoup.connect(url)
          .method(Connection.Method.GET)
          .timeout(50000);
      Connection.Response Response = connection.execute();
      System.out.println("      ,     :" + Response.statusCode());
    } catch (Exception e) {
      System.out.println("      ,     :" + e.getMessage());
    }
  }
}
이 프로그램의 논 리 는 비교적 간단 합 니 다.먼저 콩짜개 인기 영 화 를 수집 합 니 다.여 기 는 Ajax 를 직접 방문 하여 콩짜개 인기 영화 의 링크 를 얻 은 다음 에 영화 의 상세 한 페이지 링크 를 분석 하고 다 중 스 레 드 는 상세 한 페이지 링크 를 방문 합 니 다.다 중 스 레 드 상황 에서 만 콩짜개 의 방문 요구 에 도달 할 수 있 기 때 문 입 니 다.콩짜개 인기 영화 페이지 는 다음 과 같다.

위의 프로그램 을 여러 번 실행 하면 다음 그림 의 결 과 를 얻 을 수 있 습 니 다.

위의 그림 에서 알 수 있 듯 이 httpclient 가 돌아 온 상태 코드 는 403 입 니 다.이 페이지 에 접근 할 수 있 는 권한 이 없습니다.즉,두 판 망 은 우리 가 파충류 프로그램 이 라 고 생각 하고 우리 의 방문 요 구 를 거절 한 것 입 니 다.우 리 는 현재 우리 의 방문 구 조 를 분석 해 보 자.왜냐하면 우 리 는 두 판 망 을 직접 방문 하기 때문에 이때 의 방문 구 조 는 다음 과 같다.

우리 가 이 제한 을 돌파 하려 면 우 리 는 두 판 망 의 서버 를 직접 방문 할 수 없다.우 리 는 제3자 에 게 끌 어 들 여 다른 사람 이 우 리 를 대신 해서 방문 하도록 해 야 한다.우 리 는 매번 방문 할 때마다 다른 사람 을 찾 아야 한다.그러면 제한 을 받 지 않 는 다.이것 이 바로 IP 대리 이다.이때 의 방문 구 조 는 다음 그림 으로 바 뀌 었 다.

우리 가 사용 하 는 IP 대리,우 리 는 IP 대리 탱크 가 필요 합 니 다.다음은 IP 대리 탱크 에 대해 이야기 하 겠 습 니 다.
IP 에이전트 풀
프 록 시 서버 에 많은 업 체 들 이 이 부분 을 만 들 고 있 습 니 다.구체 적 인 것 은 말 하지 않 겠 습 니 다.자신의 바 이 두 IP 대 리 는 많은 것 을 찾 을 수 있 습 니 다.이런 IP 대리상 들 은 모두 유 료 와 무료 프 록 시 IP 를 제공 하고 유 료 프 록 시 IP 의 가용성 이 높 으 며 속도 가 빠 릅 니 다.온라인 환경 에서 프 록 시 를 사용 해 야 한다 면 유 료 프 록 시 IP 를 사용 하 는 것 을 권장 합 니 다.만약 에 스스로 연구 만 한다 면 우 리 는 이런 업 체 의 무료 공개 대리 IP 를 수집 할 수 있 습 니 다.이런 IP 의 성능 과 가용성 은 비교적 떨 어 지지 만 우리 가 사용 하 는 데 영향 을 주지 않 습 니 다.
우 리 는 데모 프로젝트 이기 때문에 우 리 는 스스로 IP 대리 지 를 만 들 었 다.우 리 는 어떻게 IP 대리 탱크 를 설계 해 야 합 니까?다음 그림 은 제 가 그린 간단 한 IP 에이전트 풀 구성 도 입 니 다.

위의 구조 도 에서 알 수 있 듯 이 하나의 IP 대리 시스템 은 4 개의 모듈 과 관련 되 는데 그것 이 바로 IP 수집 모듈,IP 저장 모듈,IP 검 측 모듈 과 API 인터페이스 모듈 이다.
IP 수집 모듈
각 대형 IP 대리 업 체 에서 대리 IP 를 수집 하 는 것 을 책임 지고 수집 하 는 사이트 가 많 을 수록 대리 IP 의 가용성 이 높다.
IP 저장 모듈
수집 한 프 록 시 IP 를 저장 합 니 다.주로 Redis 와 같은 고성능 데이터 베 이 스 를 사용 합 니 다.저장 에 있어 우 리 는 두 가지 데 이 터 를 저장 해 야 합 니 다.하 나 는 사용 가능 한 프 록 시 IP 를 검사 하 는 것 이 고 다른 하 나 는 아직 검 측 되 지 않 은 프 록 시 IP 를 수집 하 는 것 입 니 다.
IP 검출 모듈
수집 한 IP 가 사용 가능 한 지 확인 합 니 다.이렇게 하면 우리 가 제공 하 는 IP 의 가용성 을 높 일 수 있 습 니 다.우 리 는 먼저 사용 할 수 없 는 IP 를 걸 러 냅 니 다.
API 인터페이스 모듈
인터페이스 형식 으로 사용 가능 한 프 록 시 IP 를 대외 적 으로 제공 합 니 다.
위 는 바로 IP 프 록 시 풀 에 관 한 디자인 입 니 다.이런 것들 에 대해 우 리 는 간단하게 알 면 됩 니 다.지금 은 우리 가 IP 프 록 시 풀 서 비 스 를 작성 할 필요 가 없 기 때 문 입 니 다.GitHub 에 우수한 오픈 소스 프로젝트 가 많아 서 바퀴 를 반복 할 필요 가 없습니다.GitHub 에 8K star 가 있 는 오픈 소스 IP 에이전트 프로젝트 proxy 를 선 택 했 습 니 다.pool,우 리 는 그것 을 우리 IP 에이전트 로 사용 할 것 입 니 다.proxy 에 대하 여pool 접근:https://github.com/jhao104/proxy_pool
배포 proxypool
proxy_pool 은 python 언어 로 쓰 여 있 습 니 다.그러나 이것 도 괜 찮 습 니 다.지금 은 용기 화 배치 가 가능 하기 때문에 용기 화 배 치 를 사용 하면 일부 환경의 설 치 를 차단 할 수 있 습 니 다.미 러 만 실행 하면 서 비 스 를 실행 할 수 있 습 니 다.그 안의 구체 적 인 실현 을 알 필요 가 없 기 때문에 이 프로젝트 는 Python 의 자바 프로그래머 도 사용 할 수 있 습 니 다.proxy_pool 은 수집 한 IP 를 저장 하기 위해 Redis 를 사용 하기 때문에 proxy 를 시작 합 니 다.pool 전에 Redis 서 비 스 를 시작 해 야 합 니 다.다음은 proxypool docker 시작 단계.
거울 상 을 끌어내다docker pull jhao104/proxy_pool실행 미 러docker run --env db_type=REDIS --env db_host=127.0.0.1 --env db_port=6379 --env db_password=pwd_str -p 5010:5010 jhao104/proxy_pool미 러 를 실행 한 후,우 리 는 데 이 터 를 수집 하고 처리 하 는 데 시간 이 걸 리 기 때문에 잠시 기 다 렸 다.대기 후 http://{your 방문host}:5010/get_all/,다음 그림 에서 보 여 준 결 과 를 얻 으 면 proxypool 프로젝트 를 배 치 했 습 니 다.

IP 에이전트 사용
IP 에이전트 풀 을 구축 한 후에 우 리 는 대리 IP 를 사용 하여 콩 판 영 화 를 수집 할 수 있 습 니 다.우 리 는 IP 를 제외 하고 User Agent 요청 헤드 도 콩 판 망 으로 방문 이 파충류 프로그램 인지 아 닌 지 를 판단 하 는 요소 라 는 것 을 알 게 되 었 습 니 다.그래서 우 리 는 User Agent 요청 헤드 를 위조 할 수 있 습 니 다.우 리 는 매번 에 서로 다른 User Agent 요청 헤드 를 사용 합 니 다.
저 희 는 콩짜개 영화 채집 프로그램 에 IP 에이전트 와 랜 덤 User Agent 요청 헤드 를 도입 합 니 다.구체 적 인 코드 는 다음 과 같 습 니 다.

public class CrawlerMovieProxy {

  /**
   *    user agent   
   */
  static List<String> USER_AGENT = new ArrayList<String>(10) {
    {
      add("Mozilla/5.0 (Linux; Android 4.1.1; Nexus 7 Build/JRO03D) AppleWebKit/535.19 (KHTML, like Gecko) Chrome/18.0.1025.166 Safari/535.19");
      add("Mozilla/5.0 (Linux; U; Android 4.0.4; en-gb; GT-I9300 Build/IMM76D) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30");
      add("Mozilla/5.0 (Linux; U; Android 2.2; en-gb; GT-P1000 Build/FROYO) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1");
      add("Mozilla/5.0 (Windows NT 6.2; WOW64; rv:21.0) Gecko/20100101 Firefox/21.0");
      add("Mozilla/5.0 (Android; Mobile; rv:14.0) Gecko/14.0 Firefox/14.0");
      add("Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/27.0.1453.94 Safari/537.36");
      add("Mozilla/5.0 (Linux; Android 4.0.4; Galaxy Nexus Build/IMM76B) AppleWebKit/535.19 (KHTML, like Gecko) Chrome/18.0.1025.133 Mobile Safari/535.19");
      add("Mozilla/5.0 (iPad; CPU OS 5_0 like Mac OS X) AppleWebKit/534.46 (KHTML, like Gecko) Version/5.1 Mobile/9A334 Safari/7534.48.3");
      add("Mozilla/5.0 (iPod; U; CPU like Mac OS X; en) AppleWebKit/420.1 (KHTML, like Gecko) Version/3.0 Mobile/3A101a Safari/419.3");
    }
  };

  /**
   *      user agent
   *
   * @return
   */
  public String randomUserAgent() {
    Random random = new Random();
    int num = random.nextInt(USER_AGENT.size());
    return USER_AGENT.get(num);
  }

  /**
   *     ip 
   *
   * @param queue   
   * @throws IOException
   */
  public void proxyIpPool(LinkedBlockingQueue<String> queue) throws IOException {


    //            ip
    String proxyUrl = "http://192.168.99.100:5010/get_all/";

    CloseableHttpClient httpclient = HttpClients.createDefault();

    HttpGet httpGet = new HttpGet(proxyUrl);
    CloseableHttpResponse response = httpclient.execute(httpGet);
    if (response.getStatusLine().getStatusCode() == 200) {
      HttpEntity entity = response.getEntity();
      String body = EntityUtils.toString(entity, "utf-8");

      JSONArray jsonArray = JSON.parseArray(body);
      int size = Math.min(100, jsonArray.size());
      for (int i = 0; i < size; i++) {
        //          json
        JSONObject data = jsonArray.getJSONObject(i);
        String proxy = data.getString("proxy");
        queue.add(proxy);
      }
    }
    response.close();
    httpclient.close();
    return;
  }


  /**
   *         ip
   *
   * @return
   * @throws IOException
   */
  public String randomProxyIp() throws IOException {

    //            ip
    String proxyUrl = "http://192.168.99.100:5010/get/";

    String proxy = "";

    CloseableHttpClient httpclient = HttpClients.createDefault();

    HttpGet httpGet = new HttpGet(proxyUrl);
    CloseableHttpResponse response = httpclient.execute(httpGet);
    if (response.getStatusLine().getStatusCode() == 200) {
      HttpEntity entity = response.getEntity();
      String body = EntityUtils.toString(entity, "utf-8");
      //          json
      JSONObject data = JSON.parseObject(body);
      proxy = data.getString("proxy");
    }
    return proxy;
  }

  /**
   *         
   *
   * @return
   */
  public List<String> movieList(LinkedBlockingQueue<String> queue) {
    //   60     
    String url = "https://movie.douban.com/j/search_subjects?type=movie&tag=  &sort=recommend&page_limit=40&page_start=0";
    List<String> movies = new ArrayList<>(40);
    try {
      CloseableHttpClient client = HttpClients.createDefault();
      HttpGet httpGet = new HttpGet(url);
      //    ip   
      HttpHost proxy = null;
      //         IP
      String proxy_ip = randomProxyIp();
      if (StringUtils.isNotBlank(proxy_ip)) {
        String[] proxyList = proxy_ip.split(":");
        System.out.println(proxyList[0]);
        proxy = new HttpHost(proxyList[0], Integer.parseInt(proxyList[1]));
      }
      //          
      httpGet.setHeader("User-Agent", randomUserAgent());
      RequestConfig requestConfig = RequestConfig.custom()
          .setProxy(proxy)
          .setConnectTimeout(10000)
          .setSocketTimeout(10000)
          .setConnectionRequestTimeout(3000)
          .build();
      httpGet.setConfig(requestConfig);
      CloseableHttpResponse response = client.execute(httpGet);
      System.out.println("        ,     :" + response.getStatusLine().getStatusCode());
      if (response.getStatusLine().getStatusCode() == 200) {
        HttpEntity entity = response.getEntity();
        String body = EntityUtils.toString(entity, "utf-8");
        //          json
        JSONObject jsonObject = JSON.parseObject(body);
        JSONArray data = jsonObject.getJSONArray("subjects");
        for (int i = 0; i < data.size(); i++) {
          JSONObject movie = data.getJSONObject(i);
          movies.add(movie.getString("url"));
        }
      }
      response.close();
    } catch (Exception e) {
      e.printStackTrace();
    } finally {

    }
    return movies;
  }


  public static void main(String[] args) {
    //     ip   
    LinkedBlockingQueue<String> queue = new LinkedBlockingQueue(100);

    try {
      CrawlerMovieProxy crawlerProxy = new CrawlerMovieProxy();
      //    ip    
      crawlerProxy.proxyIpPool(queue);
      //         
      List<String> movies = crawlerProxy.movieList(queue);

      //          
      ExecutorService exec = Executors.newFixedThreadPool(5);
      for (String url : movies) {
        //    
        exec.execute(new CrawlMovieProxyThread(url, queue, crawlerProxy));
      }
      //    
      exec.shutdown();
    } catch (Exception e) {
      e.printStackTrace();
    }
  }

}

/**
 *         
 */
class CrawlMovieProxyThread extends Thread {
  //      
  String url;
  //   ip  
  LinkedBlockingQueue<String> queue;
  //    
  CrawlerMovieProxy crawlerProxy;

  public CrawlMovieProxyThread(String url, LinkedBlockingQueue<String> queue, CrawlerMovieProxy crawlerProxy) {
    this.url = url;
    this.queue = queue;
    this.crawlerProxy = crawlerProxy;
  }

  public void run() {
    String proxy;
    String[] proxys = new String[2];
    try {
      Connection connection = Jsoup.connect(url)
          .method(Connection.Method.GET)
          .timeout(50000);

      //     ip    ,     ip  
      if (queue.size() == 0) crawlerProxy.proxyIpPool(queue);
      //         ip
      proxy = queue.poll();
      //     ip
      proxys = proxy.split(":");
      //     ip
      connection.proxy(proxys[0], Integer.parseInt(proxys[1]));
      //    user agent
      connection.header("User-Agent", crawlerProxy.randomUserAgent());
      Connection.Response Response = connection.execute();
      System.out.println("      ,     :" + Response.statusCode() + " ,  ip:" + proxys[0]);
    } catch (Exception e) {
      System.out.println("      ,     :" + e.getMessage() + " ,  ip:" + proxys[0]);
    }
  }
}
수 정 된 채집 프로그램 을 실행 하려 면 여러 번 실행 해 야 할 수도 있 습 니 다.프 록 시 IP 가 매번 유효 하 지 는 않 기 때 문 입 니 다.프 록 시 IP 가 유효 하 다 면 다음 과 같은 결 과 를 얻 을 수 있 습 니 다.

그 결과 에 따 르 면 40 번 의 영화 상세 페이지 방문 은 대량의 대리 IP 가 무효 이 고 일부분 의 대리 IP 만 유효 하 다.그 결과 무료 프 록 시 IP 의 가용성 이 높 지 않다 는 것 을 직접 증 명 했 기 때문에 온라인 에서 프 록 시 IP 를 사용 해 야 한다 면 유 료 프 록 시 IP 를 사용 하 는 것 이 좋다.비록 우리 가 자체 적 으로 구축 한 IP 대리 탱크 의 가용성 이 그리 높 지 않 지만 우리 가 설정 한 IP 대리 가 콩 판 영 화 를 방문 하 는 데 성공 하여 IP 대 리 를 사용 하여 콩 판 망 의 제한 을 성공 적 으로 돌 렸 다.
파충류 서버 가 차단 되 는 이 유 는 여러 가지 가 있 습 니 다.이 글 은 주로 IP 프 록 시 설정 과 User Agent 요청 헤드 를 위조 하여 두 판 망 을 돌아 가 는 접근 제한 을 소개 합 니 다.어떻게 우리 의 프로그램 이 자원 사이트 에 의 해 파충류 프로그램 으로 간주 되 지 않도록 합 니까?다음 세 가 지 를 잘 해 야 한다.
사용자 에이전트 요청 헤더 위조IP 에이전트 사용
고정 되 지 않 은 채집 간격이 글 이 당신 에 게 도움 이 되 기 를 바 랍 니 다.다음 편 은 다 중 스 레 드 파충류 에 대한 탐색 입 니 다.파충류 에 관심 이 있다 면,한 파 를 주목 하고,서로 공부 하고,서로 발전 하 는 것 도 좋 습 니 다.
문장 에 부족 한 점 이 있 으 면,여러분 들 께 서 많이 지적 해 주시 고,함께 공부 하 며,함께 진보 해 주시 기 바 랍 니 다.
이상 이 바로 본 고의 모든 내용 입 니 다.여러분 의 학습 에 도움 이 되 고 저 희 를 많이 응원 해 주 셨 으 면 좋 겠 습 니 다.

좋은 웹페이지 즐겨찾기