카테고리 없음

[안드로이드 스튜디오] 네이버 여행 페이지 크롤링하기 / Kotlin

프로틴형님 2023. 1. 7. 21:22

네이버 여행 페이지 크롤링 예시입니다.

1. build.gradle 에 의존성 추가

build.gradle(:app)


2. manifests에 인터넷 권한 추가

AndroidManifest.xml


3. 네이버 여행 페이지 태그 분석

아래 사진에서 왼쪽 인기 도시 리스트를 크롤링 할 예정입니다.

크롤링 할 페이지

 

1. F12를 눌러 크롬 개발자 도구를 켜주세요.

 

2. Ctrl + Shift + c를 누른 후 크롤링 하고자 하는 곳을 클릭하세요.

( 저는 아래 "미국 인기도시14" 부분을 클릭하였습니다.)

 

3. ul 태그 안에 li 태그들이 많이 있는 것을 볼 수 있습니다. 

태그 구조 분석

4. li 태그에서 필요한 정보 찾기

li 태그 안에 필요한 정보 찾기

- 도시 이름 

<b class="topPlace_name__1arme"><b class="topPlace_index__2f-fX">A</b>뉴욕</b>

 

- 이미지

<img src="https://search.pstatic.net/common?src=https%3A%2F%2Fdbscthumb-phinf.pstatic.net%2F5885_000_11%2F20201127102731495_B5TZU4H7Q.jpg%2Ffb286_3_i1.jpg%3Ftype%3Dw540_fst&amp;type=f180_180_travelsearch" alt="" class="topPlace_img__20iO1">

 

- 도시 설명

<span class="topPlace_desc__rmZ30">전 세계에서 가장 트렌디한 도시. 여행자들의 로망과 기대를 120% 충족시켜 주는 여행의 천국</span>

 

제가 필요한 정보들입니다. 이제 안드로이드 스튜디오로 넘어가서 코드를 짜보겠습니다.


4. 크롤링 코드 작성

1. url 지정

var tag = "JP294232"
if (country == "프랑스"){
    tag = "FR187070"
} else if (country == "일본"){
    tag = "JP294232"
} else if (country == "미국"){
    tag = "US191"
}
val list = doTask("https://travel.naver.com/overseas/" + tag + "/country/topPlace/tmap")

 

여러 국가를 검색해보며 url 규칙을 찾아서 적용하기.

 

2. 크롤링 코드 작성

private fun doTask(url: String) : ArrayList<checkboxData> {
    var itemList : ArrayList<checkboxData> = arrayListOf()
    CoroutineScope(Dispatchers.IO).launch {
        // Jsoup을 활용해서 url 웹사이트 html 코드 불러오기
        val document = Jsoup.connect(url).get()
        // mapView_list__108hK란 클래스명을 가진 ul 태그 안의 li 태그들을 전부 가져오기
        val elements : Elements = document.select("ul.mapView_list__108hK li")
        // li 태그 마다 실행
        elements.forEach { element ->
            // 이미지
            var img = element.select("figure.topPlace_thumb__3Q3Hc img").attr("src")
            // 도시 이름
            var spot = element.select("b.topPlace_name__1arme").text()
            // 도시 설명
            var detail = element.select("span[class=topPlace_desc__rmZ30]").text()

            // 데이터 클래스에 넣어서 ArrayList에 추가
            var item = checkboxData(img, spot, detail)
            itemList.add(item)

            // 스레드를 사용하여 리사이클러뷰를 갱신 
            val test: TimerTask = object : TimerTask() {
                override fun run() {
                    runOnUiThread {
                        recomListAdapter.notifyDataSetChanged()
                    }
                }
            }
            test.run()                
        }
    }

- Coroutine 사용해서 비동기 처리

 

- Jsoup 라이브러리를 사용해서 html 코드 받아오기

 

- html 코드에서 필요한 데이터 선별

val elements : Elements = document.select("ul.mapView_list__108hK li")

"mapView_list__108hK"란 클래스명을 가진 ul 태그 안의 li 태그를 전부 가져온다.

var img = element.select("figure.topPlace_thumb__3Q3Hc img").attr("src")

li 태그에 안에 / "topPlace_thumb__3Q3Hc"란 클래스명을 가진 figure 태그 안에 / img 태그를 찾아서 src 속성 값을 가져온다.

var spot = element.select("b.topPlace_name__1arme").text()

"topPlace_name__1arme"란 클래스명을 가진 b 태그의 text를 가져온다.

var detail = element.select("span[class=topPlace_desc__rmZ30]").text()

"topPlace_desc__rmZ30"란 클래스명을 가진 span 태그의 text를 가져온다.

 

- 데이터 클래스에 위의 데이터들을 담아 ArrayList에 넣는다.

data class checkboxData(
    val img : String,
    val title : String,
    val comment : String,
)

 

- 스레드를 사용하여 리사이클러뷰를 갱신한다

val test: TimerTask = object : TimerTask() {
    override fun run() {
        runOnUiThread {
            recomListAdapter.notifyDataSetChanged()
        }
    }
}
test.run()

데이터 갱신 Thread로 안해주면 데이터는 전송되지만 UI는 갱신 안 되니 꼭 적어주어야 한다 !


4.  리사이클러뷰에 연결

일단 기본적으로 리사이클러뷰를 완성했다는 가정 하에 설명하겠다.

 

1. 리사이클러뷰 어댑터에 크롤링한 데이터를 매개변수로 전달

// 리사이클러뷰
recomListAdapter= RecomListAdapter(list)
binding.rvRecomList.adapter=recomListAdapter
recomListAdapter.notifyDataSetChanged()

 

2. 어댑터에서 리사이클러뷰 레이아웃 뷰들과 연결

inner class ViewHolder(val binding: ItemRecomCardBinding) : RecyclerView.ViewHolder(binding.root) {
    fun bind(item: checkboxData) {
        binding.imageView.clipToOutline=true
        // 도시 이름
        binding.spot.text = item.title
        // 도시 설명
        binding.comment.text = item.comment

        // 이미지 url 읽어서 넣기
        Glide.with(itemView).load(item.img).into(binding.imageView)
    }
}

 

리사이클러뷰 어댑터 전체 코드

class RecomListAdapter (private val data: ArrayList<checkboxData>) : RecyclerView.Adapter<RecomListAdapter.ViewHolder>() {


    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
        val binding = ItemRecomCardBinding.inflate(LayoutInflater.from(parent.context),parent,false)
        return ViewHolder(binding)
    }

    override fun getItemCount(): Int = data.size

    override fun onBindViewHolder(holder: ViewHolder, position: Int) {
        holder.bind(data[position])
    }

    inner class ViewHolder(val binding: ItemRecomCardBinding) : RecyclerView.ViewHolder(binding.root) {
        fun bind(item: checkboxData) {
            binding.imageView.clipToOutline=true
            // 도시 이름
            binding.spot.text = item.title
            // 도시 설명
            binding.comment.text = item.comment

            // 이미지 url 읽어서 넣기
            Glide.with(itemView).load(item.img).into(binding.imageView)
        }
    }


}

5. 실행 화면

실행 화면

 

조금이나마 도움이 되었으면 합니다 !