A ship in harbor is safe, but that is not what ships are built for.

개발일지/AI 캠프

내일배움캠프 AI - 66일차 TIL, 2022.12.02

플리피나리 2022. 12. 5. 03:50
반응형

스파르타 코딩클럽 내일배움캠프 AI 웹개발자양성과정 3회차

2022.12.02. 66일차 - TIL

 

 

1. 크롤링 구현하기

처음에는 여기유라는 사이트에서 제공하는 지역축제 정보들을 크롤링 하기로 했다. 버튼을 눌러서 가져오는 동적 크롤링이 필요하고, 앞으로도 계속 쓰일 것 같아 selenium을 이용해 크롤링해보고 싶었으나 크롬 드라이버의 문제 때문이지 잘 동작하지 않았다. 그러다 우연히 문화체육관광부 사이트에 들어가 확인해보니 이전에 우리가 beautifulsoup으로 크롤링했던 것처럼 url에 일정 규칙이 나오는 것을 보고 해당 페이지를 크롤링하기로 방향을 바꿨다.(어차피 내용도 똑같아서 아무 페이지나 괜찮았다.) 이전과 비슷한 형식이다 보니 함께 머신러닝을 담당하는 분이 바로 크롤링에 성공하셨고, 그렇게 selenium은 깨끗하게 던져버렸다.(그래도 많이 사용된다고 하니 나중에 복습할 때 한번 사용해보기로...) 나는 다른 분이 만드신 코드에 마지막 페이지를 크롤링할 때의 문제(다른 페이지는 5개의 카드인데 마지막 페이지만 카드가 4개여서 중복되어 출력되고, 각 축제 id가 초기화되어 찍혔다...)를 조금 수정했고, csv 파일을 생성하는 코드를 추가했다.

 

일단 beautifulsoup과 requests를 가상환경에 설치하고, import한다.

$ pip install requests
$ pip install beautifulsoup4

import requests
from bs4 import BeautifulSoup

1~8페이지까지 크롤링해야 하기 때문에 각 페이지의 url을 확인해보니 https://www.mcst.go.kr/kor/s_culture/festival/festivalList.jsp?pCurrentPage={num} 과 같은 형식으로 num값을 변경해 각 page를 크롤링한다. 이때 for문에서 range(8)로 설정했는데 range를 이용하면 0부터 인덱스가 시작되기 때문에 page_num에 1을 더해준 url을 이용한다.

# range를 이용하면 0부터 인덱스가 시작되므로 page_num에 1을 더해준 url을 이용
festival_id = 1
for page_num in range(8):
    url = f'https://www.mcst.go.kr/kor/s_culture/festival/festivalList.jsp?pCurrentPage={page_num+1}'
    headers = {'User-Agent': 'Chrome'}
    response = requests.get(url, headers=headers)
    soup = BeautifulSoup(response.text, 'html.parser')

첫번째 for문을 통해 리스트에서 각 카드에 대한 title, desc, period를 크롤링하고(여기서가 더 깔끔해서..) 상세 페이지로 이동할 수 있도록 link 크롤링한 값을 정리하여 result_url로 링크값을 설정한다. 그리고 result_url 주소로 들어가서 한번 더 원하는 내용을 크롤링한다. 이때는 image, region, place, price를 크롤링했다. 마지막으로 festival_data로 크롤링한 값들을 리스트에 추가한다. 마지막 페이지는 if문을 사용해 page_num이 7일 경우 4개까지 크롤링하도록 설정했다. 

import requests
from bs4 import BeautifulSoup

# range를 이용하면 0부터 인덱스가 시작되므로 page_num에 1을 더해준 url을 이용
festival_id = 1
for page_num in range(8):
    url = f'https://www.mcst.go.kr/kor/s_culture/festival/festivalList.jsp?pCurrentPage={page_num+1}'
    headers = {'User-Agent': 'Chrome'}
    response = requests.get(url, headers=headers)
    soup = BeautifulSoup(response.text, 'html.parser')
    
    if page_num != 7:
        for num in range(5):
            data = soup.select(f"#content > div.contentWrap > ul > li:nth-child({num+1})")
            # 리스트에서 뽑기
            for item in data:
                title = item.select_one('div.text > p').text
                link = item.select_one('a').get('href')
                desc = item.select_one('div.text > div').text.replace('\r\n', '')
                period = item.select_one('div.text > ul > li:nth-child(1)').text
            result_url = 'https://www.mcst.go.kr/kor/s_culture/festival/' + link

            headers2 = {'User-Agent': 'Chrome'}
            response2 = requests.get(result_url, headers=headers2)
            soup2 = BeautifulSoup(response2.text, 'html.parser')

            data2 = soup2.select("#content > div.contentWrap > div.viewWarp")
            
            # 상세페이지에서 뽑기
            for item in data2:
                image= 'https://www.mcst.go.kr' + item.select_one('img').get('src')
                region= item.select_one('dl > dd:nth-child(2)').text
                place = item.select_one('dl > dd:nth-child(10)').text
                price = item.select_one('dl > dd:nth-child(12)').text
                
                festival_data = [festival_id, title, desc, image, region, place, price, period]
                print(festival_data)
            festival_id += 1
                
                
    else:  # 마지막 페이지는 4번만 반복
        for num in range(4):
            data = soup.select(f"#content > div.contentWrap > ul > li:nth-child({num+1})")
            for item in data:
                title = item.select_one('div.text > p').text
                link = item.select_one('a').get('href')
                desc = item.select_one('div.text > div').text.replace('\r\n', '')
                period = item.select_one('div.text > ul > li:nth-child(1)').text
            result_url = 'https://www.mcst.go.kr/kor/s_culture/festival/' + link

            headers2 = {'User-Agent': 'Chrome'}
            response2 = requests.get(result_url, headers=headers2)
            soup2 = BeautifulSoup(response2.text, 'html.parser')

            data2 = soup2.select("#content > div.contentWrap > div.viewWarp")
            
            for item in data2:
                image= 'https://www.mcst.go.kr' + item.select_one('img').get('src')
                region= item.select_one('dl > dd:nth-child(2)').text
                place = item.select_one('dl > dd:nth-child(10)').text
                price = item.select_one('dl > dd:nth-child(12)').text
                
                festival_data = [festival_id, title, desc, image, region, place, price, period]
                print(festival_data)
            festival_id += 1

사실 문제를 해결하는 것에 급급해 코드를 짰기 때문에 그렇게 좋은 코드라고는 생각되지 않는다.

그 이유는 

첫번째) 동일한 기능으로 중복되는 코드가 발생한다.(쓸데없이 코드가 길어지는 불편한 상황)

두번째) 웹 크롤링은 결국 자동화가 최종 목표인데, 각 페이지의 카드 수(특히 마지막 페이지)를 찾아 직접 입력해야 하는 불편함이다.(펼쳐지는 카드 수, 페이지 수, 마지막 페이지의 카드 개수를 구하는 수식을 만드는 것이 더 좋을 듯 하다...)

 

추가로 csv로 파일로 만들고 생긴 큰 오류가 있는데... 문화체육관광부 사이트의 상세 페이지에 값이 제대로 들어가 있지 않은 경우를 볼 수 있었다. 그래서 일단 수기로 오류를 수정하기는 했는데... 이게 맞는 방법인지 모르겠다..(일단 월요일에 튜터님께 여쭤보기로... 데이터 분류... 똑바로 해라.....)

반응형