SwiftSoup을 이용한 웹 크롤링

낯선 언어를 매일 학습하며 몰입과 성장을 기록하는 일지

  ·  2 min read

문제 상황 #

로또는 API가 없다

  • API가 없어서 어떻게 당첨 결과를 가져와야 할지 고민
  • 웹 크롤링을 통해 직접 데이터를 긁어오고, QR 코드 스캔을 통해 내 복권 번호를 인식하는 기능 구현으로 변경
  • 웹 크롤링의 경우 외부 라이브러리(Swift Soup)을 통해 구현

SwiftSoup #

  • SwiftSoup을 통해 HTML을 파싱하여 등수별 당첨금, 총 판매액 등 다양한 정보 로딩

트러블 슈팅 #

  1. EUC-KR 인코딩 문제
  • 동행복권 웹페이지가 UTF-8이 아닌 EUC-KR을 통한 인코딩 방식을 채택
  • CFStringConvertEncodingToNSStringEncoding(0x0940)을 통해 EUC-KR 인코딩 설정
let urlString = "https://dhlottery.co.kr/gameResult.do?method=byWin&drwNo=\(round)"

guard let url = URL(string: urlString) else {
    print("URL 생성 실패")
    throw CrawlerError.invalidURL
}

do {
    let (data, response) = try await URLSession.shared.data(from: url)

    if let httpResponse = response as? HTTPURLResponse {
        print("상태 코드: \(httpResponse.statusCode)")
    }

    let encoding = CFStringConvertEncodingToNSStringEncoding(0x0940)
    guard let html = String(data: data, encoding: String.Encoding(rawValue: encoding)) else {
        print("EUC-KR 디코딩 실패")
        throw CrawlerError.decodingFailed
    }
}
  1. HTML 구조 분석 및 파싱
  • HTML 문자열을 얻은 후 SwiftSoup을 이용해 원하는데이터만 추출
  • Gemini를 통해 CSS Selector선별 이를 이용하는 방식
  • 가져온 데이터를 LottoResult라는 모델로 변환하여 사용
// SwiftSoup으로 데이터 추출하기
let doc = try SwiftSoup.parse(html)

// 1. 회차 정보 추출
guard let roundText = try doc.select("div.win_result h4 strong").first()?.text() else { return nil }

// 2. 당첨 번호 공(Ball) 추출
let numberElements = try doc.select("div.num.win span.ball_645")
let numbers = numberElements.compactMap { element -> Int? in
    guard let numberText = try? element.text() else { return nil }
    return Int(numberText)
}