SwiftSoup을 이용한 웹 크롤링
낯선 언어를 매일 학습하며 몰입과 성장을 기록하는 일지
· 2 min read
문제 상황 #
로또는 API가 없다
- API가 없어서 어떻게 당첨 결과를 가져와야 할지 고민
- 웹 크롤링을 통해 직접 데이터를 긁어오고, QR 코드 스캔을 통해 내 복권 번호를 인식하는 기능 구현으로 변경
- 웹 크롤링의 경우 외부 라이브러리(Swift Soup)을 통해 구현
SwiftSoup #
- SwiftSoup을 통해 HTML을 파싱하여 등수별 당첨금, 총 판매액 등 다양한 정보 로딩
트러블 슈팅 #
- 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
}
}
- 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)
}