관리 메뉴

웹개발자의 기지개

사이트 내용 추출 실습 2 - 크롤링하기 (로또 당첨번호, 네이버/다음 실시간 검색어 조회) , RecylerView 활용 본문

안드로이드

사이트 내용 추출 실습 2 - 크롤링하기 (로또 당첨번호, 네이버/다음 실시간 검색어 조회) , RecylerView 활용

http://portfolio.wonpaper.net 2019. 7. 28. 23:49

앞전의 포스팅중에 [ 사이트 내용 추출 실습 1 - 크롤링하기 https://wonpaper.tistory.com/111

 

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

Java 카테고리상의 예제에서 특정 사이트의 태그를 읽고 원하는 부분만 추출해서 크롤링하는 간단한 예제를 만들어 봤다. ( Jsoup 으로 웹페이지 소스 파싱 (최근 로또 당첨번호 확인) ) https://wonpaper.tistory..

wonpaper.tistory.com

그 다음 버전이다. 

일단 실행 화면부터 살펴보자.

 

최초화면 썰렁하다 ~~
RecycleView 적용
해당 item클릭시 해당 관련 검색어로 이동하기

위에서 네이버랑 다음은 거의 소스상 프로그램 패턴은 동일하다. 

 

일단 jsoup  이라는 웹사이트의 태그를 추출해오는 라이브러리 함수부터 추가하도록 하자

자세한 방법은 [ jar 라이브러리 추가하기 ] https://wonpaper.tistory.com/109

 

jar 라이브러리 추가하기

먼저 jar파일을 로컬에 복사해두고, 아래의 그림처럼 해당 Project 상의 app > libs 에 복사해 둔다. 다음 아래의 사진을 보듯이 해당 jar 파일 마우스 우클릭하여 Add As Library.. 를 클릭하여 실제 App 에 빌..

wonpaper.tistory.com

그다음 전체적인 소스 관련해서 대략 나타내면 다음과 같다. 

앞전 포스팅에서  [ 사이트 내용 추출 실습 1 - 크롤링하기 ] https://wonpaper.tistory.com/111

 

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

Java 카테고리상의 예제에서 특정 사이트의 태그를 읽고 원하는 부분만 추출해서 크롤링하는 간단한 예제를 만들어 봤다. ( Jsoup 으로 웹페이지 소스 파싱 (최근 로또 당첨번호 확인) ) https://wonpaper.tistory..

wonpaper.tistory.com

앞전 포스팅을 그대로 이용하는 소스들과 함께 어우러 진다.

 

등장하는 관련 소스들 정리

 

(기존 소스)

activity_main.xml - 초기화면 썰렁한(?) 버튼3개만 있는 화면페이지

LottoNum.java - 로또당첨 번호 조회

NaverSearch.java - 네이버 실시간 검색어 기본 클래스

NaverSearchManager.java - 네이버 실시간 검색어 얻어오는 매니저 클래스

DaumSearch.java - 다음 실시간 검색어 기본 클래스

DaumSearchManager.java - - 다음 실시간 검색어 얻어오는 매니저 클래스

 

(추가된 소스)

NaverActivity.java - 네이버 실시간 검색어 WebView 화면 activity 

activicy_naver.xml - 네이버 실시간 검색어 WebView 화면 xml

 

NaverRecyclerAdapter.java - 네이버쪽 RecyclerView 이용을 위한 Adapter 소스

NaverRecyclerViewActivity.java - 네이버쪽 RecyclerView 목록 화면을 위한 activicy

activity_naver_recycler_view.xml - 네이버쪽 RecyclerView 목록 화면을 위한 xml

 

item.xml - RecycleView에 한줄한줄 각 item에 대한 디자인 xml

 

DaumActivity.java - 다음 실시간 검색어 WebView 화면 activity

activicy_daum.xml - 다음 실시간 검색어 WebView 화면 xml

 

DaumRecyclerAdapter.java - 다음쪽 RecyclerView 이용을 위한 Adapter 소스

DaumRecyclerViewActivity.java - 다음쪽 RecyclerView 목록 화면을 위한 activicy

activity_daum_recycler_view.xml - 다음쪽 RecyclerView 목록 화면을 위한 xml

 

 

[ MainActivity.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
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
package com.chat.wonpa.mujimakjicrawling;
 
import android.content.Intent;
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" :
 
                        ArrayList<NaverSearch> array1 = (ArrayList<NaverSearch>) bun.getSerializable(key);
 
                        Intent intent = new Intent(getApplicationContext(),NaverRecyclerViewActivity.class);
                        intent.putExtra("NaverSearch",array1);
                        startActivity(intent);
                        break;
 
                    case "daumSearch" :
 
                        ArrayList<DaumSearch> array2 = (ArrayList<DaumSearch>) bun.getSerializable(key);
 
                        Intent intent2 = new Intent(getApplicationContext(),DaumRecyclerViewActivity.class);
                        intent2.putExtra("DaumSearch",array2);
                        startActivity(intent2);
                        break;
 
                }
            }
 
        }
    };
}
 
cs

이전 포스팅의 MainActivity.java와 흐름은 같이 간다. 

다만 139 라인과 148라인의 네이버 / 다음코너의 내용이 달라진 부분이다. 

 

3가지 버튼 (로또, 네이버, 다음) 마다 클릭 이벤트로 인해 각각의 쓰레드 방식으로 개별 처리되는데, 이 쓰레드는 handler 라는 놈을 가지고 실제 작업이 이루어지는데, 이 handler 놈으로 처리할때 넘어온 메세지를  Bundle 객체형태로 그 데이터를 받아서 Intent 처리하도록 구성하였다.  그리고, 이 Intent 를 가지고 각각 NaverRecyclerViewActivity.java 를 띠우는 방식으로 진행이 된다.

 

라인 111 에서  각 쓰레드를 처리할때 받아온 메세지 값을

Bundle bun = msg.getData();  메세지내용을 받으면 Bundle 객체가 리턴된다.

 

라인 141 에서 

ArrayList<NaverSearch> array1 = (ArrayList<NaverSearch>) bun.getSerializable(key);  처럼

NaverSearch 객체들이 모인 ArrayList를 get해서 받아온다.

 

다만 주의할점은 

라인73   bun.putSerializable("naverSearch",arrayList3); 처럼 put 으로 naverSearch객체를 넘기고,

라인141 ArrayList<NaverSearch> array1 = (ArrayList<NaverSearch>) bun.getSerializable(key); 처럼 get 으로 그 객체를 받기 위해서는

NaverSearch  기본클래스를 Serializable 로 상속해 주어야한다는 점을 잊지 말자.

DaumSearch 또한 마찬가지로 하면 된다.

 

그다음 내용은 RecyclerView 와 이 Adapter, 그리고, 각 List 항목을 클릭했을때, WebView를 만들어 여기에 실제 그 검색어의 웹사이트를 띠우는 작업을 해보자.

 

일단 [ item.xml ] 이라고 RecyclerView의 item 디자인 구성화면은 다음과 같이 심플하게 얼른 만들었다.

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
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="horizontal">
 
    <LinearLayout
        android:orientation="vertical"
        android:layout_width="0dp"
        android:layout_weight="1"
        android:layout_height="wrap_content">
 
        <TextView
            android:id="@+id/textView1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:padding="5dp" />
 
        <TextView
            android:id="@+id/textView2"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:padding="5dp"
            android:visibility="gone" />
    </LinearLayout>
</LinearLayout>
cs

 

[ NaverRecyclerViewActivity.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
import java.util.ArrayList;
 
public class NaverRecyclerViewActivity extends AppCompatActivity {
    private NaverRecyclerAdapter adapter;
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_naver_recycler_view);
        setTitle("네이버 실시간 검색어 순위 조회");
 
        RecyclerView recyclerView1 = (RecyclerView) findViewById(R.id.naver_recyclerView1);
 
        // recyclerView1의  layoutManger 형식을 지정한다. Grid형식도 설정가능하다.
        LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this);
        recyclerView1.setLayoutManager(linearLayoutManager);
 
        adapter = new NaverRecyclerAdapter();
        recyclerView1.setAdapter(adapter);
 
 
        getData();
    }
 
    public void getData() {
        Intent intent = getIntent();
        ArrayList<NaverSearch> array1 = (ArrayList<NaverSearch>) intent.getSerializableExtra("NaverSearch");
        for (int i=0;i<array1.size();i++) {
 
            // adapter에 방금 만든 Data 객체를 추가해 넣는다.
           adapter.addItem(array1.get(i));
        }
 
        // adapter 내용의 값이 변경되었음을 알려준다. 이 함수를 쓰지않으면 data가 노출안된다.
        adapter.notifyDataSetChanged();
    }
}
cs

RecyclerView 는 Adaper 에서 관련 item들을 담고 이를 setAapter 하여 작동시킨다. 

 

[  activity_naver_recycler_view.xml ]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?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=".NaverRecyclerViewActivity">
 
    <android.support.v7.widget.RecyclerView
        android:id="@+id/naver_recyclerView1"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
 
</LinearLayout>
cs

 

[ NaverRecyclerAdapter.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
import android.content.Intent;
import android.support.annotation.NonNull;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
 
import java.util.ArrayList;
 
public class NaverRecyclerAdapter extends RecyclerView.Adapter<NaverRecyclerAdapter.ItemViewHolder> {
 
    ArrayList<NaverSearch> arrayList = new ArrayList<NaverSearch>();
    int num = 1;
 
    @NonNull
    @Override
    public ItemViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        // item.xml 을 parent ViewGroup 위에 Inflate 시켜 새로운 ViewHolder를 하나 만든다.
        View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item,parent,false);
        return new ItemViewHolder(view);
    }
 
    @Override
    public void onBindViewHolder(@NonNull ItemViewHolder itemViewHolder, int position) {
        itemViewHolder.onBind(arrayList.get(position));
    }
 
    @Override
    public int getItemCount() {
        return arrayList.size();
    }
 
    public void addItem(NaverSearch naverSearch) {
        arrayList.add(naverSearch);
    }
 
 
    class ItemViewHolder extends RecyclerView.ViewHolder {
        private TextView textView1;
        private TextView textView2;
 
        public ItemViewHolder(@NonNull final View itemView) {
            super(itemView);
            textView1 = (TextView) itemView.findViewById(R.id.textView1);
            textView2 = (TextView) itemView.findViewById(R.id.textView2);
 
            // 해당 각각의 item 클릭시 Naver WebView를 이용하여 각 아이템의 url로 새창띠운다.
            itemView.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
 
                    Intent intent = new Intent(itemView.getContext(),NaverActivity.class);
                    intent.putExtra("NaverSearchName",textView1.getText().toString());
                    intent.putExtra("NaverSearchUrl",textView2.getText().toString());
 
                    // 해당 View v 에서 getContext() 를 읽어와서 startActivity 띄운다.
                    v.getContext().startActivity(intent);
                }
            });
 
        }
 
        void onBind(NaverSearch naver) {
            textView1.setText(num + "위 : " + naver.getName());
            textView2.setText(naver.getUrl());
            num++;
        }
    }
}
cs

RecyclerAdapter 는 또 다시 ViewHolder 놈으로 각각의 아이템내용들인 item.xml 의 세부 view들을 1:1 바인딩하는 처리를 한다. 세부내용들을 쏙쏙 데이터를 넣어주고 채워줘서 개별 View 들을 담은 그릇이라 보면 되겠다. 

 

여기 까지 구성하면 Naver 나 Daum 의 경우 RecyclerView 형태로 리스트 결과화면으로 뽑을수 있는데, 이 각 한줄 아이템을 클릭했을때, 처리되는 라인이 위 소스상의 49번 줄부분이다.

 

그리고, 검색어와 관련된 url 값 대로 WebView를 띄워 해당 검색어와 관련된 웹사이트로 새로운 창을 띄우는 작업을 해보도록 하자.

 

[ NaverActivity.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
import android.content.Intent;
import android.net.Uri;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.KeyEvent;
import android.webkit.WebChromeClient;
import android.webkit.WebResourceRequest;
import android.webkit.WebView;
import android.webkit.WebViewClient;
 
public class NaverActivity extends AppCompatActivity {
    WebView webView1;
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_naver);
 
        Intent intent = getIntent();
        String name = intent.getStringExtra("NaverSearchName");
        String url = intent.getStringExtra("NaverSearchUrl");
 
        setTitle("네이버 실시간 검색어 " + name);
 
        webView1 = (WebView) findViewById(R.id.webView1);
        webView1.getSettings().setJavaScriptEnabled(true);
        webView1.setWebChromeClient(new WebChromeClient());
        webView1.loadUrl(url);
 
        // 새창 관련 관리클래스
        webView1.setWebViewClient(new WebViewClientClass());
    }
 
    // 웹뷰에서 뒤로가기 버튼을 누르기
    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        if ((keyCode == KeyEvent.KEYCODE_BACK) && webView1.canGoBack()) {
            webView1.goBack();
            finish();
            return true;
        }
        return super.onKeyDown(keyCode, event);
    }
 
    private class WebViewClientClass extends WebViewClient {
        @Override
        public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
 
            Log.d("Naver check url : ", request.getUrl().toString());
            Uri uri = request.getUrl();
            if (uri.toString() != null) {
                return false;           // 현재 창에서 브라우저로 바로 본다.
 
            } else {
 
                // 새창으로 띄우는데, 암시적 인텐트로 여러개의 브라우저가 설치되어있을 경우
                // 이중에서 선택가능하다.
                Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(uri.toString()));
                startActivity(intent);
                return true;            // 새로운 창으로 띄운다.
            }
//            return super.shouldOverrideUrlLoading(view, request);
 
        }
    }
}
cs

[ activity_naver.xml ]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?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=".NaverActivity">
 
    <WebView
        android:id="@+id/webView1"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
</LinearLayout>
cs

 

자아~ 그리고 Daum 실시간 검색어 관련 소스들도 Naver 쪽의 소스와 쌍동이처럼 일치한다.

이름만 Daum 으로 시작해서 동일하게 RecyclerView 와 Adapter 등을 만들어 주면된다.

Comments