관리 메뉴

웹개발자의 기지개

사이트 내용 추출 실습 1 - 크롤링하기 본문

안드로이드

사이트 내용 추출 실습 1 - 크롤링하기

http://portfolio.wonpaper.net 2019. 7. 5. 00:01

Java 카테고리상의 예제에서 특정 사이트의 태그를 읽고 원하는 부분만 추출해서 크롤링하는 간단한 예제를 만들어 봤다. 

( Jsoup 으로 웹페이지 소스 파싱 (최근 로또 당첨번호 확인) )

https://wonpaper.tistory.com/106?category=811920 

 

준비물 : Jsoup.jar 파일 (추출 라이브러리)

 

[결과화면]

팝업되어 각각의 버튼을 클릭하면 Alert 대화상자가 떠서 그내용을 자동으로 보여주는 형식으로 구현하였다.

 

[activity_main.xml]

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
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".MainActivity">
 
    <Button
        android:id="@+id/button1"
        android:layout_margin="20dp"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" android:text="최신 로또당첨 번호 확인"/>
 
    <Button
        android:id="@+id/button2"
        android:layout_margin="20dp"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" android:text="네이버 실시간 검색어 조회"/>
 
    <Button
        android:id="@+id/button3"
        android:layout_margin="20dp"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" android:text="네이버 실시간 검색어 조회"/>
 
</LinearLayout>
cs

 

[LottoNum.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
import android.util.Log;
 
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
 
import java.io.IOException;
import java.util.ArrayList;
 
public class LottoNum {
    ArrayList<String> arrayList;
 
    public LottoNum() {
        arrayList = new ArrayList<String>();
    }
 
    public ArrayList<String> getNumber() {
 
        try {
            Document doc = Jsoup.connect("https://dhlottery.co.kr/common.do?method=main").get();
            Elements contents;
            contents = doc.select("#lottoDrwNo");
 
            arrayList.add(contents.text()); // 로또당첨 횟수
 
            for (int i=1;i<=6;i++) {
                contents = doc.select("#drwtNo" + i);
                arrayList.add(contents.text()); // 당첨번호 1번 ~ 6번
            }
 
            contents = doc.select("#bnusNo");
            arrayList.add(contents.text());     // 보너스 번호
 
        } catch (IOException e) {
            //e.printStackTrace();
            Log.d("LottoNum의 getNumber()함수 에러 : ",e.getMessage());
        }
 
        return arrayList;
    }
}
cs

 

[NaverSearch.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
public class NaverSearch {
    String num;
    String name;
    String url;
 
    public NaverSearch() { }
 
    public NaverSearch(String num,String name,String url) {
        this.num = num;
        this.name = name;
        this.url = url;
    }
 
    public String getNum() {
        return num;
    }
 
    public void setNum(String num) {
        this.num = num;
    }
 
    public String getName() {
        return name;
    }
 
    public void setName(String name) {
        this.name = name;
    }
 
    public String getUrl() {
        return url;
    }
 
    public void setUrl(String url) {
        this.url = url;
    }
}
cs

 

[NaverSearchManager.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
import android.util.Log;
 
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
 
import java.io.IOException;
import java.io.Serializable;
import java.util.ArrayList;
 
public class NaverSearchManager implements Serializable {
    ArrayList<NaverSearch> arrayList;  // NaverSearch객체를 담는 ArrayList 선언
 
    public NaverSearchManager () {
        arrayList = new ArrayList<NaverSearch>();
    }
 
    public int getNaverSearchSize() {
        return arrayList.size();
    }
 
    public void setNaverSearch(NaverSearch naverSearch) {
        arrayList.add(naverSearch);
    }
 
    public ArrayList<NaverSearch> naverSearchProcess() {
        String num = "";
        String name = "";
        String url = "";
 
        try {
            Document doc = Jsoup.connect("https://www.naver.com").get();
            Elements contents;
            //contents = doc.select("ul.ah_l li.ah_item a span.ah_k");
            contents = doc.select("ul.ah_l li.ah_item a");  // 해당 태그 부분을 추출
 
            for (Element e : contents) {
                url = e.attr("href");  // href 
                num = e.select("span.ah_r").text();
                name = e.select("span.ah_k").text();
 
                NaverSearch naver = new NaverSearch(num,name,url);
                arrayList.add(naver);
            }
 
        } catch (IOException e) {
            e.printStackTrace();
            Log.v("NaverSearchManager",e.getMessage());
        }
        return arrayList;
    }
}
cs

36라인에 doc.select("ul[data-list='1to10'] li.ah_item a[data-clk='lve.keyword']"); 으로 좀더 상세하게 태그를 잡을 수 있도록 해도 좋다.

 

[DaumSearch.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
public class DaumSearch {
    String num;
    String name;
    String url;
 
    public DaumSearch() {
 
    }
 
    public DaumSearch(String num,String name,String url) {
        this.num = num;
        this.name = name;
        this.url = url;
    }
 
    public String getNum() {
        return num;
    }
 
    public void setNum(String num) {
        this.num = num;
    }
 
    public String getName() {
        return name;
    }
 
    public void setName(String name) {
        this.name = name;
    }
 
    public String getUrl() {
        return url;
    }
 
    public void setUrl(String url) {
        this.url = url;
    }
}
cs

 

[DaumSearchManager.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
import android.util.Log;
 
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
 
import java.io.IOException;
import java.io.Serializable;
import java.util.ArrayList;
 
public class DaumSearchManager implements Serializable {
    ArrayList<DaumSearch> arrayList;
 
    public DaumSearchManager() {
        arrayList = new ArrayList<DaumSearch>();
    }
 
    public void setDaumSearch(DaumSearch daumSearch) {
        arrayList.add(daumSearch);
    }
 
    public int getDaumSearchSize() {
        return arrayList.size();
    }
 
    public ArrayList<DaumSearch> DaumSearchProcess() {
        String num = "";
        String name= "";
        String url = "";
 
        try {
            Document doc = Jsoup.connect("https://www.daum.net").get();
            Elements contents ;
            contents = doc.select("ol.list_hotissue li span.txt_issue a");
            int i=1;
            for (Element e : contents) {
                url  = e.attr("href");
                name = e.text();
                num  = Integer.toString(i);
                i++;
 
                DaumSearch daum = new DaumSearch(num,name,url);
                arrayList.add(daum);
            }
 
 
        } catch (IOException e) {
            e.printStackTrace();
            Log.v("DaumSearchManager",e.getMessage());
        }
 
        return arrayList;
    }
}
cs

 

[MainActivity.java] - 재료들을 모아 실제로 돌려본다.

아래 소스중에 lotto.getNumber() 를 쓰레드 처리하지 않고 그냥 직접 불러오면 Network 에러가 발생하고 오류처리가 된다.

 

Main쓰레드 ( MainActitity의 onCreat() ) 에서 직접 돌리면

안드로이드 android.os.NetworkOnMainThreadException 에러난다.

 

onCreate (메인메소드) 함수는 메인 쓰레드를 기준으로 처리됨으로 별도의 네트워 처리를 위해 각각 쓰레드로 일을 만들어 처리해야한다.

 

그리고, 여기서 쓰레드를 각각 돌릴때, 실제 작업은 Handler 객체를 통해서 이루어지는데, Handler 객체처리를 하기전에 

데이터들을 Bundle 형태로 묶어서 Message 에 담아서, 결국 Handler 로 sendMessage() 함수로 보내게 된다.

 

즉, 이 Thread 의 실제 작업내용은 Handler 내에서 메시지를 왔다 갔다 하면서 처리한다.

 

이때, Bundle 객체는 하나의 객체를 계속 이용함으로 

switch 문으로 구문된 각각의 동작을 시작하기전에 bun.clear() 함으로써 번들내용들을 깨끗이 지우고 시작한다.

 

아래 소스의 하이라이트는 

bun.putStringArrayList("lottoNum",arrayList) 으로 넣고, bun.getStringArrayList("lottoNum") 으로 뽑는다.

 

아울러 객체 형태로 모아놓은 네이버와 다음 추출쪽은

bun.putSerializable("naverSearch",arrayList2) 으로 넣고, bun.getSerializable("naverSearch") 으로 뽑는다.

 

물론 NaverSearchManager implements Serializable 부분처럼 Serializable을 상속을 받아야 번들로 ArrayList<객체> 형태로 보낼수 있다.

 

 

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
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
import android.os.Handler;
import android.os.Message;
import android.support.v7.app.AlertDialog;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
 
import java.util.ArrayList;
import java.util.Set;
 
public class MainActivity extends AppCompatActivity {
    Button button1, button2, button3;
    ArrayList<String> arrayList;
    ArrayList<NaverSearch> arrayList2;
    ArrayList<DaumSearch> arrayList3;
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        setTitle("크롤링 실습하기");
 
        button1 = (Button) findViewById(R.id.button1);
        button2 = (Button) findViewById(R.id.button2);
        button3 = (Button) findViewById(R.id.button3);
 
        final Bundle bun = new Bundle();
 
        // 최신 로또 번호 확인
        button1.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
 
                // Thread 로 웹서버에 접속
                new Thread() {
                    @Override
                    public void run() {
                        //super.run();
                        bun.clear();
 
                        LottoNum lotto = new LottoNum();
                        arrayList = lotto.getNumber();
 
                        bun.putStringArrayList("lottoNum",arrayList);
                        Message msg = handler.obtainMessage();
                        msg.setData(bun);
                        handler.sendMessage(msg);
 
                    }
                }.start();
            }
        });
 
        // 네이버 실시간 검색어 조회
        button2.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
 
                new Thread() {
                    @Override
                    public void run() {
                        //super.run();
                        bun.clear();
 
                        NaverSearchManager naverSearchManager = new NaverSearchManager();
                        arrayList2 = naverSearchManager.naverSearchProcess();
 
                        bun.putSerializable("naverSearch",arrayList2);
 
                        Message msg = handler.obtainMessage();
                        msg.setData(bun);
                        handler.sendMessage(msg);
                    }
                }.start();
            }
        });
 
        // 다음 실시간 검색어 조회
        button3.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                new Thread() {
                    @Override
                    public void run() {
                        //super.run();
                        bun.clear();
 
                        DaumSearchManager daumSearchManager = new DaumSearchManager();
                        arrayList3 = daumSearchManager.DaumSearchProcess();
 
                        bun.putSerializable("daumSearch",arrayList3);
 
                        Message msg = handler.obtainMessage();
                        msg.setData(bun);
                        handler.sendMessage(msg);
                    }
                }.start();
            }
        });
    }
 
    Handler handler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            //super.handleMessage(msg);
            Bundle bun = msg.getData();
 
            //Set<String> keys = bun.keySet();
            //Log.d("Handler",keys.size() + "개");
 
            String str = "";
            for (String key : bun.keySet()) {
 
                switch (key) {
                    case "lottoNum" :
 
                        str = "";
                        ArrayList<String> arrayList = bun.getStringArrayList(key);
 
                        AlertDialog.Builder dlg = new AlertDialog.Builder(MainActivity.this);
                        dlg.setTitle("제" + arrayList.get(0+ "회 로또당첨번호");
                        dlg.setIcon(R.mipmap.ic_launcher);
 
                        for (int i=1;i<6;i++) {
                            str += i + "번 : "+ arrayList.get(i) +" \n";
                        }
                        str += "\n보너스번호 : " + arrayList.get(7);
 
                        dlg.setMessage(str);
                        dlg.setPositiveButton("확인"null);
                        dlg.show();
 
                        break;
                    case "naverSearch" :
 
                        str = "";
                        ArrayList<NaverSearch> array1 = (ArrayList<NaverSearch>) bun.getSerializable(key);
 
                        AlertDialog.Builder dlg2 = new AlertDialog.Builder(MainActivity.this);
                        dlg2.setTitle("네이버 실시간 검색어 조회");
                        dlg2.setIcon(R.drawable.ic_launcher_foreground);
 
                        for (int i=0;i<20;i++) {
                            str += array1.get(i).getNum() + "위 : " + array1.get(i).getName() + "\n";
 
                        }
 
                        dlg2.setMessage(str);
                        dlg2.setPositiveButton("확인",null);
                        dlg2.show();
                        break;
 
                    case "daumSearch" :
                        str = "";
                        ArrayList<DaumSearch> array2 = (ArrayList<DaumSearch>) bun.getSerializable(key);
 
                        AlertDialog.Builder dlg3 = new AlertDialog.Builder(MainActivity.this);
                        dlg3.setTitle("다음 실시간 검색어 조회");
                        dlg3.setIcon(R.drawable.ic_launcher_foreground);
 
                        int j=1;
                        for (int i=0;i<20;i++) {
                            if (i%2 == 1) {
                                str += j + "위 : " + array2.get(i).getName() + "\n";
                                j++;
                            }
                        }
 
                        dlg3.setMessage(str);
                        dlg3.setPositiveButton("확인",null);
                        dlg3.show();
                        break;
 
                }
            }
 
 
        }
    };
}
cs

 

 

이상이다.

 

안드로이드 상으로 데이터 송수신하고자 할때는 쓰레드 처리를 해야한다.

쓰레드를 돌릴대 Handler 객체를 같이 이용하는데 상기 소스의 경우처럼 Message 를 만들어 넣고 빼고 하는 방식으로 처리할 수 도 있고, 아래처럼 handler.post 함수로 처리할 수 도 있다.

final Handler handler = new Handler();

new Thread(new Runable() {
   @Override
   public void run() {
   	
      handler.post(new Runnable() {
          @Override
          public void run() {
              // 처리할 내용
          }
      });
    
   }
}).start();

 

쓰레드의 좀더 상세한 설명을 위하여 아래의 포스팅글을 활용하면 더 좋을듯하다.

recipes4dev.tistory.com/170

Comments