본문 바로가기
Swift

[Swift] iOS에서 네이버 날씨 크롤링 하기

by Jimmy_iOS 2023. 6. 10.

지난 포스팅에서 RESTful API에 대해서 배워보았습니다.

 

Restful API란 무엇인가?

간혹 iOS개발자 구인글을 보면 ‘REST API’ 혹은 ‘RESTful API’ 통신에 대한 이해 를 우대한다는 것을 쉽게 볼 수 있는데 이번 포스팅에서는 RESTful API에 대해서 알아보겠습니다. 개요 RESTful API는 Repr

jimmy-ios.tistory.com

API와 HTTP통신에 대한 이해를 바탕으로 네이버 날씨 API에서 URI주소를 분석해 쿼리값을 알아내고

하나씩 뜯어보며 크롤링 하는 방법을 알아보겠습니다.

그리고 받아온 데이터를 Swift에서 어떻게 받아오고 가공하는지 알아보겠습니다.

네이버 날씨 웹 페이지

우선 네이버 날씨에 들어가서 기상청 데이터를 받아올꺼고 현재 위치를 기준으로 날씨 데이터를 받아오는 받아와 보겠습니다.

 

그러면 크롬에서 개발자툴인 F12를 눌러보겠습니다.

개발자 툴에 들어가게 되면 

 

위와 같은 화면이 보일텐데요. 여기서 저희가 사용할 기능은 바로 좌측에 있는 선택버튼과 Network버튼 입니다.

 

 

Network가 선택되어 있는 상태에서 좌측에 GPS 현재위치 버튼을 누르면

네트워크 통신이 발생하면서 개발자툴 밑에 뭔가가 생겼습니다

 

 

좌측에 naverRgnCatForCoords?lat=27.58230349&lng=152.9204674 를 클릭해

Headers를 보게되면 Requesr URL에 api주소와 쿼리값으로 위도와 경도값을 얻을 수 있습니다.

 

 

 

그러면 이 URI값을 포스트맨에 입력을 해서 어떤 값이 나오는지 확인해 봅시다.

 

포스트맨에서 Body값을 확인해보니 regionCode값을 획득했습니다!

이 코드는 뒤에서 사용할 네이버의 위치코드를 입력할때 필요한 코드입니다.

 

이 값을 얻고싶다면 앞으로 GPS를 통해 경도와 위도 값을 얻은다음

https://weather.naver.com/api/naverRgnCatForCoords?lat=위도값&lng=경도값으로 regionCode를 얻을 수 있습니다.

 

 

그 다음 밑에 있는 pastWetr?regionCode=09410620&aplYm=  를 보면

좀 전에 구한 regionCode를 통해 통신한 Get Request를 확인할 수 있습니다.

 

어떤 데이터를 GET을 통해 받아왔는지 볼까요?

 

 

Response에 들어가서 보면 다음과 같은 JSON형태의 데이터를 불러왔습니다.

 

이 데이터는 무엇일까요? 

 

확인해보니 5월28일부터 가져온 데이터는 과거날씨에 있는데요.

 

Swift에서 어떻게 위도와 경도를 얻고,

얻은 위도와 경도값으로 어떻게 regionCode를 얻고,

그 코드로 어떻게 과거날씨를 가져오는지 한번 알아보겠습니다.

 

아래는 Swift에서 GPS를 사용하여 현재 위치의 위도와 경도 값을 가져와서

Naver Weather API의 URL을 생성하고 regionCode를 얻는 방법입니다.

import CoreLocation

// 현재 위치를 가져 오기위한 CLLocationManager 인스턴스를 만듭니다.
let locationManager = CLLocationManager()

// 위치 사용 권한 요청
locationManager.requestWhenInUseAuthorization()
// 지역코드
var regionCode: String = ""
// 현재 위치의 위도와 경도 가져 오기
if let currentLocation = locationManager.location?.coordinate {
    let lat = String(currentLocation.latitude)
    let lng = String(currentLocation.longitude)

    // 위도와 경도 값을 사용하여 API 엔드 포인트 URL 빌드
    let apiUrl = URL(string: "https://weather.naver.com/api/naverRgnCatForCoords?lat=\(lat)&lng=\(lng)")!

    // API 엔드 포인트로 요청 보내기
    let task = URLSession.shared.dataTask(with: apiUrl) { (data, response, error) in
        if let error = error {
            print("오류: \(error.localizedDescription)")
            return
        }
        guard let data = data else {
            print("오류: 반환된 데이터가 없습니다.")
            return
        }
        do {
            // 응답 JSON을 구문 분석하여 지역 코드 가져 오기
            let json = try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any]
            if let regionCode = json?["regionCode"] as? String {
								self.regionCode = regionCode
                print("지역 코드: \(regionCode)")
            } else {
                print("오류: 응답에서 regionCode를 구문 분석할 수 없습니다.")
            }
        } catch {
            print("오류: \(error.localizedDescription)")
        }
    }

    // 요청 작업 시작
    task.resume()
} else {
    print("오류: 현재 위치를 가져올 수 없습니다.")
}

 

그 다음 JSON코드를 받아오는 방법은 아래와 같습니다.

let url = URL(string: "https://weather.naver.com/today/api/pastWetr?regionCode=\(regionCode)&aplYm=")
let task = URLSession.shared.dataTask(with: url!) { (data, response, error) in
    if error != nil {
        print(error!)
    } else {
        if let urlContent = data {
            do {
                let jsonResult = try JSONSerialization.jsonObject(with: urlContent, options: JSONSerialization.ReadingOptions.mutableContainers) as AnyObject
                print(jsonResult)
            } catch {
                print("JSON Processing Failed")
            }
        }
    }
}
task.resume()

 

받아온 JSON코드를 담을 사용자 구조체를 만들어줍니다.

 

이 데이터를 Swift에서 구조체로 저장하려면,

JSON의 각 키-값 쌍에 대한 속성을 갖는 구조체를 만들 수 있습니다.

그런 다음, 이러한 구조체의 배열을 생성하여 JSON의 모든 데이터를 보유할 수 있습니다.

struct WeatherData: Codable {
    let selectedYear: String
    let selectedMonth: String
    let pastYearList: [String]
    let pastMonthList: [Int]
    let pastWetrCalendarList: [PastWeatherData]
}

struct PastWeatherData: Codable {
    let solarDate: String
    let year: String
    let month: String
    let date: String
    let dayOfWeek: String
    let solarWeek: String
    let dayOff: Bool
    let thisMonth: Bool
    let pastWetrData: WetrData
}

struct WetrData: Codable {
    let aplYmd: String
    let lareaNm: String
    let mareaNm: String
    let wetrCd: String
    let wetrTxt: String
    let minTmpr: Double
    let maxTmpr: Double
    let rainAmt: String
    let reportYmdt: String
}

그런 다음 JSONDecoder를 사용하여 JSON 데이터를 CalendarData 구조체의 인스턴스로 디코딩 할 수 있습니다.

 

다음은 예시 코드입니다.

let decoder = JSONDecoder()
let data = jsonResult.data(using: .utf8)!
let calendarData = try! decoder.decode(CalendarData.self, from: data)

 

jsonResult은 JSON 형식의 문자열을 나타냅니다. 

data(using: .utf8)를 호출하여 Data 형식으로 변환 한 다음 

JSONDecoder를 사용하여 해당 데이터에서 CalendarData 구조체를 디코딩합니다.