-
인프런 동영상 웹 스크래핑하기 - 마치며(최종 코드)개발/웹크롤링 2022. 1. 16. 15:34
지금까지의 과정이 인프런 동영상을 스크래핑할 일이 생겨서 파이썬으로 어떻게 활용하면 할 수 있을까 3일 정도를 궁리하다가 나온 결과물입니다. 많은 시행착오가 있었고, 어쩌면 다른 사람들도 이런 것이 필요하지 않나 싶어서 이렇게 블로그의 첫 포스팅으로 쓰게 되었습니다. 사실 지금 군대에 와 있기도 하고, 대학교 1학년밖에 안 다닌 제가 알려드릴 게 많지는 않지만, 조금이나마 도움드리고 싶어서 글을 올리고자 결심했던 것 같네요.
제가 코드 전문을 올려드리지만, 너무 무턱대고 사용하지 않았으면 좋겠습니다. 인프런을 좋아하는 사람으로서 그 가치를 훼손하지 않았으면 합니다. 다운로드 받은 영상을 무단 배포하거나 상업적으로 이용하는 순간 범죄를 저지르는 거니까요, 참고해주시기 바랍니다.
다음은 제가 짠 코드입니다.
from seleniumwire import webdriver from selenium.webdriver.common.by import By from selenium.common import exceptions from getpass import getpass import requests import os import time DEST_PATH = r'c:\src\inflearn' page_url = 'https://www.inflearn.com/' os_name_inhibit = ['\\', '/', ':', '*', '?', '"', '<', '>', '|'] def clear_line(): print(f'{" ":>100}', end='\r') # 경로나 파일 명에서 쓸 수 없는 문자들을 삭제하기 def trim_path(name: str): for inhibit in os_name_inhibit: name = name.replace(inhibit, '') return name def make_dest_path(dest): pass class VideoCrawler: def __init__(self): self._driver = webdriver.Chrome() def login(self): login_id = input('아이디를 입력하세요 : ') pw = getpass('비밀번호를 입력하세요 : ') print('로그인 중입니다... 잠시만 기다려주세요', end='\r') self._driver.get(page_url) self._driver.find_elements(By.CLASS_NAME, "signin")[1].click() # id 입력 id_input = self._driver.find_element(By.CLASS_NAME, 'form__input--email') id_input.send_keys(login_id) # pwd 입력 pw_input = self._driver.find_element(By.CLASS_NAME, "e-sign-in-input") pw_input.send_keys(pw) time.sleep(1) # 로그인 버튼 클릭 submit_btn = self._driver.find_element(By.CLASS_NAME, 'e-sign-in') submit_btn.click() clear_line() print('로그인되었습니다.') time.sleep(1) self._driver.get(page_url + 'my-courses') def get_video_from_current_page(self): return self.get_video_from_url(self._driver.current_url) def get_videos_from_current_lecture(self): return self.get_all_video_from_lecture(self._driver.current_url) # start, end는 시작과 끝 지점의 인덱스 def get_all_video_from_lecture(self, url, start=0, end=4321): if self._driver.current_url != url: self._driver.get(url) try: unit_urls = [unit.get_attribute('href') for unit in self._driver.find_elements('xpath', "//a[@class='unit_item']")] except exceptions.NoSuchElementException: print('강의 메인화면으로 이동해주셔야 다운로드가 가능합니다.') return None if start > end: raise ValueError('start value never greater than end') if end >= len(unit_urls): end = len(unit_urls) - 1 size = end - start + 1 for idx, unit_url in enumerate(unit_urls): if start > idx or idx > end: continue print(f'전체 강의 다운로드 {size} 중 {idx + 1}...') self.get_video_from_url(unit_url) print('강좌 다운로드가 모두 완료되었습니다.') def get_video_from_url(self, url): # requests 목록 초기화 del self._driver.requests if self._driver.current_url != url: print('connecting to url...', url) self._driver.get(url) try: self._driver.find_element(By.TAG_NAME, 'video') except exceptions.NoSuchElementException: print('동영상이 없는 페이지입니다.') return None print('영상 대기 중...', end='\r') elapsed = 0 while True: vid_js = self._driver.find_element(By.TAG_NAME, 'video-js') if 'vjs-playing' in vid_js.get_attribute('class'): break time.sleep(0.5) elapsed += 0.5 if elapsed > 10: try: self._driver.find_element(By.XPATH, "//button[contains(@class, 'vjs-paused')]").click() except Exception as e: print(e) if elapsed > 30: print('대기 시간이 너무 오래 걸립니다...') return None print('영상 로드 완료', end='\r') # 만약 영상이 재생 중이라면 멈추게 하기. try: self._driver.find_element(By.XPATH, "//button[contains(@class, 'vjs-playing')]").click() except exceptions.NoSuchElementException: pass # 이거는 제외하고 받아오기 title_except = ['목차', '커뮤니티 게시판', '노트'] # ['현재 강의 제목', '전체 강의 제목', ...[목차 순서대로]] titles = [elem.text for elem in self._driver.find_elements(By.CLASS_NAME, 'title') if elem.text not in title_except] # 코스 제목. titles 활용 시 안 나오는 오류가 있어서 선택자로 뽑기 course_title = self._driver.find_element(By.CSS_SELECTOR, ".is-current .title").text course_index = titles[2:].index(course_title) + 1 course_title = trim_path(course_title) course_filename = f'{course_index} - {course_title}.mp4' # 강의 제목 lecture_title = titles[1] lecture_title = trim_path(lecture_title) print(f'[{lecture_title} - {course_title}] 강좌를 다운로드합니다.') # 파일이 이미 존재한다면 기본적으로 새로 생성하지 않는다. if os.path.isfile(os.path.join(DEST_PATH, lecture_title, course_filename)): print(os.path.join(DEST_PATH, lecture_title, course_filename)) print('이미 존재하는 강의입니다. 다운로드하지 않습니다.') return None headers = {} root_url = None meta_info_url = None for request in self._driver.requests: if "https://vod.inflearn.com" in request.url and '.m3u8' in request.url: root_url = request.url[:request.url.rfind('/')] + '/' headers.update(request.headers) resp = requests.get(url=request.url, headers=headers) lines = [line for line in resp.text.strip().split('\n') if '#' not in line] meta_info_url = lines[-1] break if root_url is None: print('root url을 찾을 수 없습니다.') return None resp = requests.get(url=(root_url + meta_info_url), headers=headers) if resp.status_code != 200: print(resp.text) return None # get source url list sources = [src for src in resp.text.strip().split('\n') if '#' not in src] videos = [] for idx, src in enumerate(sources): print(f'영상 다운로드 중... ({idx / len(sources) * 100:<4.1f}%)', end='\r') resp = requests.get(url=(root_url + src), headers=headers) if resp.status_code == 200: videos.append(resp.content) print('영상 다운로드 완료. 파일로 다운로드합니다.') # 다운로드 받을 장소. src_path = os.path.join(DEST_PATH, lecture_title) if not os.path.isdir(src_path): os.mkdir(src_path) with open(os.path.join(src_path, course_filename), 'wb') as f: for idx, vid in enumerate(videos): print(f'파일 합치는 중... ({idx / len(videos) * 100:<4.1f}%)', end='\r') f.write(vid) print('다운로드 완료.', lecture_title, '-', course_title) videos.clear() if __name__ == '__main__': vc = VideoCrawler() vc.login() while True: userinput = input('현재 강의에서 모두 다운로드 받으려면 y 입력. 나가려면 out 입력. ') if userinput.lower() == 'y': vc.get_videos_from_current_lecture() elif userinput.lower() == 'out': break
깃허브에서도 찾아볼 수 있으니 참고하시기 바랍니다. 버그 수정 또는 기타 편의 사항이 업데이트될 예정입니다...!
깃허브 주소 : JadenChoi2k/video-scrapper-videojs
'개발 > 웹크롤링' 카테고리의 다른 글
인프런 동영상 웹 스크래핑하기 - 3 : 네트워크 분석 - 2 (0) 2022.01.16 인프런 동영상 웹 스크래핑하기 - 3 : 네트워크 분석 - 1 (1) 2022.01.10 인프런 동영상 웹 스크래핑하기 - 2: 자동로그인 기능 - 2 (0) 2022.01.08 인프런 동영상 웹 스크래핑하기 - 2: 자동로그인 기능 - 1 (1) 2022.01.08 인프런 동영상 웹 스크래핑하기 - 1: 준비 과정 (1) 2022.01.08