본문 바로가기

안드로이드

[안드로이드] #3 크롤링+리사이클러뷰 (주식, 코인 평단가 계산기 + 크롤링 + 바텀네비게이션뷰)

안녕하세요

 

이번 시간에는 크롤링을 하고 리사이클러뷰에 담아보는 것을 해보겠습니다.

 

이전 블로그 내용과 이어지는 내용입니다.

혹시 이전 블로그를 아직 보지 못하신 분들은 아래 링크를 클릭해 먼저 봐주세요

https://adc6981.tistory.com/9

 

[안드로이드] #2 주식, 코인 평단가계산기 (주식, 코인 평단가 계산기 + 크롤링 + 바텀네비게이션

안녕하세요 이전 블로그 내용과 이어지는 내용입니다. 혹시 이전 블로그를 아직 보지 못하신 분들은 아래 링크를 클릭해 먼저 봐주세요 https://adc6981.tistory.com/8 [안드로이드] #1 바텀네비게이션뷰

adc6981.tistory.com

 

우선 화면 구성부터 해보겠습니다.

frag2.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layout_margin="3dp"
    android:orientation="vertical">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginBottom="5dp"
        android:orientation="horizontal">

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_marginRight="1dp"
            android:layout_weight="1"
            android:background="@drawable/layout_background"
            android:orientation="vertical">

            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_margin="2dp"
                android:orientation="horizontal">

                <TextView
                    android:id="@+id/textView2"
                    android:layout_width="match_parent"
                    android:layout_height="match_parent"
                    android:layout_weight="1.5"
                    android:text="코스피" />

                <TextView
                    android:id="@+id/kospi"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:layout_weight="1"
                    android:text="TextView" />
            </LinearLayout>

            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:layout_margin="2dp"
                android:orientation="horizontal">
                <LinearLayout
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:layout_weight="1.5">
                </LinearLayout>
                <LinearLayout
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:layout_weight="1">
                    <TextView
                        android:id="@+id/kospi_updown1"
                        android:layout_width="match_parent"
                        android:layout_height="wrap_content"
                        android:layout_weight="1.5"
                        android:text="123" />

                    <TextView
                        android:id="@+id/kospi_updown2"
                        android:layout_width="match_parent"
                        android:layout_height="wrap_content"
                        android:layout_weight="1"
                        android:text="456" />
                </LinearLayout>
            </LinearLayout>
        </LinearLayout>

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_marginLeft="1dp"
            android:layout_weight="1"
            android:background="@drawable/layout_background"
            android:orientation="vertical">

            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_margin="2dp"
                android:orientation="horizontal">


                <TextView
                    android:id="@+id/textView3"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:layout_weight="1.5"
                    android:text="코스닥" />

                <TextView
                    android:id="@+id/kosdaq"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:layout_weight="1"
                    android:text="TextView" />
            </LinearLayout>

            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:layout_margin="2dp"
                android:orientation="horizontal">
                <LinearLayout
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:layout_weight="1.5">
                </LinearLayout>
                <LinearLayout
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:layout_weight="1">
                    <TextView
                        android:id="@+id/kosdaq_updown1"
                        android:layout_width="match_parent"
                        android:layout_height="wrap_content"
                        android:layout_weight="1.5"
                        android:text="123" />

                    <TextView
                        android:id="@+id/kosdaq_updown2"
                        android:layout_width="match_parent"
                        android:layout_height="wrap_content"
                        android:layout_weight="1"
                        android:text="456" />
                </LinearLayout>
            </LinearLayout>
        </LinearLayout>
    </LinearLayout>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">


        <TextView
            android:id="@+id/textView"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginTop="3dp"
            android:layout_marginBottom="3dp"
            android:gravity="center"
            android:text="금융 주요 뉴스"
            android:textSize="18sp" />

        <androidx.recyclerview.widget.RecyclerView
            android:id="@+id/rv"
            android:layout_width="match_parent"
            android:layout_height="wrap_content">


        </androidx.recyclerview.widget.RecyclerView>
    </LinearLayout>
</LinearLayout>

 

 

android:background="@drawable/layout_background"

이 부분에서 오류가 나올 겁니다. 

따로 뒷 배경을 디자인해서 적용시켜야 합니다.

res→drawable에 layout_background를 만들어 줍니다.

<?xml version="1.0" encoding="utf-8"?>
<shape
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle">
    <solid android:color="#EEEEEE" />
    <stroke android:color="#000000" android:width="1dp" />
    <corners android:radius="7dp" />
</shape>

이러면 코스피와 코스닥 지수를 크롤링한 부분의 뒷배경이 변경될 것입니다.

 

 

이후 리사이클러뷰에서 보일 화면을 구성해보겠습니다.

item_list.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical">
        <TextView
            android:id="@+id/text_news"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:textSize="25sp"
            android:text="test">
        </TextView>
    </LinearLayout>
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="3dp"
        android:layout_marginBottom="3dp">

        <View
            android:layout_width="match_parent"
            android:layout_height="1dp"
            android:background="#000000" />
            
    </LinearLayout>
</LinearLayout>

 

화면 구성을 끝났습니다.

이후 자바 코드를 작성해 봅시다.

크롤링은 네이버금융에서 주요뉴스와 한국거래소에서 코스피와 코스닥 지수를 가져올 것입니다.

https://finance.naver.com/

 

네이버 금융

국내 해외 증시 지수, 시장지표, 뉴스, 증권사 리서치 등 제공

finance.naver.com

http://www.krx.co.kr/main/main.jsp

 

한국거래소

 

www.krx.co.kr

 

네이버금융에서 크롤링한 데이터를 받아올 객체를 만들어 줍니다.

뉴스 제목과 url 정보를 가져올 것입니다.

Data.java

package com.psw.stocktest;

public class Data {
    private String str_news;
    private String news_link;

    public Data() {

    }

    public String getNews_link() {
        return news_link;
    }

    public void setNews_link(String news_link) {
        this.news_link = news_link;
    }

    public String getStr_news() {
        return str_news;
    }

    public void setStr_news(String str_news) {
        this.str_news = str_news;
    }
}

 

네이버금융에서 가져온 데이터 들은 리사이클러뷰를 이용해 보여줄 것입니다.

리사이클러뷰 사용 방법입니다.

MainAdapter.java

public class MainAdapter extends RecyclerView.Adapter<MainAdapter.ViewHolder> {

    private ArrayList<Data> arrayList;
    private Context context;

    public MainAdapter(Context context, ArrayList<Data> arrayList) {
        this.arrayList = arrayList;
        this.context = context;
        notifyDataSetChanged();
    }

    @NonNull
    @Override
    public MainAdapter.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_list,parent,false);
        ViewHolder holder = new ViewHolder(view);

        return holder;
    }

    @Override
    public void onBindViewHolder(@NonNull MainAdapter.ViewHolder holder, @SuppressLint("RecyclerView") int position) {
        holder.text_news.setText(arrayList.get(position).getStr_news());
        holder.itemView.setTag(position);
        holder.itemView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                String str = arrayList.get(position).getNews_link();
                Intent intent =new Intent(Intent.ACTION_VIEW, Uri.parse(str));
                context.startActivity(intent);
            }
        });

    }

    @Override
    public int getItemCount() {
        return arrayList.size();
    }

    public class ViewHolder extends RecyclerView.ViewHolder {
        protected TextView text_news;
        public ViewHolder(@NonNull View itemView) {
            super(itemView);
            this.text_news = itemView.findViewById(R.id.text_news);
        }
    }
}

 

어댑터를 만들었다면 이것을 frag2에서 실행해 줄 것입니다.

frag2.java에서 리사이클러뷰에 필요한 객체들을 선언해 줍니다.

private ArrayList<Data> arrayList;
private MainAdapter mainAdapter;
private RecyclerView rv;
private LinearLayoutManager linearLayoutManager;

 

이후 리사이클러뷰(rv)에 mainAdapter를 담아주면 리사이클러뷰가 정상 작동할 것입니다.

rv= view.findViewById(R.id.rv);
linearLayoutManager = new LinearLayoutManager(getContext(),LinearLayoutManager.VERTICAL,true);
linearLayoutManager.setStackFromEnd(true);
rv.setLayoutManager(linearLayoutManager);

arrayList = new ArrayList<>();
mainAdapter = new MainAdapter(getActivity(),arrayList);
rv.setAdapter(mainAdapter);

 

 

이제 본격적으로 크롤링을 해봅시다.

안드로이드 매니패스트에 인터넷 권한을 설정해줍니다.

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>

 

 

 

 

먼저 한국거래소에서 코스피와 코스닥 지수를 가져오겠습니다.

크롤링은 무조건 스레드에서 실행되어야 합니다. 그리고 Handler를 이용해 화면을 업데이트할 수 있습니다.

 

 

간단하게 하면 3줄로 크롤링을 할 수 있습니다.

아래 내용 다음과 같습니다.

한국거래소에서 index-price이라는 것을 가져오는 것입니다.

document = Jsoup.connect("http://www.krx.co.kr/main/main.jsp").get();
Elements contents_price = document1.getElementsByClass("index-price");
price = contents_price.text();

 코스피의 가격을 가져오는 방법입니다.

log를 찍으면 다음과 같습니다.

8,390.67 2,450.93 319.49 797.02 1,139.00 1,452.40

KTOP30지수, KOSPI지수 등 모든 가격이 나오게 됩니다. KTOP30, KOSPI등 모두의 class이름이 index-price이기 때문입니다.

 

여기서 제가 필요한 데이터는 KOSPI지수, KOSDAQ지수입니다.

자세히 보면 데이터들이 빈칸을 기준으로 나눠져 있습니다.

String[] sp_price = price.split(" ");

그래서 price를 빈칸으로 나눈 후 제가 원하는 데이터만 가져다 사용하겠습니다.

sp_price[1], sp_price[3]이 코스피와 코스닥 지수입니다.

 

같은 방법으로 코스피 코스닥 지수를 가져와 보겠습니다.

 

document1 = Jsoup.connect("http://www.krx.co.kr/main/main.jsp").get();
Elements contents_price = document1.getElementsByClass("index-price");
price =contents_price.text();
try{
                        Elements contents_up_down = document1.getElementsByClass("index-up");
                        up_down = contents_up_down.text();
                        String[] sp_up_down = up_down.split(" ");
                        if(sp_up_down[3].equals("▲")){

                        }
                    }catch (ArrayIndexOutOfBoundsException e){
                        Elements contents_up_down = document1.getElementsByClass("index-down");
                        up_down = contents_up_down.text();
                    }

 

원래는 up_down값을 아래와 같이 가져왔으나 오류가 있어서 변경합니다.

Elements contents_up_down = document1.getElementsByClass("index-up");
up_down = contents_up_down.text();

 

코스피가 상승 할때면 class이름이 index-up으로 표현되고 하락할 때면 index-down으로 표시됩니다.

그래서 하락할때 index-up을 해서 값을 가져오면 String[] sp_up_down에서 인덱스 오류가 나올 겁니다. 

이것을 방지하기 위해 try catch 문을 추가했습니다.

 

	String[] sp_price = price.split(" ");
               String[] sp_up_down = up_down.split(" ");
                    if(sp_up_down[3].equals("▲")){
                        kospi.setTextColor(Color.parseColor("#FF0000"));
                        kospi_updown1.setTextColor(Color.parseColor("#FF0000"));
                        kospi_updown2.setTextColor(Color.parseColor("#FF0000"));
                    }else if(sp_up_down[3].equals("▼")) {
                        kospi.setTextColor(Color.parseColor("##0027FF"));
                        kospi_updown1.setTextColor(Color.parseColor("##0027FF"));
                        kospi_updown2.setTextColor(Color.parseColor("##0027FF"));
                    }else{

                    }
                    if(sp_up_down[9].equals("▲")){
                        kosdaq.setTextColor(Color.parseColor("#FF0000"));
                        kosdaq_updown1.setTextColor(Color.parseColor("#FF0000"));
                        kosdaq_updown2.setTextColor(Color.parseColor("#FF0000"));
                    }else if(sp_up_down[9].equals("▼")) {
                        kosdaq.setTextColor(Color.parseColor("##0027FF"));
                        kosdaq_updown1.setTextColor(Color.parseColor("##0027FF"));
                        kosdaq_updown2.setTextColor(Color.parseColor("##0027FF"));
                    }else{

                    }

 

 

 

지수가 상승일 경우 텍스트 색을 빨간색으로 변경했고 하락일 경우 파란색으로 변경했습니다.

 

bundle.putString("kospi",sp_price[1]);
bundle.putString("kosdaq",sp_price[3]);
bundle.putString("kospi_u_d1",sp_up_down[4]);
bundle.putString("kospi_u_d2",sp_up_down[5]);
bundle.putString("kosdaq_u_d1",sp_up_down[10]);
bundle.putString("kosdaq_u_d2",sp_up_down[11]);

 

bundel을 이용해 데이터를 보내줍니다.

 

Handler handler = new Handler(){
        @Override
        public void handleMessage(@NonNull Message msg) {
            if(dialog!=null){
                dialog.dismiss();
            }
            Bundle bundle = msg.getData();
            kospi.setText(bundle.getString("kospi"));
            kosdaq.setText(bundle.getString("kosdaq"));
            kospi_updown1.setText(bundle.getString("kospi_u_d1"));
            kospi_updown2.setText(bundle.getString("kospi_u_d2"));
            kosdaq_updown1.setText(bundle.getString("kosdaq_u_d1"));
            kosdaq_updown2.setText(bundle.getString("kosdaq_u_d2"));

            mainAdapter.notifyDataSetChanged();

        }
    };

이후 Handler에서 bundle을 이용해 데이터를 받아줍니다.

 

여기까지가 코스피와 코스닥의 지수를 가져오는 방법입니다.

 

 

 

네이버금융 주요뉴스도 비슷한 방법으로 크롤링할 것입니다.

주요뉴스의 6개의 제목과 주소를 크롤링할 것입니다. 

첫 번째 뉴스 요소를 누르면 다음 위와 같은 사진처럼 나올 겁니다. 여기서 마우스 우클릭 → 복사 → JS경로복사를 하시면

다음과 같은 내용이 복사될 것입니다.

#content > div.article > div.section > div.news_area > div > ul > li:nth-child(1) > span > a"

다음 뉴스는 child(2)를 하면 가져올 수 있겠군요

 

document2 = Jsoup.connect("https://finance.naver.com").get();
                    String str1 = "#content > div.article > div.section > div.news_area > div > ul > li:nth-child(";
                    String str2 = ") > span > a";
                    for (int i = 1; i <7; i++) {
                        String str3 = String.valueOf(i);
                        String final_str = str1+str3+str2;
                        Elements elements = document2.select(final_str);
                        String abs = elements.attr("abs:href");
                        Data data = new Data();
                        data.setStr_news(elements.text());
                        data.setNews_link(abs);
                        arrayList.add(data);
                    }
                    Collections.reverse(arrayList);

크롤링한 데이터를 가져와서 Data 객체에 담아줍니다.

 

holder.itemView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                String str = arrayList.get(position).getNews_link();
                Intent intent =new Intent(Intent.ACTION_VIEW, Uri.parse(str));
                context.startActivity(intent);
            }
        });

리사이클러뷰를 클릭하면 그에 해당하는 position값의 getNews_link()를 실행하게 됩니다.

 

 

Frag2.java 전체 코드

public class Frag2 extends Fragment {
    private View view;
    private String price;
    private String up_down;

    private TextView kospi,kosdaq,kospi_updown1,kospi_updown2,kosdaq_updown1,kosdaq_updown2;

    private ArrayList<Data> arrayList;
    private MainAdapter mainAdapter;
    private RecyclerView rv;
    private LinearLayoutManager linearLayoutManager;

    
    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        view = inflater.inflate(R.layout.frag2,container,false);

        kospi = view.findViewById(R.id.kospi);
        kosdaq = view.findViewById(R.id.kosdaq);
        kospi_updown1 = view.findViewById(R.id.kospi_updown1);
        kospi_updown2 = view.findViewById(R.id.kospi_updown2);
        kosdaq_updown1 = view.findViewById(R.id.kosdaq_updown1);
        kosdaq_updown2 = view.findViewById(R.id.kosdaq_updown2);

        rv= view.findViewById(R.id.rv);
        linearLayoutManager = new LinearLayoutManager(getContext(),LinearLayoutManager.VERTICAL,true);
        linearLayoutManager.setStackFromEnd(true);
        rv.setLayoutManager(linearLayoutManager);
        arrayList = new ArrayList<>();
        mainAdapter = new MainAdapter(getActivity(),arrayList);
        rv.setAdapter(mainAdapter);


        final Bundle bundle = new Bundle();
        new Thread(){
            @Override
            public void run() {
                Document document1;
                Document document2;
                try {
                    //코스피,코스닥 지수
                    document1 = Jsoup.connect("http://www.krx.co.kr/main/main.jsp").get();
                    Elements contents_price = document1.getElementsByClass("index-price");
                    price =contents_price.text();
                    try{
                        Elements contents_up_down = document1.getElementsByClass("index-up");
                        up_down = contents_up_down.text();
                        String[] sp_up_down = up_down.split(" ");
                        if(sp_up_down[3].equals("▲")){
							
                        }
                    }catch (ArrayIndexOutOfBoundsException e){
                        Elements contents_up_down = document1.getElementsByClass("index-down");
                        up_down = contents_up_down.text();
                    }

					//뉴스 
                    document2 = Jsoup.connect("https://finance.naver.com").get();
                    String str1 = "#content > div.article > div.section > div.news_area > div > ul > li:nth-child(";
                    String str2 = ") > span > a";
                    for (int i = 1; i <7; i++) {
                        String str3 = String.valueOf(i);
                        String final_str = str1+str3+str2;
                        Elements elements = document2.select(final_str);
                        String abs = elements.attr("abs:href");
                        Data data = new Data();
                        data.setStr_news(elements.text());
                        data.setNews_link(abs);
                        arrayList.add(data);
                    }
                    Collections.reverse(arrayList);




                    String[] sp_price = price.split(" ");
                    String[] sp_up_down = up_down.split(" ");
                    if(sp_up_down[3].equals("▲")){
                        kospi.setTextColor(Color.parseColor("#FF0000"));
                        kospi_updown1.setTextColor(Color.parseColor("#FF0000"));
                        kospi_updown2.setTextColor(Color.parseColor("#FF0000"));
                    }else if(sp_up_down[3].equals("▼")) {
                        kospi.setTextColor(Color.parseColor("##0027FF"));
                        kospi_updown1.setTextColor(Color.parseColor("##0027FF"));
                        kospi_updown2.setTextColor(Color.parseColor("##0027FF"));
                    }else{

                    }
                    if(sp_up_down[9].equals("▲")){
                        kosdaq.setTextColor(Color.parseColor("#FF0000"));
                        kosdaq_updown1.setTextColor(Color.parseColor("#FF0000"));
                        kosdaq_updown2.setTextColor(Color.parseColor("#FF0000"));
                    }else if(sp_up_down[9].equals("▼")) {
                        kosdaq.setTextColor(Color.parseColor("##0027FF"));
                        kosdaq_updown1.setTextColor(Color.parseColor("##0027FF"));
                        kosdaq_updown2.setTextColor(Color.parseColor("##0027FF"));
                    }else{

                    }


                    bundle.putString("kospi",sp_price[1]);
                    bundle.putString("kosdaq",sp_price[3]);
                    bundle.putString("kospi_u_d1",sp_up_down[4]);
                    bundle.putString("kospi_u_d2",sp_up_down[5]);
                    bundle.putString("kosdaq_u_d1",sp_up_down[10]);
                    bundle.putString("kosdaq_u_d2",sp_up_down[11]);


                    Message msg = handler.obtainMessage();
                    msg.setData(bundle);
                    handler.sendMessage(msg);
                }catch (IOException e){
                    e.printStackTrace();
                }
            }
        }.start();
        return view;
    }
    Handler handler = new Handler(){
        @Override
        public void handleMessage(@NonNull Message msg) {
            Bundle bundle = msg.getData();
            kospi.setText(bundle.getString("kospi"));
            kosdaq.setText(bundle.getString("kosdaq"));
            kospi_updown1.setText(bundle.getString("kospi_u_d1"));
            kospi_updown2.setText(bundle.getString("kospi_u_d2")+"%");
            kosdaq_updown1.setText(bundle.getString("kosdaq_u_d1"));
            kosdaq_updown2.setText(bundle.getString("kosdaq_u_d2")+"%");

            mainAdapter.notifyDataSetChanged();

        }
    };

}

 

다음 시간에는 인트로 화면과 실행 중임을 알리는 프로그래스바를 만들어 보겠습니다.

수고하셨습니다!

반응형