본문 바로가기
TOP
데이터 스킬업/웹크롤링

[ep01:웹크롤링] #18 오류 무시/예외 처리(Try/Except) with 파이썬

by 티챠림 2021. 8. 12.

[왕초보 웹크롤링 무작정 따라하기] 웹크롤링, 셀레니움, 오류 무시, 오류 예외 만들기, Try, Except, Exception.


업무지옥을 탈출한 건에 대하여(feat.업무자동화) 

#18 오류 무시/예외 처리(Try/Except)


Try / Except 사용하기 (바로가기 Click)


인스타그램(동적페이지)를 크롤링하기 위해 필수적인 코드들을 익혔다. 만들어둔 동작 자동화 코드에 .find 와 .text 메소드를 사용해 텍스트를 추출하면 된다. 브라우저 개발자 도구에서 경로를 추출하고 추출할 데이터에 맞게 수정해 코드에 넣어주었다. (브라우저 개발자도구에서 경로 파악하는 과정 생략)

 

#라이브러리 활성화
import time
from selenium import webdriver
from selenium.webdriver import ActionChains
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC

#웹 열
dr = webdriver.Chrome()
wait = WebDriverWait(dr, 5)
dr.set_window_size(414, 800)
dr.get('https://www.instagram.com/')
dr.implicitly_wait(2)


#로그인
id_box = dr.find_element_by_css_selector("#loginForm > div > div:nth-child(1) > div > label > input")
password_box = dr.find_element_by_css_selector("#loginForm > div > div:nth-child(2) > div > label > input")
login_button = dr.find_element_by_css_selector('#loginForm > div > div:nth-child(3) > button')

act = ActionChains(dr)

act.send_keys_to_element(id_box, '아이디').send_keys_to_element(password_box, '비밀번호').click(
    login_button).perform()
wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, '#react-root > div > div > section > main > div > div > div > div > button')))

# 3.내 피드로 이동하기
# 3-1. 팝업 클릭하기
first_popup = dr.find_element_by_css_selector(
    '#react-root > div > div > section > main > div > div > div > div > button')
first_popup.click()
wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, 'div.RnEpo.Yx5HN > div > div > div > div.mt3GC > button.aOOlW.HoLwm')))

second_popup = dr.find_element_by_css_selector('div.RnEpo.Yx5HN > div > div > div > div.mt3GC > button.aOOlW.HoLwm')
second_popup.click()
wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, 'section > nav > div._8MQSO.Cx7Bp > div > div > div.ctQZg > div > div:nth-child(6) > span')))


#프로필 사진 클릭하기
my_photo = dr.find_element_by_css_selector(
    'section > nav > div._8MQSO.Cx7Bp > div > div > div.ctQZg > div > div:nth-child(6) > span')
my_photo.click()
wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, 'section > nav > div._8MQSO.Cx7Bp > div > div > div.ctQZg > div > div:nth-child(6) > div.poA5q > div.uo5MA._2ciX.tWgj8.XWrBI > div._01UL2 > a:nth-child(1) > div')))

#프로필 버튼 클릭하기
profile = dr.find_element_by_css_selector(
    'section > nav > div._8MQSO.Cx7Bp > div > div > div.ctQZg > div > div:nth-child(6) > div.poA5q > div.uo5MA._2ciX.tWgj8.XWrBI > div._01UL2 > a:nth-child(1) > div')
profile.click()
wait.until(EC.presence_of_element_located((By.XPATH, '//*[@id="react-root"]/div/div/section/main/div/header/div/div/div/button/img')))


dr.find_element_by_xpath('//*[@id="react-root"]/div/div/section/main/div/div[4]/article/div/div/div[5]/div[1]').location

#상대좌표 사용을 위한 스크롤 위치 지정
dr.execute_script("window.scrollTo(0, 479)")
scroll_location = dr.execute_script("return window.pageYOffset")


#동작/스크롤/크롤링 코드
while True:
    # 로고 좌표 추출
    standard_logo = dr.find_element_by_xpath('//section/nav/div[2]/div/div/div[1]/a/div/div')
    for line in range(0, 4):
        y = 110 + 175 * line
        for box in range(0, 3):
            x = 175 * box

            #피드 마우스 오버
            act = ActionChains(dr)
            act.move_to_element(standard_logo).move_by_offset(x, y).perform()

            #좋아요/댓글 수 크롤링
            data = dr.find_elements_by_xpath('//article//ul/li')
            likes = data[0].text
            comments = data[1].text
            print(likes)
            print(comments)

    # 649 씩 스크롤을 내림
    dr.execute_script("window.scrollTo(0,{})".format(scroll_location + 648))

    # 전체 스크롤이 늘어날 때까지 대기
    dr.implicitly_wait(2)

    scroll_height = dr.execute_script("return window.pageYOffset")

    # 현재 스크롤 위치와 전체 스크롤 높이가 같으면(더 이상 스크롤이 늘어나지 않으면) 종료
    if scroll_location == scroll_height:
        break

    # 같지 않으면 다음 조건 실행
    else:
        # 스크롤 이동 후 스크롤 위치값을 수정
        scroll_location = dr.execute_script("return window.pageYOffset")
 

[ep01:웹크롤링] #14 셀레니움 크롤링 반복문(for문 & range) with 파이썬

[왕초보 웹크롤링 무작정 따라하기] 웹크롤링, 셀레니움, for 반복문, range. 업무지옥을 탈출한 건에 대하여(feat.업무자동화) #14 셀레니움 크롤링 반복문(for문 & range) for반복문과 range()활용법 (

charimlab.tistory.com

▲ xpath 경로 작성 및 텍스트 추출 관련 글

 


 

코드를 실행하니 좋아요와 댓글 순으로 개수가 프린트된다. 그런데 마지막에 오류가 발생한다? 

 

 


 

 

문제의 원인은 인스타그램 피드에서 찾을 수 있었다. 코드 상 데이터를 추출해야 하지만 해당 위치의 피드가 비어있어 데이터를 추출하지 못하여 오류가 발생하는 것이다. 원래의 계획대로라면 더 이상 데이터를 추출할 피드가 없다면, 코드를 종료하게 만들어야 한다. 반대로 생각하면, 피드가 없어 발생하는 오류를 코드 종료로 바꿔주면 되는 것이다. 오류를 임의로 설정하는 코드는 TryExcept로 만들 수 있다.

 

 


Try / Except 사용하기


Try와 Except문을 사용하면 오류가 발생하지 않았을 때는 코드가 순서대로 실행되지만, 오류가 발생했을 때는 except: 뒤에 오는 코드를 실행시킨다. 원래라면 오류 발생 시, 코드 실행이 멈추게 되지만 이를 통해 오류를 무시하거나 다른 결과로 출력할 수 있다.

 

code A
     try:
          code B
          code X
     except:
          code Y

#code A를 수행하고 cod B를 실행한다. 오류가 없다면 code X를 실행하고 오류가 발생하면 code Y를 실행한다.
code B 오류 없음  다음 코드 실행  code X
오류 발생 except: 뒤의 코드 실행  code Y

 


 

이제 이를 코드에 적용시키면 된다. try: 뒤에는 원래 코드를 넣어주고, except: 다음에는 '데이터 수집이 종료되었습니다'가 출력하고 코드를 종료하게 만들어 보자. 그런데 try:를 어디에 넣어줘야 하는 걸까? 우선 데이터를 추출할 때 오류가 발생하니 데이터 추출코드 바로 직전에 넣어보았다.

 

#크롤링 반복코드
while True:
    # 로고 좌표 추출
    standard_logo = dr.find_element_by_xpath('//section/nav/div[2]/div/div/div[1]/a/div/div')
    for line in range(0, 4):
        y = 110 + 175 * line  # 열 번호를 기준으로 175씩 y좌표를 더함
        for box in range(0, 3):
            x = 175 * box  # 박스 번호를 기준으로 175씩 y좌표를 더함

            #피드 마우스 오버
            act = ActionChains(dr)
            act.move_to_element(standard_logo).move_by_offset(x, y).perform()

	#오류가 없을 때, 데이터 추출 실행
            try:
                #좋아요/댓글 수 크롤링
                data = dr.find_elements_by_xpath('//article//ul/li')
                likes = data[0].text
                comments = data[1].text
                print(likes)
                print(comments)
			
            #오류 발생시 메세지 출력 및 코드 종료
            except:
                print('데이터 수집이 종료되었습니다.')
                break


    dr.execute_script("window.scrollTo(0,{})".format(scroll_location + 648))
    dr.implicitly_wait(2)
    scroll_height = dr.execute_script("return window.pageYOffset")
    
    if scroll_location == scroll_height:
        break
    else:
        scroll_location = dr.execute_script("return window.pageYOffset")

 

실행시켜보니 뭔가가 잘못됐다. 종료되야할 시점에서 데이터를 한번 더 추출한다. 확인해보니 데이터 추출 코드를 끝내고 반복문 처음부터 다시 시작된 것이다. 오류 발생 시 for문 전체가 종료되게 try의 위치를 바꿔보자. for문 직전으로 try를 옮기고 except 위치도 수정해 주었다.

 

#크롤링 반복코드
while True:
    # 로고 좌표 추출
    standard_logo = dr.find_element_by_xpath('//section/nav/div[2]/div/div/div[1]/a/div/div')
    
    #오류가 없을 때, 데이터 추출 실행
    try:
        for line in range(0, 4):
            y = 110 + 175 * line  # 열 번호를 기준으로 175씩 y좌표를 더함
            for box in range(0, 3):
                x = 175 * box  # 박스 번호를 기준으로 175씩 y좌표를 더함

                #피드 마우스 오버
                act = ActionChains(dr)
                act.move_to_element(standard_logo).move_by_offset(x, y).perform()


                #좋아요/댓글 수 크롤링
                data = dr.find_elements_by_xpath('//article//ul/li')
                likes = data[0].text
                comments = data[1].text
                print(likes)
                print(comments)

        # 649 씩 스크롤을 내림
        dr.execute_script("window.scrollTo(0,{})".format(scroll_location + 648))

        # 전체 스크롤이 늘어날 때까지 대기
        dr.implicitly_wait(2)

        scroll_height = dr.execute_script("return window.pageYOffset")

        # 현재 스크롤 위치와 전체 스크롤 높이가 같으면(더 이상 스크롤이 늘어나지 않으면) 종료
        if scroll_location == scroll_height:
            break

        # 같지 않으면 다음 조건 실행
        else:
            # 스크롤 이동 후 스크롤 위치값을 수정
            scroll_location = dr.execute_script("return window.pageYOffset")
    
    #오류 발생시 메세지 출력 및 코드 종료
    except:
        print('데이터 수집이 종료되었습니다.')
        break

 

수정된 코드를 실행시키니, 이번엔 반복 없이 정상적으로 데이터들이 추출되는 것을 확인할 수 있다. 이로써 동적 페이지 데이터 추출도 성공! 

 

 

 


[system] '동적 페이지 크롤링'을 성공하였습니다. 

'스킬: 오류 처리' 획득

 

승급자격을 획득하셨습니다.

현재 클래스 : 2 class

 

[보유 스킬]

오류처리, 동작제어, 자바스크립트 활용,

주문가속, 주문중첩, 주문연계, 

데이터시각화, 데이터필터링, 웹데이터 소환

 

[보유 칭호]

브라우저를 제어하는 자, 웹데이터를 불러내는 자


댓글