네이버 여행 페이지 크롤링 예시입니다.
1. build.gradle 에 의존성 추가
2. manifests에 인터넷 권한 추가
3. 네이버 여행 페이지 태그 분석
아래 사진에서 왼쪽 인기 도시 리스트를 크롤링 할 예정입니다.
1. F12를 눌러 크롬 개발자 도구를 켜주세요.
2. Ctrl + Shift + c를 누른 후 크롤링 하고자 하는 곳을 클릭하세요.
( 저는 아래 "미국 인기도시14" 부분을 클릭하였습니다.)
3. ul 태그 안에 li 태그들이 많이 있는 것을 볼 수 있습니다.
4. 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&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. 실행 화면
조금이나마 도움이 되었으면 합니다 !