티스토리 뷰

2023.05.19 - [여러가지/기타] - 농담으로 한 말이 이렇게 되었습니다.

 

농담으로 한 말이 이렇게 되었습니다.

잠깐 지금 일이 커져서 진심으로 만들고 있는 개발팀입니다. HTML 삽입 미리보기할 수 없는 소스 ========================================================== 크롤링용이라 딱히 의미는 없습니다. 연습용이기때

world-of-larooly.tistory.com

이렇게 일이 진행될 줄은 몰랐는데 

이왕 이렇게 된거 간단히 해봅시다. 

 

일단 저는 위 페이지에 있는 식당 리스트를 가져와서 랜덤으로 찍어주는 함수를 만들꺼에요. 

(이렇게 만들면 서버 따로 안만들고 저것만 변경해서 식당리스트를 제 맘대로 관리할수있으니까요)

 

근데 그전에 잠깐 지금 무엇을 하는 건지

모르시는 분들을 위해 간단히 몇가지 이야기 하고 넘어갑시다. 

 

웹 크롤링 (Web Crawling)

- 웹 페이지를 그대로 가져와서 거기서 데이터를 추출하는 방법을 말합니다. 

- 여기서 웹페이지를 가져온다는 뜻은 해당 웹의 html 같은 구성 코드를 들고온다는 의미입니다.

 

해당 웹페이지의 구성 코드를 어떻게 알고 필요한 걸 가져오나요?

- 필요시 해당 웹페이지로 들어가서 코드를 직접 보고 판단해 가져오는 방법도 있습니다. 

1. 원하시는 웹페이지에서 우클릭 후 아래 이미지를 참고하셔서 "검사"를 눌러주세요.

2. 그럼 아래와 같이 해당 페이지를 구성하는 코드가 오른쪽에 뜨게 됩니다. 

라이언 오른쪽의 코드가 실제 웹페이지를 구성하는 코드입니다.

자 이제 다시 Xcode 로 돌아와서 이야기 해볼까요?

아 그전에 참고말하면 저희는 아래 사진에 보이는 "RestrauntList"를 가져오는게 목표입니다.

실제 사이트의 html 의 일부분입니다.

저는 SwiftSoup 이라는 패키지를 사용할겁니다. 

(애는 가져온 데이터를 사용하기 쉽게 도와주는 패키지입니다(통신 X))

https://github.com/scinfu/SwiftSoup

 

GitHub - scinfu/SwiftSoup: SwiftSoup: Pure Swift HTML Parser, with best of DOM, CSS, and jquery (Supports Linux, iOS, Mac, tvOS,

SwiftSoup: Pure Swift HTML Parser, with best of DOM, CSS, and jquery (Supports Linux, iOS, Mac, tvOS, watchOS) - GitHub - scinfu/SwiftSoup: SwiftSoup: Pure Swift HTML Parser, with best of DOM, CSS,...

github.com

pod 'SwiftSoup'

패키지가 추가되어있다는 가정하에 진행하겠습니다. 

(cocoapod 이나 swift package manager 를 통해 추가해주세요.)

import SwiftSoup // 여기에 오류가 안나면 설치된겁니다.

이제 여기서 부터는 금방입니다. 

저희는 아까 그 웹사이트를 사용할꺼라고 했죠? 

let testUrl = "https://world-of-larooly.tistory.com/129"

(위 주소는 연습하시는 분들을 위해 계속 열어두겠습니다.)

 

아 그런데 일단 통신을 해야 웹페이지랑 연결이 가능하겠죠? 

그래서 잠시 URLSession 을 이용해 웹페이지 정보를 가져오겠습니다. 

 (URLSession : 기본으로 제공해주는 통신 도우미라고 생각하시면 됩니다.)

* 원래는 다른방식도 가능했는데 Xcode14 기준 보라색 경고문이 떠서 아래 방법으로 변경했습니다.*

if let url = URL(string: testUrl) {
	URLSession.shared.dataTask(with: url) { (data, response, error) in
    // 여기서 처리해주시면됩니다.
    }.resume()
}

* 왜 URLSession 을 사용해서 표현하나요?

- 그냥 하게 되면 웹페이지에서 정보를 가져온 후에 화면을 그려 사용자 입장에서는 느리거나 멈춘것처럼 보일수있습니다. 

- 아래 방식을 사용하면 다운로드를 다 받지 않아도 다른 코드를 실행해 이와 같은 일을 줄일 수 있습니다.

- 이런걸 보통 비동기 처리 방식 이라고 합니다. 

 

이제 받아온 데이터를 html 로 바꿔서 본격적으로 크롤링 해봅시다!

저는 해당 부분이 길어져서 전체 html 을 받아와 String 으로 만들고

전체 html(String) 에서 제가 원하는 부분을 뽑아내는 함수를 따로 만들었습니다. (getStringListFromHtml)

URLSession.shared.dataTask(with: url) { (data, response, error) in
    guard let htmlData = data else { return }
    let webString = String(data: htmlData, encoding: .utf8)
    print(webString) // 해당 웹 페이지의 html 이 출력됩니다.
    let foodList = self.getStringListFromHtml(htmlString: webString!, kind: "RestrauntList") + self.getStringListFromHtml(htmlString: webString!, kind: "BreadList")
  
    DispatchQueue.main.async {
    // UI에 관련된 처리를 하려면 반드시 main스레드에서 작성해주세요!
    	self.textLbl.text = foodList.randomElement()
    }
}.resume()

일단 코드 먼저 보여드릴께요.

 func getStringListFromHtml(htmlString : String, kind : String) -> [String] {
        var resultArray : [String] = []
        do{
            let doc = try SwiftSoup.parse(htmlString)
            if let getList = try doc.getElementById(kind)?.select("p").array(){
                for item in getList{
                    let text = try item.select("p").text()
                    resultArray.append(text)
                }
            }
            return resultArray
        }catch{
            return []
        }
    }

생각보다 간단합니다

저는 해당 부분을 [String]으로 가져오기 위해 저렇게 표현을 했습니다.

일단 해당 조건을 만족하는 부분을 배열의 형태로 가져오고 

let doc = try SwiftSoup.parse(htmlString) // 전체 html 코드 
if let getList = try doc.getElementById(kind)? //특정 id부분을 잘라서 가져오고
			.select("p") // 해당 부분 안의 <p></p>를 가져와
                        .array() // 배열의 형식으로 표현해달라

아래처럼 해당 배열을 [String] 형태로 가져오는 겁니다. 

for item in getList{
    let text = try item.select("p").text() // 해당 <p></p>안에 글씨를 가져오는 부분
    resultArray.append(text) // 배열안에 해당 String 을 넣어줍니다.
}

html 에서 자신이 데이터를 가져오길 원하는 부분의 id만 파악하면 위처럼 뽑아내는게 가능합니다.

간단하죠? 

아래는 전체 코드입니다. 

import UIKit
import SwiftSoup

class ViewController: UIViewController {

    @IBOutlet weak var textLbl: UILabel!
    let testUrl = "https://world-of-larooly.tistory.com/129"
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        setListFromStringrURL(stringUrl: testUrl)
    }
    func getStringListFromHtml(htmlString : String, kind : String) -> [String] {
        var resultArray : [String] = []
        do{
            let doc = try SwiftSoup.parse(htmlString)
            if let getList = try doc.getElementById(kind)?.select("p").array(){
                for item in getList{
                    let text = try item.select("p").text()
                    resultArray.append(text)
                }
            }
            return resultArray
        }catch{
            return []
        }
    }
    func setListFromStringrURL(stringUrl: String) {
        if let url = URL(string: stringUrl) {
        URLSession.shared.dataTask(with: url) { (data, response, error) in
            guard let imageData = data else { return }
            let webImage = UIImage(data: imageData)
            let webString = String(data: imageData, encoding: .utf8)
            let foodList = self.getStringListFromHtml(htmlString: webString!, kind: "RestrauntList") + self.getStringListFromHtml(htmlString: webString!, kind: "BreadList")
            DispatchQueue.main.async {
                  self.textLbl.text = foodList.randomElement()

            }
        }.resume()
     }
}

 

* 번외 

- 참고로 여기서 Data 타입으로 넘어오기 때문에 원하시는 타입으로 바꿔 쓰시면

이미지나 파일도 가져오는게 가능합니다.   

URLSession.shared.dataTask(with: url) { (data, response, error) in          
	guard let imageData = data else { return }
	let webImage = UIImage(data: imageData)
}.resume()

 

뭔가 간단하게 적는다고 쓰다보니 

내용이 길어졌네요. 

 

그래도 누군가에게 도움이 되었으면 좋겠네요. 

오늘도 파이팅입니다. 

댓글