나의 공부 일기

프로젝트) 크롤링/스크랩핑 프로젝트 3일차 본문

프로젝트

프로젝트) 크롤링/스크랩핑 프로젝트 3일차

곽병권 2023. 12. 6. 11:25
728x90

같이 프로젝트를 진행한 팀원: https://jihoon44-it.tistory.com/

 

 

 

2일차에서는 

https://k-python-note-taking.tistory.com/60

 

 

프로젝트) 크롤링/스크랩핑 프로젝트 2일차

1일차에서 https://k-python-note-taking.tistory.com/59 프로젝트) 크롤링/스크랩핑 프로젝트 1일차 저는 지금 파이썬 개발자와 데이터분석에 관한 국비지원을 받고 있습니다. 2주간 글을 작성하지 안했던것

k-python-note-taking.tistory.com

여기까지 다뤄보았습니다.

 

이번에는 오류를 어떻게 수정하였는지, 추가적으로 작성된 코드는 무엇인지 작성해보겠습니다.

from bs4 import BeautifulSoup
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
import time
from urllib.parse import quote
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
import re

import pandas as pd
def switch_frame(frame):
    browser.switch_to.default_content()  # frame 초기화
    browser.switch_to.frame(frame)  # frame 변경

# 각 iframe들 [searchIframe / entryIframe / ]

ls2 = ['강남구', '강동구', '강북구', '강서구', '관악구', '광진구', '구로구',' 금천구', '노원구', '도봉구', '동대문구', '동작구', '마포구',
     '서대문구', '서초구', '성동구', '성북구', '송파구', '양천구', '영등포구', '용산구', '은평구', '종로구', '중구', '중랑구']
# -----------------------------------25개의 구를 한번에 하면 인터넷 문제로 오류가 자주남-----------------------------------
# -----------------------------------5개 구로 쪼개서 하나씩 돌리며 파일을 만듬-----------------------------------
ls = ['강남구']

cafe_name_list = []
cafe_classification_list = []
cafe_review_count_list = []
cafe_address_list = []
cafe_review_list = []
background_image_url_list = []
cafe_menu_ls_list = []

# 처음 모든구를 한번에 돌릴려고 했으나 인터넷문제로 에러가 자주남. for문 제거
# for i in range(len(ls)):  # 모든 자치구에 있는 카페를 알아보기 위해 반복
# 브라우저 열기
keyword = quote(f'{ls[i]} 카페')
browser = webdriver.Chrome()
browser.get(url)
browser.implicitly_wait(10)

# 스위치 변경
switch_frame("searchIframe")
time.sleep(1)
# 버튼 변수들
next_btn = browser.find_elements(By.CSS_SELECTOR, '.zRM9F> a')
browser.implicitly_wait(10)

# 버튼길이만큼 반복 첫번재는 이전버튼 이므로 제외
for btn in range(len(next_btn))[1:]:
    print(f'------------------------------------{btn}/{len(next_btn)-1}------------------------------------')

    #body부분을 잡기 위해 쓸데없는 버튼을 클릭해줌
    browser.find_element(By.XPATH, '//*[@id="_pcmap_list_scroll_container"]/ul/li[1]/div[1]/div[1]/div').click()

    #검색결과가 모두 보이지 않기 때문에 page down을 눌러 끝까지 펼쳐준다.
    for scroll_1 in range(8):
        browser.find_element(By.TAG_NAME, 'body').send_keys(Keys.END)

    resp = browser.page_source     # 처음 브라우저 소스와, 버튼을 누를때마다 그것에 관한 페이지 소스 가져옴
    soup = BeautifulSoup(resp, 'html.parser') # 브라우저소스를 html로 변환하여 가져옴
    length = len(soup.select('#_pcmap_list_scroll_container > ul > li'))+1  # 한페이지에 보이는 카페들의 개수 반환

    # 카페들의 개수만큼 반복
    for j in range(1, length+1):
        # 스위치 변경
        switch_frame("searchIframe")

        # 해당하는 카페에 정보를 알기위해 그 카페를 클릭해줌
        # 구마다 /html/body/div[3]/div/div[3]....../sapn[1] or /html/body/div[3]/div/div[4]....../sapn[1] 가 있음
        try:
            browser.find_element(By.XPATH, f'/html/body/div[3]/div/div[3]/div[1]/ul/li[{j}]/div[1]/a/div/div/span[1]').click()
        except NoSuchElementException:
            browser.find_element(By.XPATH, f'/html/body/div[3]/div/div[4]/div[1]/ul/li[{j}]/div[1]/a/div/div/span[1]').click()
        finally:
            # 카페정보를 받아오는 시간 충분하게 받아줌 인터넷이 느려 충분히 시간 줘야됌
            time.sleep(3)

        # 스위치 변경
        switch_frame("entryIframe")

        # 버튼이 누를 필요 없어서 제거
#---------------------------------------------------------------------------------------

#         browser.find_element(By.CLASS_NAME,'_UCia').click()
#         browser.implicitly_wait(10)
 
#---------------------------------------------------------------------------------------

        print(f'-------------------------------{j}번째-------------------------------')

        # 카페 주소 저장
        cafe_addr = WebDriverWait(browser,1000).until(EC.presence_of_element_located((By.XPATH,'/html/body/div[3]/div/div/div/div[5]/div/div[2]/div[1]/div/div[1]/div/a/span[1]')))

        cafe_address = cafe_addr.text


        # 이미지 가져오기
        background_image_style = browser.find_element(By.XPATH,'/html/body/div[3]/div/div/div/div[1]/div/div[1]/div/a/div').get_attribute('style')
        background_image_url = re.search(r'url\("([^"]+)"\)', background_image_style).group(1)

        # 위에서 버튼을 누르지 않으니 스크롤이 제대로 돌아가지 않음 entryIframe 내의 element 클릭 그래야 스크롤이 내려감
        browser.find_element(By.ID, '_title').click()
        for scroll in range(0,10):
            browser.find_element(By.TAG_NAME, 'body').send_keys(Keys.PAGE_DOWN)

        resp = browser.page_source
        soup = BeautifulSoup(resp, 'html.parser')

        cafe_menu_ls = []
        cafe_menu = soup.select('div.MN48z > div.erVoL > div.MENyI > span.VQvNX')
        if len(cafe_menu) == 0:
            cafe_menu = soup.select('div.YzCTi > div.Fi0vA > div.RhpMT > a.place_bluelink.ihmWt')
            for menu in cafe_menu:
                cafe_menu_ls.append(menu.text)
        else:
            for menu in cafe_menu:
                cafe_menu_ls.append(menu.text)

        # 리뷰 높은순 3개 가져오기.
        cafe_review = []
        try:
            for k in range(1,4):
                cafe_review.append(soup.select(f'div.place_section_content > div.iqjjt > a > ul > li:nth-child({k}) > div.CsBE9 > span.nWiXa')[0].text[1:-1])
        except:
            cafe_review.append('리뷰 참여자가 10명이 되지 않습니다.')

        # 스위치 변경
        switch_frame("searchIframe")

        resp = browser.page_source
        soup = BeautifulSoup(resp, 'lxml')

# --------------------------------------------------------평점이 없는 매장이 훨신 많아 평점 제거--------------------------------------------------
        # 딕셔너리에 담을 변수들 생성
        cafe_name = soup.select(f'#_pcmap_list_scroll_container > ul > li:nth-child({j}) > div.CHC5F > a.tzwk0 > div > div > span.TYaxT')[0].text
        cafe_classification = soup.select(f'#_pcmap_list_scroll_container > ul > li:nth-child({j}) > div.CHC5F > a.tzwk0 > div > div > span.KCMnt')[0].text
        cafe_review_count = soup.select(f'#_pcmap_list_scroll_container > ul > li:nth-child({j}) > div.CHC5F > div > div > span:nth-child(2)')[0].text
        if cafe_review_count[:2] == '리뷰':
            cafe_review_count = cafe_review_count[3:]
        elif cafe_review_count[:2] != '리뷰':
            try:
                cafe_review_count = soup.select(f'#_pcmap_list_scroll_container > ul > li:nth-child({j}) > div.CHC5F > div > div > span:nth-child(3)')[0].text[3:]
            except:

                cafe_review_count = soup.select(f'#_pcmap_list_scroll_container > ul > li:nth-child({j}) > div.CHC5F > div > div > span:nth-child(1)')[0].text[3:]
        else :
            cafe_review_count = '0'

        cafe_name_list.append(cafe_name)
        cafe_classification_list.append(cafe_classification)
        cafe_review_count_list.append(cafe_review_count)
        cafe_address_list.append(cafe_address)
        cafe_review_list.append(cafe_review)
        background_image_url_list.append(background_image_url)
        cafe_menu_ls_list.append(cafe_menu_ls)

        dict_temp = {
            'name':cafe_name_list,
            'class':cafe_classification_list,
            'review_count':cafe_review_count_list,
            'address':cafe_address_list,
            'review_top3':cafe_review_list,
            'background_image_url':background_image_url_list,
            'cafe_menu':cafe_menu_ls_list        
        }    
       
    next_btn[-1].click() # 다음 버튼 클릭
    time.sleep(5)
    # 버튼이 눌리지 않으면 종료
    if not next_btn[-1].is_enabled():
        break

browser.implicitly_wait(10)
browser.close()

df = pd.DataFrame(dict_temp)
df.to_csv('C:/Users/user/crawling_project/crawling_강남구.csv',index = False, encoding='utf-8-sig')

 

이제 불러들인 값들을 딕셔너리에 저장한 후 csv파일에 저장을 하도록 하였습니다.

 

이 과정을 진행하는중 인터넷이 느려서 발생하는 오류가 상당히 많이 발생하여,

코드를 한번 돌리면 한 자치구가 끝나면 다음구가 자동으로 넘어가면서 파일을 저장하게 만들었었는데, 이를 한개의 자치구씩 직접 코드를 실행하였습니다.

 

중간에 주소를 받아오는 방식을 수정해서 주소를 가져오려면 이 전 코드에서는 

이 부분을 눌러 밑에 도로명 주소를 복사하였는데, 이럴 필요없이 그냥 보이는 주소를 복사하여도 문제가 없다는걸 깨닫고

        # 버튼이 누를 필요 없어서 제거
#---------------------------------------------------------------------------------------
            # browser.find_element(By.CLASS_NAME,'_UCia').click()
            # browser.implicitly_wait(10)

저걸 클릭하는 이부분을 제거해주었습니다.

 

        # 해당하는 카페에 정보를 알기위해 그 카페를 클릭해줌
        # 구마다 /html/body/div[3]/div/div[3]....../sapn[1] or /html/body/div[3]/div/div[4]....../sapn[1] 가 있음
        try:
            browser.find_element(By.XPATH, f'/html/body/div[3]/div/div[3]/div[1]/ul/li[{j}]/div[1]/a/div/div/span[1]').click()
        except NoSuchElementException:
            browser.find_element(By.XPATH, f'/html/body/div[3]/div/div[4]/div[1]/ul/li[{j}]/div[1]/a/div/div/span[1]').click()
        finally:
            # 카페정보를 받아오는 시간 충분하게 받아줌 인터넷이 느려 충분히 시간 줘야됌
            time.sleep(3)

거의 모든 카페들은 div[3]으로 되어있지만, 가끔 div[4] 로 되어있는 부분이 있어 오류가 발생하였는데,

이렇게 예외 처리를 해줌으로써 해결하였습니다.

 

 

 

NoSuchElementException이란 그 element를 찾지 못하였을때 발생하는 오류입니다.

 # 카페 주소 저장
        cafe_addr = WebDriverWait(browser,1000).until(EC.presence_of_element_located((By.XPATH,'/html/body/div[3]/div/div/div/div[5]/div/div[2]/div[1]/div/div[1]/div/a/span[1]')))

카페 주소를 저장하는 이부분은  WebDriverWait(browser,1000) 브라우저가 열리지 않을경우 1000초동안 기다리라고 설정해놨습니다.

인터넷이 느려 화면을 보여주는속도가 느릴때,

아직 화면이 나오지 않았는데 그 화면에 해당하는 요소를 찾을때 발생하는 오류를 없애주기 위해서입니다.

.until(EC.presence_of_element_located 이것으로 그 요소를 찾게되면 1000초를 기다리지 않고 넘어가도록  설정해주었습니다.

 

나머지 코드는 짜는데 오류는 없었고

 

마지막으로 csv파일로 저장할때

df = pd.DataFrame(dict_temp)
df.to_csv('C:/Users/user/crawling_project/crawling_강남구.csv',index = False, encoding='utf-8-sig')

pandas를 활용하여 csv파일로 저장해주었습니다.

 

pandas란 파이썬의 데이터 분석 라이브러리입니다.

수치형 테이블과 시계열 데이터를 조작하고 운영하기 위한 데이터를 제공합니다.

 

저는 https://dandyrilla.github.io/2017-08-12/pandas-10min/

 

판다스(pandas) 기본 사용법 익히기

데이터 분석을 위한 파이썬 라이브러리인 판다스(pandas) 의 기본 사용법을 소개해 놓은 ‘10 Minutes to pandas’ 를 번역해 놓은 글입니다. pandas 의 기본 사용법을 익히시려는 분들에게 실습을 천천히

dandyrilla.github.io

이분의 블로그를 참고하여 사용하였습니다.

 


 

3일차는 여기까지 마무리하였고,

4일차에서는 추가적으로 django를 이용하여 간단하게 웹페이지를 구현해보도록 하겠습니다.

 

728x90