안녕하세요
이번 시간에는 크롤링을 하고 리사이클러뷰에 담아보는 것을 해보겠습니다.
이전 블로그 내용과 이어지는 내용입니다.
혹시 이전 블로그를 아직 보지 못하신 분들은 아래 링크를 클릭해 먼저 봐주세요
우선 화면 구성부터 해보겠습니다.
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>
화면 구성을 끝났습니다.
이후 자바 코드를 작성해 봅시다.
크롤링은 네이버금융에서 주요뉴스와 한국거래소에서 코스피와 코스닥 지수를 가져올 것입니다.
http://www.krx.co.kr/main/main.jsp
네이버금융에서 크롤링한 데이터를 받아올 객체를 만들어 줍니다.
뉴스 제목과 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();
}
};
}
다음 시간에는 인트로 화면과 실행 중임을 알리는 프로그래스바를 만들어 보겠습니다.
수고하셨습니다!
'안드로이드' 카테고리의 다른 글
안드로이드 DataBinding 사용 방법 (0) | 2022.09.16 |
---|---|
[안드로이드] #4 평단가계산기+크롤링 마무리 (주식, 코인 평단가 계산기 + 크롤링 + 바텀네비게이션뷰) (1) | 2022.09.01 |
[안드로이드] #2 주식, 코인 평단가계산기 (주식, 코인 평단가 계산기 + 크롤링 + 바텀네비게이션뷰) (0) | 2022.08.26 |
[안드로이드] #1 바텀네비게이션뷰 (주식, 코인 평단가 계산기 + 크롤링 + 바텀네비게이션뷰) (2) | 2022.08.24 |
안드로이드 생명주기 (LifeCycle) 정리 (JAVA) (0) | 2022.08.22 |