SwiftSoup을 사용한 웹 크롤링

silverCastle·2022년 8월 20일
0

들어가기 전에, 필자는 과거 '반스' 브랜드의 '스타일36' 제품을 사고 싶었지만 재고가 몇개 남아있는지 명시적으로 보여주지 않아 웹 크롤링을 통해 재고를 알아냈다. 그때는 파이썬의 Beautiful Soup을 사용하였는데, 지금은 iOS 앱에서 웹 크롤링을 쓰기 위해 SwiftSoup 라이브러리를 사용하려고 한다. 그런데 왜 공통적으로 Soup이라는 단어를 쓰는지 궁금하여 찾아보았더니 뒤죽박죽 섞여있는 구조를 가진 웹 문서가 마치 스프와 같다고 하는데 이 스프를 아름답게 해주는 라이브러리라고 하여 지어진 이름이었다.
어쨌든간에 이 내용은 본문과 별 상관이 없이 단순한 필자의 궁금증 때문에 이야기하였다..

✍️ SwiftSoup 라이브러리 설치

https://github.com/scinfu/SwiftSoup
위 링크를 통해 라이브러리를 설치한다.

Cocoapod(코코아팟) 설치 및 사용 방법
pod install 시 에러
설치하는 방법을 모르겠다면 위 링크들을 참고한다!

✍️ 예제를 통한 사용법

필자는 웹 크롤링을 사용하는 이유가 베이킹 관련 앱을 만들기 위해서이므로 아래와 같은 관련 웹 사이트를 크롤링할 것이다.
https://www.serveq.co.kr/recipe/BAK/recipe_view?R_IDX=1417&PAGESIZE=12&SORTCOL=&SORTDIR=&SEL_R_CATE_CODE=BAK001&SEARCH_COL=&SEARCH_KEYWORD=

프로젝트를 하나 생성하여 RecipeClass 파일을 만든다. 이 class는 ObservableObject 프로토콜을 상속받는다. url은 웹 크롤링을 하고 싶은 주소를 입력하면 되고 recipeTitles와 recipeContents는 각각 웹 크롤링을 통해 가져올 제목과 내용을 저장할 프로퍼티이다.

let url = URL(string: "https://www.serveq.co.kr/recipe/BAK/recipe_view?R_IDX=1417&PAGESIZE=12&SORTCOL=&SORTDIR=&SEL_R_CATE_CODE=BAK001&SEARCH_COL=&SEARCH_KEYWORD=")
    @Published var recipeTitles: [String] = []
    @Published var recipeContents: [[String]] = []

사이트에서 개발자 도구 혹은 원하는 부분을 우클릭해 검사를 선택하여 코드를 아래와 같이 볼 수 있다.

위 구조를 보면 필자가 가져올 제목은 "make" id를 가진 태그에 있는 h4태그에 있는 텍스트이다. 만드는 방법, 제조공정, 충전물 제조를 가져올 것이다. 옵셔널 바인딩과 do-catch문을 통해 혹시 모를 예외를 처리한다. titles 프로퍼티에 값을 할당하는 코드의 의미는 "make"라는 id를 가진 element에 있는 h4태그를 선택하여 그 정보를 배열로 저장한다는 의미이다. 여기서 주의할 점은, titles는 [String]? 타입이 아니라 [Element]? 타입이라는 것이다. titles 배열의 각 원소를 for문을 통해 recipeTitles에 저장하면 끝이다.

// MARK: - 제목 가져오기
    func fetchTitle() {
        if let url = url {
            do {
                let webString = try String(contentsOf: url)
                let document = try SwiftSoup.parse(webString)
                
                var titles = try document.getElementById("make")?.select("h4").array()
                for title in titles! {
                    let t = try title.select("h4").first()?.text()
                    recipeTitles.append(t!)
                }
            } catch let error {
                print(error)
            }
        }
    }

내용을 가져오는 것 또한 같은 맥락으로 생각하면 되는데 다른 점은 recipeContents는 [String] 타입이 아니라 [[String]] 타입이기 때문에 이중 for문을 사용하였다.

// MARK: - 내용 가져오기
    func fetchContent() {
        if let url = url {
            do {
                let webString = try String(contentsOf: url)
                let document = try SwiftSoup.parse(webString)
                
                var contents = try document.getElementById("make")?.select("div").select("ol").array()
                for content in contents! {
                    let c = try content.select("li").array()
                    var arr: [String] = []
                    for j in c {
                        arr.append(try j.text())
//                        recipeContents.append(try j.text())
                        print(j)
                    }
                    recipeContents.append(arr)
                }
            } catch let error {
                print(error)
            }
        }
    }

이제 ContentView로 돌아와 각 메소드들을 호출하여 보여주기만 하면 끝이다. 아래는 그 결과이다.

이렇게 해서 SwiftSoup을 다뤄보았는데 사용하다보면 익숙해질 것이다.
전체 코드는 아래에서 확인할 수 있다.
https://github.com/eunsung-dev/SwiftSoupDemo

0개의 댓글