336x280(권장), 300x250(권장), 250x250, 200x200 크기의 광고 코드만 넣을 수 있습니다.

11번가 답게 11월 11일까지 원서를 접수 받았다. 그리고 13일에 시험을 바로 보고, 11일 뒤인 24일에 결과 발표가 나왔다. 

 

자소서 질문

-활용 가능한 프로그래밍 언어/기술/개발Tool 등을 키워드로 작성해주세요.

-지원직무를 선택한 이유에 대해 설명하고 이를 위해 어떠한 노력을 하였는지 소개해주세요.

-도전적인 목표를 설정하고 지속적인 몰입을 통해 성장한 대표적인 경험에 대하여 소개해주세요.

-공동의 목표달성을 위해 상호신뢰와 소통을 바탕으로 협업을 진행한 사례에 대하여 역할과 기여 중심으로 소개해주세요.

 

지원할 수 있는 직무가 SW개발, 검색모델링, Machine Learning 이렇게 3가지로 나뉘었는데 직무 내용을 보니 검색모델링이 더 적합한 것 같아서 검색모델링쪽으로 지원했다. 

 

 

 

2019년에 올라온 채용 영상을 보면 '이 직무는 이런 일을 하는구나'를 느낄 수 있어서 꼭 보고 지원하면 도움이 될 것으로 보인다. 저때는 검색추천이 있었는데 직무간 변화가 있었던 모양이다. 

 

ML쪽은 이미지 검색, 카탈로그 자동 생성쪽이었고, 검색모델링이 조금 더 NLP쪽이라 진행했던 프로젝트(카테고리 분류, 추천, 검색 기능 개선)와 비슷했던 것 같다. 사실 지원 인원이 더 적었다. 

 

 

 

 

다른 곳과 다르게 코딩테스트(코테)는 프로그래머스로 하지 않고 코딜리티(codility)로 진행을 했다. 부정행위 적발에 대한 얘기도 없을 뿐 아니라 별도 IDE를 사용할 수 있어서, 약간 본인의 양심에 맡기는 듯 했다. 그리고 문제는 영어다.

 

코딜리티는 처음이라서 사이트에서 어떤식으로 문제가 나오는지 연습을 했다.

 

 

 

 

 

1. Iterations lesson - Learn to Code - Codility

Find longest sequence of zeros in binary representation of an integer.

app.codility.com

회원가입을 하고 문제를 풀어볼 수 있다. 히든 케이스도 히든 케이스지만... 시간 복잡도(O)를 중요시 여겼다. O^2가 되면 효율이 엄청 떨어지는 것을 확인할 수 있다. 솔직히 알고리즘도 아직 이해가 안 됐는데, 시간 복잡도 개념은 더 생소했다. 어떻게 하면 효율적으로 풀 수 있을지를 한 번 더 생각해야 했다.

 

이전에는 백준을 풀었는데 이번에 코딜리티를 풀면서 깨달았던 점은, 예전에 했던 토이 프로젝트들 코드가 진짜 최악의 효율을 가지고 있다는 것... 데이터가 적었으니 괜찮았지, 데이터가 많아지면 시간이 엄청 걸리겠구나라고 느꼈다. 그리고 for문 2번 돌리면 될 것을 왜 이렇게 복잡하게 해야하나 싶기도 했다 ㅋㅋ

 

하여튼 11번가 코딩테스트 난이도 후기를 찾아봤는데, 사람들이 대부분 쉽다고 얘기했다.. 알고리즘, 구현도 다른 코테보다 쉽고, 복잡한 알고리즘도 안 나온다고...

 

 

 

 

결과는 당연히 떨어졌지만 약간 당황스러웠다. 11번가 코테가 끝나고 프로그래머스를 풀었는데 2~3 레벨에 비해, 문제가 어려운 편은 아닌 것 같았다. 그런데 시간대별로 각각 다른 직무의 사람들이 시험을 봐서, 앞에 S/W 직무 사람들의 후기를 들었을 때 대부분 문제가 너무 쉽게 나왔다고 했다. 1~2줄이면 가능하다부터, 시간복잡도가 이렇다 저렇다. 

 

후기를 보고 별 걱정 없이 1시간 30분짜리, 3문제를 봤다. 그.런.데 딱 첫번째 문제부터 간단하게 나올 문제가 아니었다. 구현이었는데 조금은 생각해야할 문제였고, 마지막 문제는 조금 이해가 힘들었다. 테스트 케이스를 하나 더 줬으면 좋지 않았을까 싶었는데 1개만 주어졌다. 2번 문제는 코딜리티 예시 문제와 비슷해서 금방 풀었다.

 

1번 문제는 테스트케이스가 다 맞았지만, 아마 히든 케이스에서 틀릴 것 같은 예감이 있었고, 3번 문제는 거의 손도 못 대고 끝났다. 좀 아쉬움이 남는 코딩테스트 ㅠㅠ 실력을 더 키워야겠다.

 

 

 

 

 

 

 

 

 

 

336x280(권장), 300x250(권장), 250x250, 200x200 크기의 광고 코드만 넣을 수 있습니다.

저번주 토요일, 어제 갑작스런 코딩테스트 이메일을 받아서 계속 알고리즘 공부를 했다. 그래서 오랜만에 글을 쓰게 된다.

 

자소설에서 자소서를 148명이나 작성했으니 최소 2~3배를 뽑는다고 하면 많게 잡으면 450명 정도 쓴거 같은데 서류를 붙었다는게 신기했다. 자소설 대화방에서도 탈락했다는 사람이 거의 없는 걸 보니 코테를 보고나서 결정하는게 아닌가 싶었다.

 

자기소개서 내용도 솔직히 첫번째 말고는 차별화해서 쓸 게 없었다. 직무에서 NLP도 한다는 내용이 있어서 관심을 가지고 내용을 작성했다.

 

 

 

자기소개소 질문

 

- 회사와 직무를 선택한 이유는 무엇이며, 만약 입사하게된다면 어떻게 회사 발전에 기여할 수 있을지 기술해주시기 바랍니다.(700)

- 지원하신 직무와 관련된 역량을 키우기 위해 노력한 경험 및 결과에 대해 작성해주시기 바랍니다. (700, 최대 3개까지 작성 가능) (예시) [관련 경험 제목] 작성 후 ①역량을 키우기 위한 노력한 경험에 대한 내용과 기간을 작성 ②교내활동(강의/교내 프로젝트/논문/스터디/동아리/학회 등)과 교외활동(공모전/개인학습/프로젝트 등) 등으로 나누어 자유롭게 선택 가능

- CJ올리브네트웍스가 '취업준비생이 가장 가고싶은 회사 1위'가 되기 위해서 어떤 부분이 필요하다고 생각하는지 기술해주시기 바랍니다. (100)

 

 

 

 

과제 테스트 총 2문제 120분

 

CJ올리브네트웍스는 특이하게 인적성이 아닌 인성검사만 봤다. 적성까지 더했으면 아마 큰일 났을 듯... 그리고 프로그래머스로 처음 시험을 보다 보니 이거저거 세팅할 것이 많았다. 그랠도 친절하게 하는 안내서까지 보내주셔서 쉽게 따라할 수 있었다. 다른 곳에서는 코딩 테스트라고 하지만 CJ는 데이터 분석 과제라고 해서 2시간 동안 2문제를 푸는 것이었다. 

 

ML/AL 쪽은 검색을 안 하고 외워서까지 할 실력은 아닌데 처음에 멘붕이었다. 다른 사람들도 외워서 하기 힘들다고 얘기를 하고 있었는데 채용담당자님께서 메일 하나를 더 보내주셨다.

 

 

 

오픈북으로 시험을 본다는 것이다. 구글, 네이버에서 검색은 가능하지만, 절대 깃허브에 들어가면 안 된다는 내용이다. 문제 유형에 대해서 간단하게 언급을 하자면, 첫번째 문제는 데이터를 가지고 연산을 할 수 있으면 된다. 자주 다루셨던 분이라면 그래도 금방 하실 수 있을 것 같다. 저는 하나 헷갈리는 부분이 있어서 중간에 삥삥 돌아서 시간을 많이 뺏겼다.

 

두번째 문제는 여러 모델들을 사용해 예측을 해서 정확도를 높이면 됩니다. 오픈 카톡 인원 중에 상반기 때 시험을 보고, 하반기 때도 합격은 했는데 다른 회사랑 겹쳐서 힌트를 주셨다. 그래서 준비기간동안 캐글 데이터셋을 가지고 데이터 분석을 했다. 저는 시각화를 사용해서 데이터를 보고, 어떻게 하면 정확도를 높일 수 있을까 뻘짓을 하다가... 마지막에 데이터가 꼬여서 문제를 제대로 제출하지 못 했다 ㅠㅠ 우선 간단하게 해서 제출부터 하시는 것을 추천 드려요.

 

후기를 보면 정확도가 거의 비슷비슷했다. 그리고 거의 합격컷이 2솔이 아닐까 싶다. 처음 보는 코테라 어떻게 해야할지 몰라서 당황했다는 건 핑계고, 준비가 부족했던 것 같다. 신기하게 서류를 붙여주는 곳들이 있어서 다음에는 다른 코테 후기를 써보겠습니다.

 

 

 

 

 

 

 

336x280(권장), 300x250(권장), 250x250, 200x200 크기의 광고 코드만 넣을 수 있습니다.

지금 크롤링을 하고 있어서 시간이 나는 김에 글을 작성합니다. 크롤링도 크롤링이지만 이 데이터를 어떻게 정제할지가 더 고민이네요. 지난 번 글들을 활용해서 작성하오니 본인의 목적에 맞게끔 수정해서 사용하면 됩니다!

 

 

 

 

 

 

유튜브 크롤링(1) - 셀레니움 페이지 자동 번역, api 번역기 없이 가능! (키 입력, 마우스 입력)

유튜브로 새로운 수익모델을 찾기위한 채널 분석을 시도하고 있다. (기존 채널에 영상을 새로 올려야 하는데 요즘 못 올리고 있다 ㅠㅠ) 솔직히 노가다를 해도 되는데 파이썬을 배웠으면 자동화

0goodmorning.tistory.com

기능

- 특정 유튜브 채널에서 동영상 목록의 링크를 가져오기 (채널명, 구독자수)

- 제목, 조회수, 날짜, 좋아요 수, 싫어요 수, 댓글 개수

- 댓글 크롤링 (번역 기능 추가)

- 자동번역 자막 추출

 

 

 

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
from selenium import webdriver
from bs4 import BeautifulSoup
from selenium import webdriver
from selenium.webdriver.common.keys import Keys
import time
 
options = webdriver.ChromeOptions() # 크롬 옵션 객체 생성
user_agent = "Mozilla/5.0 (Windows NT 4.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/37.0.2049.0 Safari/537.36 "
options.add_argument('user-agent=' + user_agent)
options.add_argument('headless'# headless 모드 설정
options.add_argument("window-size=1920x1080"# 화면크기(전체화면)
options.add_argument("disable-gpu"
options.add_argument("disable-infobars")
options.add_argument("--disable-extensions")
options.add_argument("--mute-audio"#mute
options.add_argument('--blink-settings=imagesEnabled=false'#브라우저에서 이미지 로딩을 하지 않습니다.
options.add_argument('incognito'#시크릿 모드의 브라우저가 실행됩니다.
options.add_argument("--start-maximized")
 
#1
prefs = {
  "translate_whitelists": {"en":"ko"},
  "translate":{"enabled":"true"}
}
options.add_experimental_option("prefs", prefs)
 
#2
prefs = {
  "translate_whitelists": {"your native language":"ko"},
  "translate":{"enabled":"True"}
}
options.add_experimental_option("prefs", prefs)
 
#3
options.add_experimental_option('prefs', {'intl.accept_languages''ko,ko_kr'})
cs

기본 셀레니움 webdriver 세팅입니다. prefs 기능은 영어를 번역할 때 필요한 기능이라서 끄셔도 상관 없습니다. 그리고 처음에 어떻게 돌아가는지 궁금하시면 # options.add_argument('headless') headless 기능을 꺼주세요.

 

 

 

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
import os
import pandas as pd
import winsound
 
ytb = pd.read_csv('youtube_link.csv')
ytb_link = ytb.link.to_list()
 
for i in ytb_link :
    
    driver = webdriver.Chrome('chromedriver.exe', options= options)
    driver.get(i)
 
    # 스크롤 다운
    time.sleep(1.5)
   endkey = 4 # 90~120개 / 늘릴때 마다 30개
    while endkey:
        driver.find_element_by_tag_name('body').send_keys(Keys.END)
        time.sleep(0.3)
        endk -= 1
 
    channel_name = driver.find_element_by_xpath('//*[@id="text-container"]').text
    subscribe = driver.find_element_by_css_selector('#subscriber-count').text
    channel_name = re.sub('[=+,#/\?:^$.@*\"※~&%ㆍ!』\\‘|\(\)\[\]\<\>`\'…《\》]''', channel_name)
    # print(channel_name,subscribe)
 
    # bs4 실행    
    html = driver.page_source
    soup = BeautifulSoup(html, 'lxml')
 
    video_list0 = soup.find('div', {'id''contents'})
    video_list2 = video_list0.find_all('ytd-grid-video-renderer',{'class':'style-scope ytd-grid-renderer'})
 
    base_url = 'http://www.youtube.com'
    video_url = []
 
    # 반복문을 실행시켜 비디오의 주소를 video_url에 넣는다.
    for i in range(len(video_list2)):
        url = base_url+video_list2[i].find('a',{'id':'thumbnail'})['href']
        video_url.append(url)
 
    driver.quit()    
 
    if subscribe :
        channel = channel_name + ' - ' + subscribe
    else :
        channel = channel_name
        
    
    directory = f'data/{channel}/subtitle'
    if not os.path.exists(directory):
        os.makedirs(directory)
        
    print(channel, len(video_url))
    
    ytb_info(video_url, channel)
    print()
    winsound.PlaySound('sound.wav', winsound.SND_FILENAME)
cs

ytb_link : 본인이 수집하고자하는 채널을 리스트 형식으로 만들어주세요. 저는 csv 파일로 만들어서 컬럼 이름을 'link'로 하여 생성을 했습니다. 

 

channel : 채널 이름으로 폴더를 만들기 때문에, 폴더 이름에 들어가면 오류가 생기는 부호들을 미리 전처리 합니다. subtitle까지 만든 건 미리 자막 파일을 저장할 수 있는 폴더도 같이 만들어놨습니다.

 

# 한 채널이 끝날 때마다 윈도우 플레이사운드로 알려줍니다. 시끄럽다고 생각하시면 끄면 됩니다.

 

 

 

 

1
2
3
4
5
6
7
8
9
10
11
12
import time
 
last_page_height = driver.execute_script("return document.documentElement.scrollHeight")
 
while True:
    driver.execute_script("window.scrollTo(0, document.documentElement.scrollHeight);")
    time.sleep(0.5)
    
    if new_page_height == last_page_height:
        break
    last_page_height = new_page_height
    time.sleep(0.75)
cs

endkey : 본인이 수집하고자 하는 채널의 링크 개수를 결정합니다. 현재 설정으로는 90~120개를 수집합니다. time.sleep(2)으로 설정하시면 180개까지 크롤링을 합니다. endkey 개수를 늘리면 30개씩 추가가 됩니다. 에라 모르겠다하고 모든 링크를 크롤링하시려면 위에 코드를 입력해주세요.

 

 

 

 

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
# 정보만 크롤링하고 싶을 때
from bs4 import BeautifulSoup
import pyautogui
import pandas as pd
import re
 
def ytb_info2(video_url,channel) :
    print(f'{channel}',' 크롤링 시작')
    driver = webdriver.Chrome('C:/work/python/Asia_GAN/myproject/youtube/chromedriver.exe', options= options)
 
    #데이터 넣을 리스트
    date_list = []
    title_list = []
    view_list = []
    like_list = []
    dislike_list = []
    comment_list = []
    
    #각 채널별 영상으로 크롤링
    for i in range(len(video_url)):
        start_url = video_url[i]
        print(start_url, end= ' / ')
        driver.get(start_url)
        driver.implicitly_wait(1.5)
                
        body = driver.find_element_by_tag_name('body')
        
        #댓글 null 값 방지 
        num_of_pagedowns = 2
        while num_of_pagedowns:
            body.send_keys(Keys.PAGE_DOWN)
            time.sleep(0.5)
            num_of_pagedowns -= 1
            time.sleep(0.5)
        
        #크롤링 요소    
        try : 
            info = driver.find_element_by_css_selector('.style-scope ytd-video-primary-info-renderer').text.split('\n')
 
            if '인기 급상승 동영상' in info[0] :
                info.pop(0)
            elif '#' in info[0].split(' ')[0] :
                info.pop(0)
        
            title = info[0]
            divide = info[1].replace('조회수 ','').replace(',','').split('회')
            view = divide[0]
            date = divide[1].replace(' ','')
            like = info[2]
            dislike = info[3]    
            
            driver.implicitly_wait(1)  
                  
            try:
                comment = driver.find_element_by_css_selector('#count > yt-formatted-string > span:nth-child(2)').text.replace(',','')
            except:
                comment = '댓글x'
                
            #리스트에 추가
            title_list.append(title)
            view_list.append(view)
            date_list.append(date)
            like_list.append(like)
            dislike_list.append(dislike)
            comment_list.append(comment) 
            
            # 크롤링 정보 저장    
            new_data = {'date':date_list, 'title':title_list, 'view':view_list, 'comment': comment_list, 'like':like_list, 'dislike':dislike_list}
            df = pd.DataFrame(new_data)
            df.to_csv(f'data/{channel}/{channel}.csv', encoding='utf-8-sig')
        except :
            continue
        
        # 확인용
        print(title, view, date, like, dislike, comment)
   
    driver.quit()
cs

자막과 댓글이 필요 없을 경우

제목, 날짜, 조회수, 좋아요 수, 싫어요 수, 댓글 수만 크롤링을 합니다. 정보 양이 많지 않기 때문에 셀레니움만으로도 가능합니다. html_source를 bs4로 넘겼을 때와 비교해도 얼마 차이가 나지 않습니다.

 

# print(title, view, date, like, dislike, comment) 만약 어떤 정보가 나오는지 확인할 필요가 없으시면 비활성화해주세요. 

 

 

 

 

나는 댓글과 자막도 필요하신 분들은

밑으로

 

 

 

 

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
from youtube_transcript_api import YouTubeTranscriptApi
from konlpy.tag import Kkma
from pykospacing import Spacing
 
def ytb_subtitle(start_url, title) :
    try:
        code = start_url.split('=')[1]
        srt = YouTubeTranscriptApi.get_transcript(f"{code}", languages=['ko']) #한글로, 딕셔너리 구조
 
        text = ''
 
        for i in range(len(srt)):
            text += srt[i]['text'+ ' '
            
        text_ = text.replace(' ','')
 
        #문장 분리 / kss 사용해도 무방
        kkma = Kkma()
 
        text_sentences = kkma.sentences(text_)
 
        #종결 단어
        lst = ['죠','다','요','시오''습니까','십니까','됩니까','옵니까','뭡니까',]
 
        df = pd.read_csv('not_verb.csv',encoding='utf-8')
        not_verb = df.stop.to_list()
 
        #단어 단위로 끊기
        text_all = ' '.join(text_sentences).split(' ')
 
        for n in range(len(text_all)) :
            i = text_all[n]
            if len(i) == 1 : #한글자일 경우 추가로 작업x
                continue
            
            else :
                for j in lst : #종결 단어
                    #질문형
                    if j in lst[4:]:
                        i += '?'
                    
                    #명령형                
                    elif j == '시오' :
                        i += '!'
                    
                    #마침표    
                    else :
                        if i in not_verb : #특정 단어 제외
                            continue
                        else :        
                            if j == i[len(i)-1] : #종결
                                    text_all[n] += '.'
                                    
 
        spacing = Spacing()
        text_all_in_one = ' '.join(text_all)
 
        text_split = spacing(text_all_in_one.replace(' ','')).split('.')
        text2one= []
        for t in text_split:
            text2one.append(t.lstrip())  
            
        w = '. '.join(text2one)
                        
        f = open(f'data/{channel}/subtitle/{title}.txt','w',encoding='utf-8')
        f.write(w)
        f.close()
        print('O')
    except:
        print('X')
cs
 

유튜브 크롤링(2) - ㄹㅇ 초간단 유튜브 자막 다운 & 추출 (문장분리까지)

유튜브 크롤링 글에 제목, 조회수, 댓글, 좋아요를 크롤링하는 방법에 대해서 글을 써야 하는데, 요즘 자소서를 쓰고 알고리즘 공부도 하고 이것저것 하다보니 글을 쓸 시간이 많지 않았다. 유튜

0goodmorning.tistory.com

유튜브 자막 추출 다운과 관련해서는 이전 글을 참고해주시면 좋을 것 같습니다. not_verb.csv 파일의 경우 '다', '요'로 끝나는 단어 중 동사가 아닌 명사, 형용사 단어를 stop 컬럼으로 추가하시면 됩니다.

 

 

 

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
# 영어 번역 없음
import winsound as sd
from bs4 import BeautifulSoup
import pyautogui
import pandas as pd
import re
 
def beepsound():
    fr = 2000    # range : 37 ~ 32767
    du = 1000     # 1000 ms ==1second
    sd.Beep(fr, du) # winsound.Beep(frequency, duration)
 
def ytb_info(video_url,channel) :
    print(f'{channel}',' 크롤링 시작')
    driver = webdriver.Chrome('chromedriver.exe', options= options)
    # new_data = {'date': '', 'title': '', 'view': '', 'comment': '', 'like':'', 'dislike':''}
    
    count = 1
    
    #데이터 넣을 리스트
    date_list = []
    title_list = []
    view_list = []
    like_list = []
    dislike_list = []
    comment_list = []
    
    try:
        #각 채널별 영상으로 크롤링
        for i in range(len(video_url)):
            start_url = video_url[i]
            print(start_url, end= ' / ')
            driver.get(start_url)
            driver.implicitly_wait(1.5)
                    
            body = driver.find_element_by_tag_name('body')
            
            #댓글 null 값 방지 
            num_of_pagedowns = 1
            while num_of_pagedowns:
                body.send_keys(Keys.PAGE_DOWN)
                time.sleep(0.5)
                num_of_pagedowns -= 1
                driver.implicitly_wait(1)
            
            #크롤링 요소    
            try : 
                info = driver.find_element_by_css_selector('.style-scope ytd-video-primary-info-renderer').text.split('\n')
 
                if '인기 급상승 동영상' in info[0] :
                    info.pop(0)
                elif '#' in info[0].split(' ')[0] :
                    info.pop(0)
            
                title = info[0]
                divide = info[1].replace('조회수 ','').replace(',','').split('회')
                view = divide[0]
                date = divide[1].replace(' ','')
                like = info[2]
                dislike = info[3]    
                        
                try:
                    comment = driver.find_element_by_css_selector('#count > yt-formatted-string > span:nth-child(2)').text.replace(',','')
                except:
                    comment = '댓글x'
                    
                #리스트에 추가
                title_list.append(title)
                view_list.append(view)
                date_list.append(date)
                like_list.append(like)
                dislike_list.append(dislike)
                comment_list.append(comment) 
                
                # 크롤링 정보 저장    
                new_data = {'date':date_list, 'title':title_list, 'view':view_list, 'comment': comment_list, 'like':like_list, 'dislike':dislike_list}
                df = pd.DataFrame(new_data)
                df.to_csv(f'data/{channel}/-{channel}.csv', encoding='utf-8-sig')
 
            except :
                continue
 
            # print(title, view, date, like, dislike, comment)
            
            num_of_pagedowns = 1
            while num_of_pagedowns:
                body.send_keys(Keys.PAGE_DOWN)
                time.sleep(0.5)
                num_of_pagedowns -= 1
                
            #페이지 다운
            last_page_height = driver.execute_script("return document.documentElement.scrollHeight")
 
            while True:
                driver.execute_script("window.scrollTo(0, document.documentElement.scrollHeight);")
                # driver.implicitly_wait(2) #오류남
                time.sleep(0.5)
                new_page_height = driver.execute_script("return document.documentElement.scrollHeight")
 
                if new_page_height == last_page_height:
                    break
                last_page_height = new_page_height
                # driver.implicitly_wait(1)
                time.sleep(0.75)
            
            time.sleep(0.5)
 
            
            # 댓글 크롤링
            html = driver.page_source
            soup = BeautifulSoup(html, 'lxml')
            
            users = soup.select("div#header-author > h3 > #author-text > span")
            comments = soup.select("yt-formatted-string#content-text")
            
            user_list=[]
            review_list=[]
 
            for i in range(len(users)):
                str_tmp = str(users[i].text)
                str_tmp = str_tmp.replace('\n''')
                str_tmp = str_tmp.replace('\t''')
                str_tmp = str_tmp.replace('              ','')
                str_tmp = str_tmp.replace('            ','')
                user_list.append(str_tmp)
 
                str_tmp = str(comments[i].text) 
                str_tmp = str_tmp.replace('\n''')
                str_tmp = str_tmp.replace('\t''')
                str_tmp = str_tmp.replace('            ''')
 
                review_list.append(str_tmp)        
 
            
            # 댓글 추가    
            pd_data = {"ID":user_list, "Comment":review_list}
            youtube_pd = pd.DataFrame(pd_data)
            
            title = re.sub('[-=+,#/\?:^$.@*\"※~&%ㆍ!』\\‘|\(\)\[\]\<\>`\'…《\》]''', title)
            youtube_pd.to_csv(f"data/{channel}/{title}.csv", encoding = 'utf-8-sig')#,index_col = False)
            print('ㅁ',end='')
 
            # 자막 추출
            ytb_subtitle(start_url, title)
            
            # 광고 끄기
            if count :
                # time.sleep(1)
                try:
                    driver.implicitly_wait(0.5)
                    driver.find_element_by_css_selector("#main > div > ytd-button-renderer").click()
                    count -=1
                except:
                    continue
 
    except :
        driver.quit()
        beepsound()
    driver.quit()
    beepsound()
cs

기본 정보 / 댓글 / 자막까지 

기본 정보 크롤링 밑으로 추가된 기능은 스크롤 다운 후, html page_source를 bs4로 넘겨서 댓글을 크롤링 합니다. 양이 많기 때문에 셀레니움보다 가볍고 빠른 bs4를 사용하시는 것을 추천드립니다.

 

댓글을 다 크롤링하고, 자막까지 받았을 때 영상 1개당 33초 정도 걸렸습니다. 컴퓨터, 인터넷 사양에 따라서 다를 거라 생각합니다. 한 채널이 끝날 때마다 소리가 나게 했습니다. 필요 없으면 꺼주세요!

 

 

 

 

*주의사항 *

유튜브 댓글은 기본적으로 인기 댓글순으로 정렬이 되어있기 때문에, 뒤에 있는 댓글일수록 공감을 적게 받거나 관심이 적은 댓글일 확률이 높습니다. 저는 모든 댓글이 필요하지 않기 때문에, 가장 크롤링이 빠르면서 댓글들 정보를 모을 수 있게 시간 설정을 했습니다. 댓글이 적으면 모든 댓글을 크롤링하지만, 많아지면 60~90% 정도만 크롤링을 하게 됩니다.

 

모든 댓글들이 필요하신 분들은, time.sleep을 1초 이상으로 해주세요. driver.implicitly_wait의 경우 스크롤은 내려가는데 댓글들이 로딩이 되지 않는 경우가 있어서 time.sleep을 사용했습니다. 

 

 

 

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
#영어 번역
import pyautogui
import pandas as pd
import re
 
def ytb_info(video_url,channel) :
    print(f'{channel}',' 크롤링 시작')
    driver = webdriver.Chrome('chromedriver.exe', options= options)
    df = pd.DataFrame()
 
    count = 1
    
    #각 채널별 영상으로 크롤링
    for i in range(len(video_url)):
        start_url = video_url[i]
        print(start_url, end= '/ ')
        driver.implicitly_wait(1)
        driver.get(start_url)
        
        #영어 번역
        pyautogui.hotkey('shift','F10')
        for i in range(7):
            pyautogui.hotkey('down')
        pyautogui.hotkey('enter')
        
        body = driver.find_element_by_tag_name('body')
        
        #댓글 null 값 방지 
        num_of_pagedowns = 1
        while num_of_pagedowns:
            body.send_keys(Keys.PAGE_DOWN)
            time.sleep(.75)
            num_of_pagedowns -= 1
            driver.implicitly_wait(1)
        
        #크롤링 요소    
        info = driver.find_element_by_css_selector('.style-scope ytd-video-primary-info-renderer').text.split('\n')
 
        if '인기 급상승 동영상' in info[0] :
            info.pop(0)
        elif '#' in info[0].split(' ')[0] :
            info.pop(0)
        
        title = info[0]
        divide = info[1].replace('조회수 ','').replace(',','').split('회')
        view = divide[0]
        date = divide[1].replace(' ','')
        like = info[2]
        dislike = info[3]    
                
        try:
            comment = driver.find_element_by_css_selector('#count > yt-formatted-string > span:nth-child(2)').text.replace(',','')
        except:
            comment = '댓글x'
   
        
        # 크롤링 정보 저장    
        new_data = {'date':date, 'title':title, 'view':view, 'comment': comment, 'like':like, 'dislike':dislike}
        df = df.append(new_data, ignore_index=True)
        df.to_csv(f'data/{channel}/{channel}.csv', encoding='utf-8-sig')
        # print(title, view, date, like, dislike, comment)
        
        #페이지 다운
        last_page_height = driver.execute_script("return document.documentElement.scrollHeight")
 
        while True:
            driver.execute_script("window.scrollTo(0, document.documentElement.scrollHeight);")
            time.sleep(1)
            new_page_height = driver.execute_script("return document.documentElement.scrollHeight")
 
            if new_page_height == last_page_height:
                break
            last_page_height = new_page_height
            time.sleep(1)
        
        time.sleep(0.5)
        
        #댓글 크롤링
        review_list = []
        user_list =[]
        reviews = driver.find_elements_by_css_selector('#content-text')
        users = driver.find_elements_by_css_selector('h3.ytd-comment-renderer a span')
        num = 0
        for i in range(len(users)):
            review = reviews[i].text.replace('\n'' ')
            review_list.append(review)
            
            user = users[i].text
            user_list.append(user)
            
        # 댓글    
        pd_data = {"ID":user_list, "Comment":review_list}
        youtube_pd = pd.DataFrame(pd_data)
        
        title = re.sub('[-=+,#/\?:^$.@*\"※~&%ㆍ!』\\‘|\(\)\[\]\<\>`\'…《\》]''', title)
        youtube_pd.to_csv(f"data/{channel}/{title}.csv", encoding = 'utf-8-sig')
        print('ㅁ',end='')
 
        # 자막 추출
        ytb_subtitle(start_url, title)
        
        # 광고 끄기
        if count :
            # time.sleep(1)
            try:
                driver.implicitly_wait(0.5)
                driver.find_element_by_css_selector("#main > div > ytd-button-renderer").click()
                count -=1
            except:
                continue
        
    driver.quit()
cs

해외 번역

단점 : headless으로 하면 안 된다. 마우스를 사용하지 못 한다. 시간이 진짜아아아아 엄처어어어엉 오래 걸린다. 굳이 이렇게 안 해도 될 거라고 생각이 드는데 혹시나 필요하신 분들을 위해서 남긴다.

 

가장 문제가 되는 부분이 번역을 한 정보는 bs4로 넘어가지 않는다. 셀레니움으로 모든 댓글과 닉네임들을 모아야 하기 때문에 시간이 오래 걸리는 것이다.

 

 

 

이 데이터들을 어떻게 사용할 것인지는 아직까지는 비밀.

+ Recent posts