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로 넘어가지 않는다. 셀레니움으로 모든 댓글과 닉네임들을 모아야 하기 때문에 시간이 오래 걸리는 것이다.

 

 

 

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

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

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

 

 

 

유튜브 크롤링(3) 올인원 - 채널 제목, 댓글, 조회수, 자막까지

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

0goodmorning.tistory.com

다른 분들도 친절하게 크롤링 방법에 대해서 언급을 하고 있어서 일단 관련 글은 미루다가 작성(9.29 수정), 구글 검색을 해도 잘 나오지 않는 유튜브 자막 다운 방법에 대해서 쓰겠습니다. 검색을 했을 때 pytube3로도 다운이 가능하다고 되어있으나 2020년 글들이고 오류가 나서 사용을 하지 못 했습니다. 사용 방법을 아시면 알려주세요 :)

 

pip install youtube-transcript-api

 

 

 

1
2
3
4
5
6
7
8
9
10
11
12
from youtube_transcript_api import YouTubeTranscriptApi
# 오징어 게임 url https://www.youtube.com/watch?v=ysz5Sl3msyk
 
srt = YouTubeTranscriptApi.get_transcript("ysz5Sl3msyk", languages=['ko'])
 
with open("subtitles.txt""w", encoding='utf-8'as f:  
    for i in srt:
        # writing each element of srt on a new line
        f.write("{}\n".format(i))


# {'text': '저는...', 'start': 0.0, 'duration': 0.584} 이런 식으로 저장됨
cs

이걸로 srt 느낌의 자막을 다운로드 받을 수 있습니다. 쉽죠? 자막 다운이 급하신 분들을 위해서 먼저 설명을 드렸고, 딱 자막 부분만 필요하신 분들은 더 따라와 주세요. 오타나 인식이 잘못된 부분은 구글의 SST를 탓하세요! 

 

 

※ 야매 주의 ※

(야매가 싫으시면 뒤로가기 버튼을)

 

 

[사용]

pykospacing(맞춤법), kkma.sentence(kss도 가능!)

 

[시도]

kss(문장분리) / mecab, kkma, okt pos

 

[순서]

자막 다운 -> 띄어쓰기 제거, kkma.sentence -> 야매 문장 분리 -> 띄어쓰기 제거, spacing

-kss 시도를 해봤지만, 문장 부호가 없으면 시간이 많이 걸리고 모든 문장으로 안 나뉘어지는 단점 // 다시 해보니 0.3초 뭐지...

-spacing을 먼저 해봤으나 맞춤법 전문이라 제대로 안 나뉘어짐 ('~요' 인식이 잘 안 됨)

-꼬꼬마로 문장을 어느정도 나누고, 문장 부호를 추가하니 그나마 나아짐

 

 

 

1
2
3
4
5
6
7
from youtube_transcript_api import YouTubeTranscriptApi
# 오징어 게임 url https://www.youtube.com/watch?v=ysz5Sl3msyk
 
srt = YouTubeTranscriptApi.get_transcript("ysz5Sl3msyk", languages=['ko'])
 
for i in srt :
    print(i)
cs

어떤 식으로 자막이 이루어졌는지 확인을 해보기 전에, 영상이 자막을 지원하는 영상인지 아닌지부터 확인을 합니다. 자동생성 자막을 지원하지 않을 경우 에러가 납니다.

 

 

 

 

결과를 확인해보면 리스트 형식 안에 딕셔너리 형식이 있습니다. 우리는 'text'만 필요하기 때문에 딕셔너리에서 'text' 부분만 가져오면 됩니다. 

 

 

 

 

1
2
3
4
5
6
text = ''
 
for i in range(len(srt)):
    text += srt[i]['text'+ ' ' # text 부분만 가져옴
    
text_ = text.replace(' ','')
cs

간단하게 가져왔지만 문제가 하나 있습니다. 띄어쓰기가 제대로 되어 있지 않아서 잘 알아볼 수가 없습니다. 한국어 전처리 패키지인 PyKoSpacing을 바로 사용하려고 했으나, 문장 부호가 없어서 잘 되지 않습니다. 영어의 경우 친절하게 '.'을 찍어주는데 한국은 차별하는게 아닌가... 

 

kss 한국어 문장 분리기를 사용해보았으나 특정 어휘(EX : 처음)에서 이상하게 분리를 하고, 모든 문장을 나눠주지는 않았다. 글 쓰기 전에 kss를 사용하는데 상당한 시간이 걸려서 사용하지 않았는데, 글 쓰는 중에 다시 테스트를 하니 깔끔하게 잘 나뉘어서 당황스럽다. 그래서 Kkma.sentence()를 사용했던건데 어떤 것을 사용하든 상관은 없어보인다. 

 

그 후 종결어미인지 확인하기 위해서 mecab, kkma, okt를 사용해서 확인을 하려고 했으나 case를 나눌 것들이 너무 많아서 야매를 사용했다. kkma는 품사 분류표가 너무 복잡하고 pos로 나눌 때 명사가 포함되면 종결어미인지 분간이 잘 안 갔다. okt의 경우 품사 분류표가 간단하지만 (verb, noun 등..) 얘도 생각보다 case가 많았다.

 

 

 

 

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
from konlpy.tag import Kkma
import pandas as pd
 
#문장 분리 / 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] += '.'
                            print(text_all[n], end = '/ ')
cs

여기서부터 야매 방법이다.

 

종결형 단어를 뽑아서 lst로 만들었다. 구어체는 제외하고 뉴스기사를 여러개 찾아보면서 거의 웬만한 종결형 단어를 넣었다. 문제가 있으면 추가로 집어넣으면 된다. 그리고 문장을 모두 단어로 쪼개서 실행을 했다. 

 

처음에는 '죠, 다, 요'로만 구성하려고 했으나 질문, 감탄형 문장부호도 필요할 것 같아서 추가했다. 마지막 단어에 '다, 요'가 쓰인 경우, 무조건 종결형 어미가 아니라서 특정 단어를 제외했다. 바다, 날마다, 우간다, 노가다 / 필요, 수요, 중요, 노동요  ... 같은 단어를 찾아서 csv파일에 추가했다. 다로 끝나는 단어는 10만개가 넘어서 추후에 더 추가하면 될 듯 하다.

 

 

 

결과를 보면 나쁘지 않다. 걸러지는 걸 보여주면 효과가 더 좋아보일텐데 일단 변경되는 것만 보여드립니다. 그리고 나서 한국 전처리 기본 패키지인 pykospacing을 사용하게 되면 나름 괜찮게 결과가 나온다.

 

 

 

1
2
3
4
5
from pykospacing import Spacing
 
spacing = Spacing()
text_all_in_one = ' '.join(text_all)
print(spacing(text_all_in_one.replace(' ','')))
cs

단점은 마침표 다음에 띄어쓰기가 안 되어 있는 경우도 있어서. split으로 나누고 다시 붙이면 된다.

 

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

유튜브로 새로운 수익모델을 찾기위한 채널 분석을 시도하고 있다. (기존 채널에 영상을 새로 올려야 하는데 요즘 못 올리고 있다 ㅠㅠ) 솔직히 노가다를 해도 되는데 파이썬을 배웠으면 자동화를 하는게 맞지 않을까 싶고, 재밌는 실험들을 해보고 싶어서 코드를 짜고 있다.

 

셀레니움으로 페이지 자동 번역, 키 입력 정보가 필요하신 분들은 밑으로 내려주세요!

 

진행할 목록

- 페이지 성별 분석 (이름 데이터셋 수집 완료, CNN 카테고리 분류 완료, 검증 단계) => 얘 때문에 자동번역이 필요했음

- 페이지 연령 분석, 댓글 문체로 분류 도전 (자체 유튜브 조회수 100만 영상들에서 댓글 크롤링 후, 연령, 성비 분석 후 학습)

- 댓글로 영상 주제 분석 (긍정, 부정, 어떤 반응으로 영상이 화제가 됐는지 체크)

- 어그로 확인 (같은 주제, 같은 업로드 날짜 => 왜 다른 조회수? 썸네일, 키워드)

 

 

 

 

셀레니움 자동 번역 필요했던 이유?

연령대가 있는 층들은 대부분 유튜브 계정 닉네임이 본인 이름으로 되어 있어서 성별 분석하기가 조금 수월해 보였다. 하지만 영어로 이름을 입력하신 분들도 있어서 이걸 자동으로 번역해서 닉네임을 크롤링 해야 했다. 문제는 셀레니움으로 자동 번역이 잘 되지 않았다.

 

-파이썬에서 셀레니움 웹 사이트 언어 변경

-Translate the webpage opened via Selenium Webdriver to English

-Translate webpage language using Selenium

-Select an Option from the Right-Click Menu in Selenium Webdriver

-Modifying the language option in selenium

 

이렇게 다양하게 검색을 했는데도 문제가 잘 해결 되지 않았다. (구글이 PageRank를 활용한 알고리즘이라고 하는데 SEO 시스템을 잘 몰라서 외국에서 검색이 될지는 모르겠지만 그래도 찾아주시는 분들은 도움이 됐으면 좋겠다.) 일단 시도했던 것들에 대해서 간단하게 보여주자면

 

 

 

 

1
2
3
4
5
from selenium import webdriver
 
options = webdriver.ChromeOptions()
options.add_argument("--lang=ko_KR")
options.add_argument("--lang=ko")
cs

셀레니움에서 크롬옵션으로 기본 언어를 설정하면 된다고 하지만 내 경우는 되지 않았다. --lang=en 영어로도 안 된다는 사람들이 많았다. 그래서 계속 구글링을 시도했다.

 

 

 

 

 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
from selenium import webdriver
 
#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

translate_whitelists에서 en -> ko로 바꿔주면 된다고 친절하게 설명이 되어있는데 이것 또한 되지 않았다. 그래서 영어권에서는 2번, 3번 방법의 경우를 추천했는데 이것 또한 되지 않았다^^ 

 

 

 

그래서 생각한 방법이 오른쪽 마우스로 번역하면 되겠거니 싶었다. 마우스 좌표 값을 설정해서 오른쪽 버튼을 클릭하기 번거로워서, 키보드로 마우스 오른쪽 버튼을 누르는 방법이 있나 찾아보니 shift + f10이라고 구글에서 친히 알려주신다. 그리고 Translate에 'T' 앞 글자를 따서 단축키를 만들지 않았을까 하고 T를 눌러보니 번역이 됐다!

 

그리고 send_keys(Keys.SHIFT + Keys.F10 + 't')를 해봤더니 t가 눌리지 않았다. 이런... 

 

 

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
from selenium.webdriver.common.action_chains import ActionChains
from selenium.webdriver.common.keys import Keys
 
#1
body = driver.find_element_by_tag_name('body')
body.send_keys(Keys.SHIFT, 'a')
 
#2
action = webdriver.ActionChains(driver)
 
# 한번에 버튼 누르기
action.send_keys(Keys.SHIFT, Keys.F10, 't').perform()
 
# 키를 누르고 떼기
action.key_down(Keys.SHIFT).key_down(Keys.F10).send_keys("t").key_up(Keys.SHIFT).key_up(Keys.F10).perform()
cs

ctrl + f, shift + a 처럼 '페이지 내 검색어 찾기', '전체 선택'같이 셀레니움에서 번역이 아닌 단순 키 입력을 원하시는 분이라면 1번 방법을 사용하시면 됩니다. 이것도 안 돼서 더 찾아보니, ActionChains를 활용하면 된다고 해결책으로 제시했는데 이것도 되지 않았다. 

 

 

 

1
2
3
4
5
6
import pyautogui
 
pyautogui.hotkey('shift','F10')
for i in range(7):
    pyautogui.hotkey('down')
pyautogui.hotkey('enter')
cs

그래서 가장 원초적인 방법을 사용했다. 이전에 파이썬으로 계산기 들어가서 숫자 계산할 때 사용했던, 마우스 / 키보드 자동 조작 모듈을 사용했다. 

 

 

 

 

되긴 됐다. 자동으로 한국어 번역이...!

 

다만 단점이 있다면 headless로 작업이 되지 않다는 점과 pyautogui를 사용하기 때문에 초반에 다른 걸 만지면 안 된다. 좋게 생각하면 셀레니움으로 크롤링이 잘 작동되고 있구나를 확인한다고 생각하자.

 

다음은 유튜브 정보, 댓글 크롤링 방법들에 대한 포스팅을 진행하려고 한다. 요즘 유튜브가 지속적으로 페이지 UI랑, 아이콘 설정들을 조금씩 바꾸고 있어서 며칠 전 됐던 코드도 오류가 났다. 그래서 오늘 조금 수정을 했는데 글쓰는데 시간이 걸려서...

 

 

 

 

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

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

0goodmorning.tistory.com

 

유튜브 크롤링(3) 올인원 - 채널 제목, 댓글, 조회수, 자막까지

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

0goodmorning.tistory.com

 

+ Recent posts