1. 웹 페이지의 종류
2. 크롤링
2-1. 준비해야 할 것
2-2. html 요청 및 처리
3. 시도해본 방법들
3-1. JSON으로 받기
3-2. html 요청 및 처리
3-3. webView
나는 정적 페이지를 크롤링하고 싶다 -> '안드로이드 jsoup 파싱' 검색
나는 동적 페이지를 크롤링 할건데 JSON으로 결과를 받을 거다 -> '안드로이드 retrofit' 검색
나는 동적 페이지를 크롤링 할건데 HTML으로 결과를 받을 거다 -> 계속 읽으세요
나는 뭔소린지 1도 모르겠다 -> [목차 1]까지 읽어보세요
retrofit response html, android javascript parse, call.enqueue not working 등 이런 키워드로 검색해서 들어왔다
-> 저랑 같은 문제를 겪고 계실지도..? 계속 읽어보세요
1. 웹 페이지의 종류
우선 웹 페이지의 종류에 대해 알아야 한다. 종류에 따라 크롤링 방법을 달리 해야 하기 때문이다.
웹 페이지는 크게 2가지로 분류할 수 있다.
- 정적 페이지
- 동적 페이지
그리고 요청을 보내서 받게 되는 값도 2가지로 분류할 수 있다. (더 있긴 함)
- HTML
- JSON
이 요청을 보내는 대상(정적 페이지 or 동적 페이지)과 받게 되는 값(HTML or JSON)에 따라 방법을 다르게 써야 한다.
웹 알못인 나는 이것 때문에 삽질을 얼마나 했는지 모른다 후
모든 방법을 다 기록하기엔 글이 너무 길어질 것 같고 또 다른 블로그 글도 많아서
이 게시물에서는 내가 삽질한 동적 페이지 + html 케이스 경우만 다루려고 한다.
정적 페이지가 무엇인지에 대해서는 여기 게시물의 [목차 5-2]에 적어두었다.
돌려주는 값이 HTML인지 JSON인지는 Postman이라는 프로그램에 돌려보면 알 수 있다.
2. 크롤링
2-1. 준비해야 할 것
<uses-permission android:name="android.permission.INTERNET"/> // 인터넷 사용 권한
android:usesCleartextTraffic="true" // HTTP 접근 허용
Manifest 파일에 위 두 속성을 추가한다.
permission은 <Manifest> 태그 안에 넣으면 되고
usesCleartextTraffic은 <application> 태그 안에 넣으면 된다.
// coroutine
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.4.2'
// Retrofit
implementation 'com.squareup.retrofit2:retrofit:2.6.2'
implementation 'com.squareup.retrofit2:converter-gson:2.6.0'
비동기 실행을 위해 코루틴을, POST request를 위해 Retrofit을 gradle에 추가하자.
2-2. html 요청 및 처리
interface JsonPlaceHolderApi {
@FormUrlEncoded
@POST("여기는 맨 끝에 있는 '/' 뒤에 꺼")
fun boardListPost(@FieldMap fields: MutableMap<String, String>): Call<ResponseBody>
}
object NoticeNetwork {
private const val baseUrl = "여기에는 베이스 url"
private val retrofit = Retrofit.Builder()
.baseUrl(baseUrl)
.addConverterFactory(GsonConverterFactory.create())
.build()
fun getJsonApi(): JsonPlaceHolderApi {
return retrofit.create(JsonPlaceHolderApi::class.java)
}
}
ResponseBody는 받아온 데이터를 가공 없이 출력하기 위한 자료형이다.
val call = NoticeNetwork.getJsonApi().boardListPost(parameter)
call.enqueue(object : Callback<ResponseBody> {
override fun onResponse(
call: Call<ResponseBody>,
response: Response<ResponseBody>
) {
if(response.isSuccessful) {
try {
response.body()!!.string() // <- 여기에 결과가 들어있음
} catch (e: Exception) {
}
}
}
override fun onFailure(call: Call<ResponseBody>, t: Throwable) {
}
})
body에는 인스턴스의 주소값이 들어있으므로 그냥 사용하면 안 된다.
parameter에 필수 파라미터 값을 넣어서 post요청을 보내고 (get이면 get을 해야겠쥬?)
최종적으로 body()에 결과가 담겨 나오는데 string()으로 메서드를 사용하면 데이터를 얻을 수 있다.
(toString 아님!!) 이제 얻은 데이터를 Jsoup으로 원하는 대로 파싱 해서 사용하면 된다.
결론은 일반적인 retrofit 사용방법에서 <ResponseBody>를 사용하고 string()만 사용하면 된다는 거
나는 이걸 몰라서 몇 시간 동안 헛짓거리 하고 다녔다는 거
lifecycleScope.launch(Dispatchers.IO){
var rep = NoticeRepository()
rep.requestPost()
}
액티비티에서 비동기 실행을 해야 함을 잊지 말자.
이것저것 시도해보다가 예제 코드 구조가 약간 개떡같이 됐지만 잘 돌아간다.
3. 시도해본 방법들
3-1. gson
var gson = GsonBuilder().setLenient().create() // 이걸
private val retrofit = Retrofit.Builder()
.baseUrl(baseUrl)
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.addConverterFactory(GsonConverterFactory.create(gson)) // 여기다가 넣음
.build()
이렇게 하면 된다고 해서 이렇게도 해봄
3-2. webView
webView를 이용해 해결하신 분의 글은 여기에서 볼 수 있다.
웹뷰를 통해 실제로 해당 페이지에 들어가서 크롤링하는 방법도 있었다.
왜인지 모르겠지만 난 이 방법도 먹히질 않았다. 정적인 부분은 잘 긁어와 지는데 동적인 부분은 가져오질 못했다.
관련 글이 많이 있는 게 아닌 걸로 봐서 정석 방법은 아닌 것 같다. 약간 야매 느낌?
그 외에 다양하게 이것저것 시도해봤는데 진짜 너무 엉뚱한 방향으로 삽질을 해서.. 안 적어야겠다.. 부끄러움...
💡 느낀 점
- 앱 개발자가 꿈이지만 웹에 대해서도 공부가 많이 필요할 것 같다.
- 파이썬에서 셀레니움을 이용하는 것처럼 안드로이드에서 웹뷰를 이용해 크롤링을 할 수 있다니.. 흥미로웠다.
- okhttp가 retrofit의 베이스라는 걸 공부해놓고 망각하고 있었다. 리마인드 하게 됨
📘 참고한 자료
'오늘은 뭘 배울까? > Android' 카테고리의 다른 글
안드로이드 Room의 사용법과 예제 (18) | 2021.04.03 |
---|---|
Retrofit이란? (사용하기 전에 알아야 할 것들) (10) | 2021.04.02 |
앱을 삭제했는데 데이터가 남아있어요 + Room cannot verify the data integrity. (0) | 2021.03.13 |
RecyclerView + MVVM + Room을 연습해보자! (0) | 2021.03.11 |
안드로이드 View Model(뷰 모델)을 공부해보자! (14) | 2021.03.08 |
댓글