G2G 페이지를 크롤링하면서 원하는 html 구문이 requests 방식으로는 스크래핑이 안되서 이유를 찾아봤다. g2g 페이지는 javascript를 활용, 동적 페이지로 구성된 녀석이라 페이지를 열어서 활성된 html 구문을 가져와야 원하는 정보를 받아올 수 있는 경우였다.
Selenium
직접 페이지를 열기 위한 라이브러리 selenium의 파이썬(or 아나콘다) 설치를 진행한다.
1 2
$ pip install selenium $ conda install selenium
나의 경우 venv 환경을 이용할 예정이라 venv 경로에서 bash를 이용해 pip install 해줬다.
브라우저 webdriver 설치
selenium을 받으면 끝이 아니라 selenium으로 html 구문을 받아오기 위해 webdriver를 받아와서 지정해줘야한다.
특정 조건이 만족할 때까지 지정된 시간 동안 대기하고, timeout된 경우 TimeoutException이 발생한다.
expected_conditions 함수의 조건이 만족할 때까지 기다리는 코드를 사용할 수 있다.
1 2 3 4 5 6 7 8 9
from selenium import webdriver from selenium.webdriver.common.by import By from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC
wait = WebDriverWait(driver, 30) # 특정 요소를 찾을 수 있을 때까지 element = EC.presence_of_element_located((By.CLASS_NAME , "offers-bottom-attributes.offer__content-lower-items")) wait.until(element)
동적 페이지의 경우 하나의 URL에서 구조가 바뀌는 경우도 있다.
이런 경우 클릭이 가능할 때까지 기다리는 함수를 사용할 수 있다.
1 2
# 클릭이 가능할 때까지 element = EC.element_to_be_clickable((By.XPATH, '//a[@href="#!-1"]'))
아래의 조건들을 활용할 수 있다.
title_is
title_contains
presence_of_element_located
visibility_of_element_located
visibility_of
presence_of_all_elements_located
text_to_be_present_in_element
text_to_be_present_in_element_value
frame_to_be_available_and_switch_to_it
invisibility_of_element_located
element_to_be_clickable
staleness_of
element_to_be_selected
element_located_to_be_selected
element_selection_state_to_be
element_located_selection_state_to_be
alert_is_present
try ~ finally 구문을 활용하면 true인 경우 다음 단계로 넘어가는 코드를 활용할 수 있다.
라이브러리를 불러오다보면 의존성 패키지의 과거 버전이 필요할 때가 있다. 모든 패키지에 대응할 수는 없으니 파이썬이나 해당 패키지를 재설치 혹은 Downgrade를 진행해야 할 것이다. 위 문제를 해결할 수 있는 방법이 여러 파이썬을 생성해 각각 환경에 맞는 패키지를 설치해 따로 불러오는 방법으로 가상환경이라 한다.
가상환경 생성
설정에서 생성하기
프로젝트를 생성하고 코드 작업을 진행하다 버전 에러가 났을 때,
Files - Settings - Project:
- Python Interpreter 에서 Add... 를 눌러 추가할 수 있다.
가장 기본적인 Venv를 설치할 수 있다. anaconda 설치가 됐다면 conda 환경도 추가할 수 있지만, 기본적인 파이썬 Venv 설치를 진행할 예정. 파이썬 exe 경로와 가상환경 dir 경로를 잘 확인해주자
프로젝트와 같이 생성하기
보통의 경우는 파이참에서 프로젝트를 생성하며 가상환경 설치를 진행할 것이다.
터미널에서 생성하기
파이참같은 개발환경을 사용하지 못하고 리눅스 등 환경의 터미널에서 생성해야할 경우는 venv — 가상 환경 생성를 참조한다.
패키지 설치
파이썬 가상환경을 생성했다면 사용할 패키지를 설치하면 된다.
정상적으로 설치했다면 문제 없겠지만 어느 파이썬을 참조하고 있는지 터미널에서 확인해보자.
1 2 3 4
# window $where python # mac, linux .ect $which python
가상환경을 참조하지 못하고 있다면 아래 명령어로 (venv) 강제 진입해보자.
1 2 3 4
# window $source ./venv/Scripts/activate # mac, linux .ect $source ./venv/bin/activate
설정 interpreter에서
파이참 같은 경우엔 Python Interpreter - install 버튼을 선택해 설치할 패키지와 버전을 선택할 수 있다.
requirements.txt
버전을 일일히 기억하고 있기도 뭐하고 설치할 패키지가 많다면 하나하나 설치하는 것도 일이다.
## Q7(Program_Language): 칼럼번호 8~20 - others df21_Jp_PL = pd.DataFrame() df21_Jp_PL['Program_Language'] = [df21_Jp[col][1:].value_counts().index[0] for col in df21_Jp.columns[7:20]] df21_Jp_PL['counts'] = [df21_Jp[col][1:].value_counts().values[0] for col in df21_Jp.columns[7:20]]
## Q7(Program_Language): 칼럼번호 8~20 - others df21_Ch_PL = pd.DataFrame() df21_Ch_PL['Program_Language'] = [df21_Ch[col][1:].value_counts() .index[0] for col in df21_Ch.columns[7:20]] df21_Ch_PL['counts'] = [df21_Ch[col][1:].value_counts() .values[0] for col in df21_Ch.columns[7:20]]
## 제거된 나라 칼럼과 value를 각각 삽입 및 통합 df21_Jp_PL.insert(0, 'Country', 'Japan') df21_Ch_PL.insert(0, 'Country', 'China')
---------------------------------------------------------------------------
IndexError Traceback (most recent call last)
<ipython-input-5-89d86f0a4d0b> in <module>
11 ## Q7(Program_Language): 칼럼번호 8~20 - others
12 df21_Ch_PL = pd.DataFrame()
---> 13 df21_Ch_PL['Program_Language'] = [df21_Ch[col][1:].value_counts() .index[0] for col in df21_Ch.columns[7:20]]
14 df21_Ch_PL['counts'] = [df21_Ch[col][1:].value_counts() .values[0] for col in df21_Ch.columns[7:20]]
15
<ipython-input-5-89d86f0a4d0b> in <listcomp>(.0)
11 ## Q7(Program_Language): 칼럼번호 8~20 - others
12 df21_Ch_PL = pd.DataFrame()
---> 13 df21_Ch_PL['Program_Language'] = [df21_Ch[col][1:].value_counts() .index[0] for col in df21_Ch.columns[7:20]]
14 df21_Ch_PL['counts'] = [df21_Ch[col][1:].value_counts() .values[0] for col in df21_Ch.columns[7:20]]
15
E:\Sadness\anaconda3\lib\site-packages\pandas\core\indexes\base.py in __getitem__(self, key)
4295 if is_scalar(key):
4296 key = com.cast_scalar_indexer(key, warn_float=True)
-> 4297 return getitem(key)
4298
4299 if isinstance(key, slice):
IndexError: index 0 is out of bounds for axis 0 with size 0
결측 column 식별 및 제거
*IndexError: index 0 is out of bounds for axis 0 with size 0 오류가 식별됐다. 아마 China, Program_Language의 특정 응답이 없어서 발생한거같다.
## Q7(Program_Language): 칼럼번호 8~20 - others df21_Jp_PL = pd.DataFrame() df21_Jp_PL['Program_Language'] = [df21_Jp[col][1:].value_counts().index[0] for col in df21_Jp.columns[7:19]] df21_Jp_PL['counts'] = [df21_Jp[col][1:].value_counts().values[0] for col in df21_Jp.columns[7:19]]
## Q7(Program_Language): 칼럼번호 8~20 - others - Q7_Part12(None) df21_Ch_PL = pd.DataFrame() df21_Ch_PL['Program_Language'] = [df21_Ch_rmQ07P12[col][1:].value_counts() .index[0] for col in df21_Ch_rmQ07P12.columns[7:18]] df21_Ch_PL['counts'] = [df21_Ch_rmQ07P12[col][1:].value_counts() .values[0] for col in df21_Ch_rmQ07P12.columns[7:18]]
## 제거된 나라 칼럼과 value를 각각 삽입 및 통합 df21_Jp_PL.insert(0, 'Country', 'Japan') df21_Ch_PL.insert(0, 'Country', 'China')
## Q7(Program_Language): 칼럼번호 8~20 - others df21_Jp_PL = pd.DataFrame() df21_Jp_PL['Program_Language'] = [df21_Jp[col][1:].value_counts().index[0] for col in df21_Jp.columns[7:19]] df21_Jp_PL['counts'] = [df21_Jp[col][1:].value_counts().values[0] for col in df21_Jp.columns[7:19]]
## Q7(Program_Language): 칼럼번호 8~20 - others - Q7_Part12(None) df21_Ch_PL = pd.DataFrame() df21_Ch_PL['Program_Language'] = [df21_Ch_rmQ07P12[col][1:].value_counts() .index[0] for col in df21_Ch_rmQ07P12.columns[7:18]] df21_Ch_PL['counts'] = [df21_Ch_rmQ07P12[col][1:].value_counts() .values[0] for col in df21_Ch_rmQ07P12.columns[7:18]]
## 제거된 나라 칼럼과 value를 각각 삽입 및 통합 df21_Jp_PL.insert(0, 'Country', 'Japan') df21_Ch_PL.insert(0, 'Country', 'China')
## Q18(Program_Language): 칼럼번호 83~95 - others & other(text) df19_Jp_PL = pd.DataFrame() df19_Jp_PL['Program_Language'] = [df19_Jp[col][1:].value_counts().index[0] for col in df19_Jp.columns[82:93]] df19_Jp_PL['counts'] = [df19_Jp[col][1:].value_counts().values[0] for col in df19_Jp.columns[82:93]]
## 2019 China Q18_Part11(None) 결측값 제거 df19_Ch_rmQ18P11 = df19_Ch.drop(['Q18_Part_11'], axis='columns')
## Q18(Program_Language): 칼럼번호 83~95 - others & other(text) - Q18_Part11(None) df19_Ch_PL = pd.DataFrame() df19_Ch_PL['Program_Language'] = [df19_Ch_rmQ18P11[col][1:].value_counts() .index[0] for col in df19_Ch_rmQ18P11.columns[82:92]] df19_Ch_PL['counts'] = [df19_Ch_rmQ18P11[col][1:].value_counts() .values[0] for col in df19_Ch_rmQ18P11.columns[82:92]]