JAVA Web Crawling (Scraping) / 웹페이지 크롤링 Apache HttpComponents

2016. 11. 21. 15:20language/java

 

JAVA Crawling (Scraping) / 웹페이지 크롤링 Apache HttpComponents

 

Java에서 웹페이지를 String 타입(문자열) 크롤링 하는 방법을 소개한다.

 

문자열로 크롤링 후 jericho, jsoup 등 Java에서 사용하기 편하도록 DOM 객체로 파싱해주는 라이브러리를 사용하면 좀 더 효율적으로 크롤링된 문자열을 사용할 수 있다.

 

하지만 여기에서는 단순히 문자열로만 크롤링하는 방법을 소개하겠다.

 

 

 

 

크롤링은 원하는 페이지 1개만 크롤링 하는 경우도 있지만

 

세션을 유지한 채 사이트 내부의 링크를 돌아다니며 필요한 페이지만 크롤링할수도 있다.

 

예를들면 다음과 같은 경우다.

 

로그인 > 특정 메뉴 > 서브메뉴 > 목록 화면 (크롤링)

 

이번 포스팅은 세션을 유지한 채 페이지들을 이동하며 크롤링하는 방법이다.

 

 



 

일단 최소한의 라이브러리가 필요하다.

 

최소한의 라이브러리 목록은 다음과 같다.

 

commons-codec-1.9.jar
commons-logging-1.2.jar
httpclient-4.5.2.jar
httpcore-4.4.4.jar

 

 

아래 주소로 이동한다.

http://hc.apache.org/downloads.cgi

 

 

 

위에 표시된 곳을 클릭해서 라이브러리를 다운받는다.

 

다운받은 파일을 압축 풀면

httpcomponents-client-4.5.2-bin\httpcomponents-client-4.5.2\lib 경로 아래에 라이브러리 파일이 있다.

 

commons-codec-1.9.jar
commons-logging-1.2.jar
httpclient-4.5.2.jar
httpcore-4.4.4.jar

 

그 중에서 위 4개 파일을 프로젝트에 추가한다. (최소한의 라이브러리 파일)

 

그리고 다음과 같은 클래스를 하나 만든다.

그대로 복사해서 이름만 마추면 된다.

 

HttpClientMain.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
 
import org.apache.http.NameValuePair;
import org.apache.http.client.ResponseHandler;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.utils.URLEncodedUtils;
import org.apache.http.impl.client.BasicResponseHandler;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.message.BasicNameValuePair;
 
public class HttpClientMain {
 
    private static CloseableHttpClient client;
 
    /**
     * @param args
     */
    public static void main(String[] args) {
        client = HttpClientBuilder.create().build();
 
        String url = "http://cofs.tistory.com";
        Map<StringString> param = new HashMap<StringString>();
        param.put("userId""id");
        param.put("password""pw");
 
        HttpClientMain jm = new HttpClientMain();
        String string = jm.get(url, param);
        System.out.println(string);
 
        String url2 = "http://cofs.tistory.com/movePage";
        Map<StringString> param2 = new HashMap<StringString>();
        param2.put("no""1");
        String string2 = jm.get(url2, param2);
        System.out.println(string2);
 
        try {
            client.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
 
    /**
     * POST
     * 
     * @param url       요청할 url
     * @param params    파라미터 Map
     * @param encoding  파라미터 Encoding Type
     * @return 응답본문 문자열
     */
    public String post(String url, Map<StringString> params, String encoding) {
 
        try {
            HttpPost post = new HttpPost(url);
            System.out.println("================================");
            System.out.println("POST : " + post.getURI());
            System.out.println("================================");
 
            List<NameValuePair> paramList = convertParam(params);
            post.setEntity(new UrlEncodedFormEntity(paramList, encoding));
 
            ResponseHandler<String> rh = new BasicResponseHandler();
 
            String execute = client.execute(post, rh);
 
            return execute;
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            //            client.close();
        }
 
        return "error";
    }
 
    public String post(String url, Map<StringString> params) {
        return post(url, params, "UTF-8");
    }
 
    /**
     * GET
     * 
     * @param url       요청할 url
     * @param params    파라미터 Map
     * @param encoding  파라미터 Encoding Type
     * @return 응답본문 문자열
     */
    public String get(String url, Map<StringString> params, String encoding) {
 
        try {
            List<NameValuePair> paramList = convertParam(params);
            HttpGet get = new HttpGet(url + "?" + URLEncodedUtils.format(paramList, encoding));
            System.out.println("================================");
            System.out.println("GET : " + get.getURI());
            System.out.println("================================");
            ResponseHandler<String> rh = new BasicResponseHandler();
 
            String execute = client.execute(get, rh);
 
            return execute;
 
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            //            client.close();
        }
 
        return "error";
    }
 
    public String get(String url, Map<StringString> params) {
        return get(url, params, "UTF-8");
    }
 
    private List<NameValuePair> convertParam(Map<StringString> params) {
        List<NameValuePair> paramList = new ArrayList<NameValuePair>();
        Iterator<String> keys = params.keySet().iterator();
        while (keys.hasNext()) {
            String key = keys.next();
            paramList.add(new BasicNameValuePair(key, params.get(key).toString()));
        }
 
        return paramList;
    }
 
}
 
cs

 

위 소스에서 가장 중요한 부분만 설명하겠다.

 

21# : 전역변수에 client를 선언함

27# : 메인이 실행될 때 client를 생성

45# : client close (종료)

 

client가 전역변수로 선언되어 있으므로

client를 close 하기 전까지 같은 세션으로 페이지들을 이동할 수 있다.

 



 

만약 단일 페이지만 크롤링한다면 다음과 같이 수정하면 된다.

21#, 27# 에 선언된 client 전역변수와 client를 생성하는 부분을

59# post 함수 내부, 96# get 함수 내부로 이동하고

45# 에 있는 client close를 제거하고 78#, 113# 의 client.close() 의 주석을 해제하면 된다.

 

 

 

이전 소스들을 참고하다 보니 Deprecate 되어있는 함수들이 있었다.

Deprecate 정보

 

new DefaultHttpClient() is deprecated

HttpClientBuilder.create().build() 로 대체함 (HttpClientMain.java 27# 참고)

 

getConnectionManager().shutdown() is deprecated

client.close() 로 대체함 (HttpClientMain.java 45# 참고)