지난 22년 7월 경,
파이썬과 Selenium 패키지 및 웹드라이버를 이용해
네이버 카페에 있는 모든 게시글의 모든 파일을 다운받는 명령문을 작성했습니다.
모두 다운받아야 하는데 손으로 하기에는 시간이 걸릴 것 같아서 만들기 시작한 명령문인데
소요된 시간을 측정해보니 그냥 일일히 다운받는 쪽이 더 빠르긴 했을것 같습니다 :) 만
만들면서 매우 재미도 있었고 셀레니움의 여러 툴들을 배울 수 있었습니다.
모르는 게 많아서 웹 서치를 매우 많이 해서 다 만들고 나면 나도 꼭 명령문 업로드를 해야겠다고 생각했으나
약사 고시 준비로 미루다가 6개월이 지나 업로드 합니다.
#%%
# import pyperclip
import time
from selenium import webdriver
from selenium.webdriver.chrome.service import Service as ChromeService
from selenium.webdriver.chrome.options import Options
from webdriver_manager.chrome import ChromeDriverManager
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.action_chains import ActionChains
op = webdriver.ChromeOptions()
네이버 카페의 게시판별 CSS ID는는 f"#menuLink{dic[게시판]}"과 같이 지정되기 때문에
아래와 같이 딕셔너리를 작성했습니다.
게시판 개수가 몇 개 안되어 이렇게 손으로 쳤습니다.
dic = {'게시판7':'7', '게시판1' : '1', '게시판3':'3', '게시판4':'4', '게시판5':'5', '게시판6':'6', '게시판8':'8', '게시판9':'9', '게시판10':'10', '게시판11':'11', '게시판12':'12', '게시판13':'13', '게시판14':'14', '게시판15':'15', '게시판16':'16'}
게시판목록 = list(dic.keys())
카페에 있는 여러개의 게시판 목록을 for 문으로 구성하고
하나씩 방문하면서 그 안의 모든 게시글을 다시 다운받을 예정입니다.
게시판마다 각기 다른 디렉토리에 저장되게 하기 위해
webdriver.ChromeOptions() 명령어로 디렉토리를 지정합니다.
이후 웹드라이버를 새창으로 하나 띄우도록 합니다.
for 게시판 in 게시판목록 :
#크롬에서 다운받고 싶은 위치 지정하기
op.add_experimental_option('prefs', {'download.default_directory':r'/Users/다운하고 싶은 디렉토리 입력/{}'.format(과목)})
def chromeWebdriver():
try :
chrome_service = ChromeService(ChromeDriverManager().install())
driver = webdriver.Chrome(service = chrome_service, options=op)
except Exception as e:
driver = None
print(e)
return driver
이후 다운받고 싶은 파일이 있는 카페의 주소로 이동합니다.
명령문을 잘라서 업로드하느라 잘 보이지는 않지만
아래부터 모든 코드는 for문 안에서 작동하고 있습니다.
driver = chromeWebdriver()
driver.get("https://cafe.naver.com/다운받고 싶은 카페 주소")
driver.execute_script 명령을 이용하여
각 Element의 고유한 name으로 네이버의 아이디 입력 칸, 비밀번호 입력 칸을 지정하고
그 자리에 미리 변수로 텍스트를 입력하도록 합니다.
인터넷 속도를 고려하여 사이에 time.sleep()을 추가합니다.
#reCAPTCHA 없이 로그인, script로 아이디와 비밀번호 입력하기
my_id = "네이버 아이디"
my_pw = "네이버 비밀번호"
driver.execute_script("document.getElementsByName('id')[0].value = '" + my_id + "'")
time.sleep(0.5)
driver.execute_script("document.getElementsByName('pw')[0].value = '" + my_pw + "'")
time.sleep(0.5)
Element의 ID로 로그인 버튼을 지정해 클릭합니다.
#로그인 버튼 클릭
button = driver.find_element(By.ID, "log.login")
button.click()
time.sleep(1)
제가 파일을 다운받고자 했던 게시판들은
드롭다운처럼 대분류에 가려져 있어서
드롭다운 버튼을 인식해 펼쳐주는 명령문을 넣었습니다.
드롭다운이 없는 카페는 생략해도 되는 부분입니다.
참고로 time.sleep()과 달리 driver.implictly_wait()은
직전 명령어로 인한 동작이 끝난 이후로 time.sleep()을 적용한다는 차이점이 있습니다.
#CSS_SELECTOR로 게시판 드롭다운 펼치기
dropdown = driver.find_element(By.CSS_SELECTOR, '#group2btn > a > img')
dropdown.click()
driver.implicitly_wait(3)
그다음 미리 만들어둔 딕셔너리를 이용해
게시판을 지정하여 진입합니다.
#게시판 지정 및 클릭
board = driver.find_element(By.CSS_SELECTOR, f"#menuLink{dic[게시판]}")
driver.implicitly_wait(3)
board.click()
그 다음 작성하면서 시간을 가장 많이 들였던 부분인데,
iframe을 찾고 지정하는 것입니다.
네이버에서 가장 유명한 중고나라 카페를 예로 들면
(예시를 위해 스크린샷만 찍고 여기서 파일을 다운받지 않았습니다.)
스크린 샷에서 그어놓은 노란 선 안은 iframe 이라는 프레임으로,
프레임 외 부분과 별도로 분리되어 있습니다.
즉 게시판을 선택해 진입한 이후에,
게시글 내부에서 첨부파일을 클릭하고 다운 받는 행동들은
iframe 안에서 이루어져야 한다는 뜻이었습니다.
저는 컴알못이므로,
어떻게 해야 이 내부의 요소를 지정해서 클릭을 할 수 있는지만 알아보았습니다.
아래는 사용자가 상호작용하고 싶은 영역이
어떤 아이프레임인지 찾는 명령문입니다.
#페이지 안의 모든 iframe 찾기
iframes = driver.find_elements(By.TAG_NAME, 'iframe')
# iframe ID로 특정이 안 될 때, 원하는 iframe 찾기
for i, iframe in enumerate(iframes):
try :
print('%d번째 iframe입니다' % i)
print(iframe)
driver.switch_to.frame(iframes[i])
print('%d번째 페이지로 전환합니다' % i)
print(driver.page_source)
driver.switch_to.default_content()
except Exception as e :
print('pass by except : iframe[%d]' % i)
print(e)
pass
제가 필요했던 frame은 7번째였나봅니다.
그래서 아래와 같이 게시글 내부의 원하는 프레임 영역으로 전환합니다.
#iframe 전환
driver.switch_to.frame(iframes[7])
driver.implicitly_wait(2)
우선 첫 번째 게시글로 진입합니다.
#첫 파일 다운로드
print(f'{과목} 파일 다운로드 시작')
WebDriverWait(driver, 10).until(EC.element_to_be_clickable((By.CSS_SELECTOR, "첫 게시글 선택자"))).click()
그 다음에는 다운로드하는 일련의 행위를 묶은 download() 함수 및
여러가지 요소를 정의했습니다.
다운받을 파일들의 목록을 = files
다운로드를 위해 클릭해야 할 버튼을 = 펼치기
다음 게시글로 넘어가기 위한 버튼을 = Next
각각 CSS 선택자로 정의했습니다.
게시글에 진입해서 다운로드 한 뒤, 뒤로가기 후 다음 게시글을 선택하는 방법도 있겠지만
네이버 카페 게시글 하단에 게시글 일부를 보여준 뒤, 다음 게시글로 넘어갈 수 있도록 만든 창을
Next로 지정하고 이용하기로 했습니다.
def download() :
files = driver.find_elements(By.CSS_SELECTOR, "li.AttachFileListItem")
펼치기 = driver.find_element(By.CSS_SELECTOR, "첨부파일 펼치기 선택자")
download = driver.find_element(By.CSS_SELECTOR, f"#app > div > div > div.ArticleContentBox > div.article_container > div.AttachFileList > div.attach_file > div > ul > li:nth-child({idx+1}) > div.file_download > a:nth-child(1)")
article = 2
Next = "#app > div > div > div.RelatedArticles > div.RelatedArticlesTabContainer > div > div.tab_content > div > ul > li:nth-child(4) > div.tit_area > a > span"
for idx in range(len(files)) :
ActionChains(driver).move_to_element(button).click(button).perform()
download = driver.find_element(By.CSS_SELECTOR, f"#app > div > div > div.ArticleContentBox > div.article_container > div.AttachFileList > div.attach_file > div > ul > li:nth-child({idx+1}) > div.file_download > a:nth-child(1)")
driver.implicitly_wait(10)
ActionChains(driver).move_to_element(download).click(download).perform()
driver.implicitly_wait(10)
time.sleep(2)
print(f'{게시판}의 {article-1}번째 게시글 중 {idx+1}번째 파일 저장 완료')
아래는 게시글의 순서에 따라 다음 게시글로 이동할 때 클릭해야 할 위치가 달라지는 점을 반영한 명령어입니다.
위에 노란색 박스 친 부분이 앞으로 클릭해야 할 부분이라면,
현재 머무르는 게시글은 3번째에 위치하게 됩니다.
그러다가 마지막 페이지 도달 전에는 Next 선택자 == 현재 머무르는 게시글 선택자 의 상황이 되어,
이 때는 Next 말고 리스트의 마지막 게시글로 이동하도록 합니다.
elif driver.find_element(By.CSS_SELECTOR, "li.list_item.selected > div > a > span" ) == driver.find_element(By.CSS_SELECTOR, Next):
print("마지막 페이지 도달 전")
time.sleep(1)
try :
download()
except Exception as e:
print(e)
print(f'{게시판}의 {article-1}번째 게시글 저장 실패')
driver.find_element(By.CSS_SELECTOR, "#app > div > div > div.RelatedArticles > div.RelatedArticlesTabContainer > div > div.tab_content > div > ul > li:nth-child(5) > div.tit_area > a > span").click()
사실 이러한 상황은 첫 번째, 두 번째 게시글일떄도 마찬가지입니다.
따라서
1) 게시판의 1,2번째 게시글에 머무르는 중
2) 게시판의 뒤에서 두번째 게시글에 머무르는 중
3) 그 외(중간 게시글이라 이동 보드?의 중간에 머무름)
으로 상황을 나누어서 작성했습니다.
또한 만약 파일 다운로드에 실패하면 원인과 함께 실패했음을 표시해서,
나중에 human intervention으로 직접 실패한 게시글을 찾아가 다운로드 할 수 있도록 했습니다.
while True :
if article == 2 or article == 3 :
try :
download()
except Exception as e:
print(e)
print(f'{게시판}의 {article-1}번째 게시글 저장 실패')
#다음 게시글 선택하고 넘어가기
driver.find_element(By.CSS_SELECTOR, f"#app > div > div > div.RelatedArticles > div.RelatedArticlesTabContainer > div > div.tab_content > div > ul > li:nth-child({article}) > div.tit_area > a > span").click()
time.sleep(1)
article += 1
elif driver.find_element(By.CSS_SELECTOR, "li.list_item.selected > div > a > span" ) == driver.find_element(By.CSS_SELECTOR, Next):
print("마지막 페이지 도달 전")
time.sleep(1)
try :
download()
except Exception as e:
print(e)
print(f'{게시판}의 {article-1}번째 게시글 저장 실패')
driver.find_element(By.CSS_SELECTOR, "#app > div > div > div.RelatedArticles > div.RelatedArticlesTabContainer > div > div.tab_content > div > ul > li:nth-child(5) > div.tit_area > a > span").click()
time.sleep(1)
article += 1
try :
download()
except Exception as e:
print(e)
print(f'{게시판}의 {article-1}번째 게시글 저장 실패')
print(f"{게시판} 게시판 다운로드 완료")
break
else :
try :
download()
except Exception as e:
print(e)
print(f'{게시판}의 {article-1}번째 게시글 저장 실패')
driver.find_element(By.CSS_SELECTOR, Next).click()
time.sleep(1.3)
article += 1
driver.quit()
하는 동작은 간단한데
왜 이렇게 했는지 설명하는 것은 어렵네요 참
제가 CSS나 HTML이 어떻게 작동하는지나 기본적인 명칭도 모르는 채
웹 드라이버를 어떻게든 쓰려고 도구적으로 사용해서 그런 것 같습니다.
어쨌거나 이리저리 검색해 가며 작성한 명령어가 결과적으로 잘 돌아가서
파이썬과 셀레니움을 이용해
네이버 카페 안의 모든 파일을 잘 다운로드 받을 수 있었습니다. :)
'내가 좋아하는 정보들 > 뒤쳐지지 말자, IT' 카테고리의 다른 글
[DataFrame/Matplotlib] 미국 금리에 따른 국내 상장 채권 ETF의 가격 추이 알아보기, matplotlib 알아보기 (1) | 2022.08.31 |
---|---|
맥북에서 한컴 오피스 문서(hwp) 편집하기 / 한컴오피스 웹버전 (6) | 2022.01.15 |
크롬에서 원하는 색상으로 커스텀 다크모드 설정하기, 그레이 모드! (0) | 2022.01.02 |
맥OS 한컴오피스 한글 뷰어에서 hwp 문서를 pdf로 저장하기 (0) | 2021.12.21 |
MAC에서 pip 명령어 안 될 때, pip 설치 (0) | 2021.09.08 |
댓글