이 글은 제가 작업했던 내용을 정리하기 위해 수기 형식으로 작성 된 글입니다.
1. 텍스트 데이터 수집(크리에이터 컨텐츠, 광고주 제안서)
- 텍스트 데이터 전처리 및 키워드화
- 키워드로 워드임베딩 모델 학습
- 아이템 벡터화
저번에 작성했던 글에서 전체로직을 간단하게 소개했었다. 이제 하나씩 내용을 소개하려 한다.
1. 텍스트 데이터 수집(크리에이터 컨텐츠, 광고주 제안서)-1
데이터를 다루는 입장에서 데이터가 변화한다면 해당 변화를 추적하고 DB를 업데이트 해주어야 한다. 사실상 광고주의 제안서는 새로 추가되는 것 뿐 데이터가 변동되는 부분은 없다.
하지만 크리에이터의 경우 채널에 컨텐츠가 지속적으로 새로 올라오거나, 구독자수가 변동되고, 해당 크리에이터의 채널 자체가 없어지는 경우도 있다.
예를 들어 광고주가 추천로직을 통해 노출된 크리에이터가 맘에 들어서 해당 크리에이터의 채널에 들어갔는데 채널이 없거나, 영상이 없거나, 데이터가 다르다면 서비스의 신뢰도가 바닥으로 떨어질 것이다.
이부분을 방지하기 위해서는 지속적인 데이터 수집 과정이 필요하고 이를 스크래핑을 통해 수집하였다.
스크래핑을 통해서 수집해야 될 데이터는 아래와 같이 구성하였다.
- 채널명
- 구독자 수
- 지역
- 영상제목
- 영상 조회수
- 영상 업로드일시
- 영상 더보기 텍스트
- 영상 장르
스크래핑 기초
스크래핑에 사용한 언어는 python이다.
초기 스크래핑에 사용한 라이브러리는 셀레니움과 뷰티풀스프(?)였다.
스크래핑 자체는 코딩을 배우고나서 처음 해보는 거라 시행착오를 좀 겪었다. 어떤코드가 어떤걸 의미하는지 구글신의 도움을 많이 받았었다. 감사해요 구글신 (그리고 열심히 블로그 써주신 여러분들께도 감사..)
그렇게 우여곡절을 겪으며 짠 스크래핑 로직을 간단하게 설명하면 아래와 같다.
셀레니움
크롬창 생성 → Youtube 채널접속 → 화면조작 수행(스크롤, 클릭 등)
뷰티풀스프
화면조작 수행 완료 된 페이지 소스 추출 → 추출 된 소스에서 데이터 검색 → 데이터 텍스트화
최종 결과
텍스트화 된 데이터를 전처리 하여 DB에 업로드
스크래핑 코드
from selenium import webdriver
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.support.ui import Select
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By
import time
from bs4 import BeautifulSoup
# 셀레니움 기본 세팅
user_agent = 'user-agent=Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.87 Safari/537.36'
options = webdriver.ChromeOptions()
options.add_argument('--disable-translate')
options.add_argument('--no-sandbox')
options.add_argument('--disable-dev-shm-usage')
options.add_argument("--disable-infobars")
options.add_argument("--enable-logging")
options.add_argument("--log-level=0")
options.add_argument("--single-process")
options.add_argument("--ignore-certificate-errors")
options.add_argument('--start-fullscreen')
options.add_argument('user-agent={0}'.format(user_agent))
driver = webdriver.Chrome(options=options)
driver.implicitly_wait(3)
base_url = '<https://www.youtube.com>'
recent_order_para = '/videos'
about_suffix = '/about'
c_url = "<https://www.youtube.com/channel/UCSQ55iSysqYErLRDC6ARi9w>"
# 예시 채널로 크롬드라이버 접속
driver.get(c_url+recent_order_para)
for i in range(3):
driver.find_element(By.TAG_NAME,'body').send_keys(Keys.PAGE_DOWN)
time.sleep(1)
# 해당 채널의 30개 영상 제목, 영상 링크 수집
page = driver.page_source
soup = BeautifulSoup(page,'lxml')
channel_name = driver.find_element_by_xpath('//*[@id="text-container"]').text
follower = driver.find_element_by_css_selector('#subscriber-count').text
all_title = soup.find_all('a','yt-simple-endpoint focus-on-expand style-scope ytd-rich-grid-media')
vod_link = [base_url+n['href'] for n in all_title]
vod_link = vod_link[0:30]
creator_data = []
for i in range(len(vod_link)):
driver.get(vod_link[i])
driver.implicitly_wait(5)
pannel = driver.find_element(By.XPATH,'//*[@id="contents"]')
driver.execute_script("arguments[0].scrollBy(0,100)",pannel)
button = driver.find_element(By.ID,'expand')
button.click()
page = driver.page_source
soup = BeautifulSoup(page,'lxml')
more_text = soup.select('#description')[1].text
meta_box = soup.find_all('span',"style-scope yt-formatted-string bold")
vod_title = soup.find('h1',"style-scope ytd-watch-metadata").text.replace('\\n','')
view_cnt = meta_box[0].string
pub_dt = meta_box[2].string
creator_data.append([channel_name, follower, vod_link[i], vod_title, more_text, view_cnt, pub_dt])
pd.DataFrame(creator_data,columns=['채널명','구독자수','영상링크','영상제목','더보기내용', '조회수','업로드날짜'])
driver.quit()
스크래핑 결과
이렇게 구성된 크롤러의 경우 채널 1개당 영상 10개를 확인한다는 가정하에 1.5분에 한명꼴로 크롤링을 하게 된다.
실제적으로는 업무상 필요한 데이터는 추가적으로 더 있어 약 3분정도의 시간에 걸쳐 한명의 크리에이터가 크롤링 되었다.
결과적으로, 다뤘던 크리에이터 DB의 개수가 약 6만개 정도였는데 크롤러 하나로 돌게 되면 약 125일이 지나야 전체 데이터가 업데이트 될 수 있다.
이렇게 되면 너무 시간적인 리스크가 생기게 된다.
이걸 해결하기 위해서 도커로 크롤러 이미지를 만들고, 컨테이너를 여러개 돌려서 해결해 보기로 했다.
스크래핑에 도커 한 스푼
도커는 한마디로 말하자면 내가 언제 어디서든 사용하고 싶은 코드를 따로 한땀 한땀 구성하지 않고도 사용할 수 있도록 마치 CD나 사진처럼 코드를 이미지로 만들어 도커 레포지토리에 올리고 이 이미지를 받아 사용하는 방식이다.
이를 통해 OS에 구애받지 않고 다양한 모델, 코드, OS등을 사용할 수 있다. (도커에 관련된 기초 링크는 아래 참조 참고)
도커 이미지를 만들기 위해서는 아래와같이 도커파일이 꼭 필요하다.
일반적으로 도커파일은 아래와 같이 구성된다.
# 도커 컨테이너의 기본 구성
# 현재 파이썬으로 구성되어 있으나, 우분투나 다른 OS로도 구축 가능
FROM python:3
ARG DEBIAN_FRONTEND=noninteractive
ENV LANG C.UTF-8-SIG
# 필요한 라이브러리를 설치하는 부분
# requirements.txt를 활용하여 설치도 가능함
RUN apt-get update -y
RUN wget <https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb>
RUN apt -y install ./google-chrome-stable_current_amd64.deb
RUN wget -O /tmp/chromedriver.zip <http://chromedriver.storage.googleapis.com/`> curl -sS chromedriver.storage.googleapis.com/LATEST_RELEASE`/chromedriver_linux64.zip
RUN unzip /tmp/chromedriver.zip chromedriver -d /usr/local/bin/
RUN pip3 install pip --upgrade
RUN pip3 install selenium
RUN pip3 install jupyter
RUN pip3 install pandas
RUN pip3 install datetime
RUN pip3 install BeautifulSoup4
RUN pip3 install requests
RUN pip3 install lxml
RUN pip3 install PyMySQL
RUN pip3 install --upgrade --ignore-installed pip setuptools
# 도커 파일이 위치한 폴더를 도커 이미지 내부에 복사
COPY . /usr/src
# 커맨드를 입력하기 위한 workdir 설정
WORKDIR /usr/src
# 커맨드 입력
CMD ["python3", "total_collector.py"]
도커 파일의 경우 메모장으로 작성하게 되면 제대로 작성이 안 될 수도 있으니, IDE를 활용해서 만드는게 편하다.
도커 파일을 처음 만들 때 그냥 일반 메모장으로 만들었는데, 이를 인식하지 못해서 도커 이미지가 만들어 지지 않았던 기억이 있다.
이렇게 만들어진 도커 이미지는 터미널 상에서 pull 명령어로 도커 레포에 올리거나 도커 데스크탑을 통해서 올릴 수 도 있다. 도커 레포에 올린다면 어떤 컴퓨터에서 건 해당 이미지를 받아서 사용할 수 있다.
사실 도커 컨테이너 여러개를 동시에 활용하는 경우에는 도커 스웜을 사용하는게 더 효과적이긴 하다. 롤링 업데이트 기능도 있고, 전체를 한번에 삭제하거나 서버를 분리해서 사용할 수 도 있다.
이전에 AWS를 활용해서 python기반의 영상 제작 프로그램을 만들었던 적이 있었는데 그때도 도커를 활용해서 대량 제작을 했었다. 그때 활용했던게 도커 스웜이었으나, 결과적으로는 트래픽을 너무 많이 사용하게 되어서 로컬에서 영상을 제작하게 되었던 경험이 있다.
스크래핑 같은 경우는 성능이 뛰어난 데스크탑에서 이미지를 돌려 사용하게 되어서 굳이 도커스웜을 구축해서 진행하지는 않았다.
도커 컨테이너는 터미널에서 직접 명령어를 입력해서도 만들 수 있기 때문에 쉘 스크립트를 만들어 주기적으로 기존에 수집 된 데이터를 제외한 나머지 데이터를 나누어 스크래핑하게 만들 수도 있다.
#!/bin/bash
sudo docker run -d --restart always --name collect1 -it 도커이미지이름
sudo docker run -d --restart always --name collect2 -it 도커이미지이름
sudo docker run -d --restart always --name collect3 -it 도커이미지이름
sudo docker run -d --restart always --name collect4 -it 도커이미지이름
sudo docker run -d --restart always --name collect5 -it 도커이미지이름
....
위에서 보았듯이 스크래핑 한개로는 약 125일이 걸릴일이 도커 컨테이너 15개로 7~8일로 줄일 수 있게 되었다.
PS.
크롤링이 아니라 스크래핑이라 적어놓은 이유는 변화를 직간접적으로 추적하여 데이터를 수집하는 방식이 아닌, 내가 정해놓은 주기에 따라 정해진 페이지의 데이터를 가져오기만 하기 때문이다.
이전에는 그것또한 크롤링이다라고 생각하였지만 아래의 글을 보고 생각이 조금 바뀌었다.
크롤링과 스크래핑의 차이는 아래의 글에서 확인해 볼 수 있다.
https://velog.io/@mowinckel/웹-크롤링-I
참조링크
[Docker] 파이썬 스크립트를 실행하는 도커 이미지 빌드하기
도커로 파이썬 코드 실행하기, Run python code with docker
[Docker] 딥러닝과 파이썬 환경구축을 위한 도커파일
PyCharm + Docker로 파이썬 개발환경 셋업하기 (Dockerization)
도커(Docker) 입문편: 컨테이너 기초부터 서버 배포까지
리눅스에서 파이썬 환경 구축하기 (Docker를 이용해 테스트)
[Docker Basic] 16. Docker 데이터 저장 개념 / 기본 명령어 - volume
[도커] 도커개념 & 도커 실행하기(이미지 생성 / 컨테이너 생성)
공유 디렉토리 연결
도커 컨테이너에 파일 복사하기
[Docker] 기동중인 도커 컨테이너에 파일 복사 및 스냅샷 생성
도커 컨테이너 볼륨에 마운트하기
Docker 컨테이너에 데이터 저장 (볼륨/바인드 마운트)
[Docker] Volume과 Bind mount에 대하여
[Docker] 데이터 관리(1) Volume 과 Bind mounts
도커 기초 블로그
도커 도커기초 도커이미지 도커 이미지만들기 도커파일작성 도커파일 크롤링 크롤링 기초 유튜브 크롤링 셀레니움 뷰티풀숲 뷰티풀수프 selenium beautifulsoup python python 크롤링 스크래핑
'[신.만.추]' 카테고리의 다른 글
신입이 만드는 추천시스템-6(웹서버 구축) (0) | 2022.12.10 |
---|---|
신입이 만드는 추천시스템-5(아이템 벡터화) (0) | 2022.12.10 |
신입이 만드는 추천시스템-4(한국어 전처리 및 워드임베딩) (0) | 2022.12.09 |
신입이 만드는 추천시스템-3(셀레니움 최소화) (0) | 2022.12.09 |
신입이 만드는 추천시스템-1(개요) (0) | 2022.12.09 |