오랜만에 들어온 김에 요즘 해보고 있는 것에 대해 글을 써보자는 취지로 작성!


요즘 Crawler 를 만들어보고 싶어서 이것저것 알아 본 결과, 개발하기 쉬운 Web Crawler를 선정했다.

 

우선, Web Crawler에 대한 설명은...

아래 링크를 참조하고, 

https://ko.wikipedia.org/wiki/%EC%9B%B9_%ED%81%AC%EB%A1%A4%EB%9F%AC

개발에 대해 얘기를 해보자.


Youtube를 Crawling 대상으로 선정한 이유는 아무래도 데이터가 많고, 다양하기 때문에 선정하게 되었다. (나름 참고할 문서들도 많았다...)


그리고 개발 언어는 Python을 이용하였고, 버전은 3.5버전이다.

원래 2.7버전을 했었는데, 3.5 버전이 더 빠르다보니(더 안정적인거 같다...) 바꾸게 되었다.


개발하기 전에 먼저 필요한 것은 Python 설치와 BeautifulSoup 설치이다.

Python 설치는 구글에 'Python 설치' 라고 검색하면 잘나와있기에 넘어간다.


BeautifulSoup 은 html 과 xml 문서를 파싱해주는 라이브러리이다.


pip를 이용하여 간단하게 설치할 수 있다.


pip install lxml



Youtube Crawling 의 경우, xml 문서를 파싱하기 때문에 위와 같이 설치하지만, html 파싱을 하는 경우에는 

아래 명령어로 설치를 할 수가 있다.


pip install html5lib



BeautifulSoup 라이브러리를 설치하고 본격적으로 개발을 해보자.


Python에 대해 잘모르지만, BeautifulSoup 문서와 다른 자료들을 참고하여 아래와 같은 소스를 만들었다. 



# -*- coding: utf-8 -*-

from bs4 import BeautifulSoup 

import lxml 

import requests 

import json 

import datetime

 


video_info = { 'title':'', 'video_link':'', 'img_link':'', 'play_time':'', 'hits' : '', 'updated_time':'', 'description':'', 'reg_time':'' } 


def get_video_link(target_url): 

response = requests.get(target_url) 

soup = BeautifulSoup(response.text, "lxml") 

lis = soup.find_all('li', {'class' : 'channels-content-item yt-shelf-grid-item'}) 

for li in lis : 

title = li.find('a', {'title' : True})['title'] 

video_link = 'https://www.youtube.com' + li.find('a', {'href' : True})['href'] 

img_link = li.find('img', {'src' : True})['src'] 


#<span class="video-time" aria-hidden="true"><span aria-label="8분, 55초">8:55</span></span>

play_time = li.find('span', {'class' : 'video-time'}).text 


#<ul class="yt-lockup-meta-info"><li>조회수 2,902,617회</li><li>6개월 전</li></ul>

hits = li.find_all('li')[2].text 

updated_time = li.find_all('li')[3].text 

video_info = { 

'title' : title, 

'video_link' : video_link, 

'img_link' : img_link, 

'play_time' : play_time, 

'hits' : hits, 

'updated_time' : updated_time 

}

 

print(video_info) 


return video_info 


def get_hot_video_info(target_url): 

response = requests.get(target_url) 

soup = BeautifulSoup(response.text, "lxml") 

lis = soup.find_all('li', {'class' : 'expanded-shelf-content-item-wrapper'}) 

for li in lis : 

# exception

try : 

title = li.find('a', {'title' : True})['title'] 

video_link = 'https://www.youtube.com' + li.find('a', {'href' : True})['href'] 

img_info = li.find('img', {'data-thumb' : True})

if img_info != None :

img_link = img_info['data-thumb'] 

else : 

img_link = li.find('img', {'src' : True})['src'] 

#<span class="video-time" aria-hidden="true"><span aria-label="8분, 55초">8:55</span></span>

#play_time = li.find('span', {'class' : 'video-time'}).text 

play_time_info = li.find('span', {'class' : 'video-time'})

if play_time_info != None :

play_time = play_time_info.text

else :

play_time = None

#<ul class="yt-lockup-meta-info"><li>조회수 2,902,617회</li><li>6개월 전</li></ul>

hits = li.find_all('li')[3].text 

updated_time = li.find_all('li')[2].text 

description_info = li.find('div',{'class':True, 'class':'yt-lockup-description yt-ui-ellipsis yt-ui-ellipsis-2'})

if description_info != None :

description = description_info.text

else :

description = None


now = datetime.datetime.now()

video_info = { 

'title' : title, 

'video_link' : video_link, 

'img_link' : img_link, 

'play_time' : play_time, 

'hits' : hits, 

'updated_time' : updated_time,

'description' : description,

'reg_time' : now.strftime('%Y-%m-%d %H:%M:%S')

}

print(video_info) 


except BaseException as e : 

print(e)

 


return video_info 



target_url = 'https://www.youtube.com/user/CJENMMUSIC/videos' 

target_url2 = 'https://www.youtube.com/feed/trending'


# 특정 채널

#get_video_link(target_url)


# 인기 리스트

get_hot_video_info(target_url2)




위의 소스를 조금씩 파헤쳐보자. 

첫번째 줄인 # -*- coding: utf-8 -*- 이 부분은 한글 인코딩과 관련하여 넣어준 부분이다.


다음으로 import 부분은 필요한 라이브러리를 선언하는 부분이다.


다음 줄에는 video_info 를 선언하는 부분이다. 이것은 Dictionary 타입으로 Key-Value 형식으로 되어 있다. Youtube Crawling 한 데이터 중 필요한 부분을 여기에 저장한다.


이 프로그램에는 두개의 함수가 있다. 하나는 get_video_link 이고 다른 하나는 get_hot_video_info 이다. 


get_video_link 는 특정 채널에 대한 영상 정보를 가져오는 함수이고, get_hot_video_info 는 인기 채널에 대한 영상 정보를 가져오는 함수이다.

참고로 get_video_link 함수는 예외처리가 제대로 안되어 있으므로, 참고를 한다면 get_hot_video_info 함수를 참고하는 것이 좋다.


설명도 get_hot_video_info 함수에 대해서만 해보자. (차근차근 설명을 남기기 위해 쪼개서 설명하자...)


response = requests.get(target_url) 

soup = BeautifulSoup(response.text, "lxml") 

lis = soup.find_all('li', {'class' : 'expanded-shelf-content-item-wrapper'}) 


먼저 target_url 은 Crawling할 Youtube 주소를 의미 하고, 

첫번째 라인은 target_url 로부터 xml 을 요청하는 부분이고, 이에 대한 반환 값은 response 이다.

두번째 라인은 반환값인  response를 BeautifilSoup 라이브러리를 이용하여 파싱을 하는 부분이다. 파싱결과는 soup이다.

세번째 부분은 파싱된 soup에서 li 속성 중 class가 expanded-shelf-content-item-wrapper 인 부분을 찾아서 리스트로 넣는 부분이다.

리스트는 lis 이다.

이 부분을 이해하기 위해선 xml에 대한 지식이 필요하다...



간단하게 설명을 하자면, 

크롬 브라우저에서 Youtube 인기를 접속하여 개발자모드(F12를 누르면됨)를 들어가보면 다음과 같이 나온다. 익스플로러도 가능하다.




위의 그림과 같이 동영상에 파란색으로 나오게 하려면 xml 정보있는 부분 위에  과 같은 표시를 누르고 

동영상 부분에 마우스를 가지고 가면 해당 부분에 대한 xml 위치를 나타낸다. 여기서 보면 저 부분이 li 속성이고 class가 expanded-shelf-content-item-wrapper 라는 것을 알 수 있다. 이 부분에서 해당 동영상에 대한 정보를 가지고 오는 것이다.





다시 코드설명으로 돌아와서 설명을 해보자.


for li in lis : 

# exception

try : 

title = li.find('a', {'title' : True})['title'] 

video_link = 'https://www.youtube.com' + li.find('a', {'href' : True})['href'] 

img_info = li.find('img', {'data-thumb' : True})

if img_info != None :

img_link = img_info['data-thumb'] 

else : 

img_link = li.find('img', {'src' : True})['src'] 

#<span class="video-time" aria-hidden="true"><span aria-label="8분, 55초">8:55</span></span>

#play_time = li.find('span', {'class' : 'video-time'}).text 

play_time_info = li.find('span', {'class' : 'video-time'})

if play_time_info != None :

play_time = play_time_info.text

else :

play_time = None

#<ul class="yt-lockup-meta-info"><li>조회수 2,902,617회</li><li>6개월 전</li></ul>

hits = li.find_all('li')[3].text 

updated_time = li.find_all('li')[2].text 

description_info = li.find('div',{'class':True, 'class':'yt-lockup-description yt-ui-ellipsis yt-ui-ellipsis-2'})

if description_info != None :

description = description_info.text

else :

description = None


now = datetime.datetime.now()


이 부분은 반복문으로 lis 리스트를 li에 넣고 실질적인 분석을 하는 부분이다. (try 는 혹시 모를 exception을 위해 걸어 놓았다.)

분석은 위에서 간단하게 설명한 xml 분석을 통해서 필요한 정보를 찾고 그 위치를 코드에 입력하는 방식이다.

title 은 동영상의 제목으로, a 속성에 title 정보가 있는 부분에 존재를 한다. 

video_link 는 동영상 페이지로 접속하는 페이지링크로, a 속성에 href 정보가 있는 부분에 존재하며, 공통 도메인인 'https://www.youtube.com'이 빠져 있으므로 직접 추가해줬다.

img_link 는 동영상의 썸네일 이미지정보로, img 속성에 data-thumb 정보가 있다면, data-thumb 에 존재하고, 

data-thumb 정보가 없는 경우, src 정보에 존재한다.

play_time 은 동영상 길이에 대한 정보로, span 속성에 video-time 정보에 존재한다. 이 정보를 존재하지 않는 경우도 있다. (정확한 이유를 아직...)

hits 는 조회 수 정보로, li 속성 중 4번째 위치에 있는 정보이다. (해당 정보안에 li 속성이 여러개 존재하는데, 앞에 3개가 있고 4번째에 조회 수 정보가 존재.)

updated_time 는 업로드 시간 정보로, li 속성 중 3번째 위치에 있는 정보이다.

description 는 동영상 설명에 대한 정보로, div 속성 중 class 정보가 yt-lockup-description yt-ui-ellipsis yt-ui-ellipsis-2 인 곳에 있는 정보이다.

now 는 현재 Crawling 한 시간 정보이다.


video_info = { 

'title' : title, 

'video_link' : video_link, 

'img_link' : img_link, 

'play_time' : play_time, 

'hits' : hits, 

'updated_time' : updated_time,

'description' : description,

'reg_time' : now.strftime('%Y-%m-%d %H:%M:%S')

}

print(video_info) 


except BaseException as e : 

print(e)

 


return video_info 


추출한 정보들을 video_info 에 위와 같이 저장을 하고, 출력을 한다.

추후에 수집한 데이터를 저장하기 위해서는 이 부분에는 데이터를 전송하는 부분이 추가되어야 한다. 

예외가 발생하는 부분이 있으면 except BaseException as e :  으로 빠지고 계속해서 반복문을 수행한다.

반복문 수행이 끝나면 리턴한다.



target_url = 'https://www.youtube.com/user/CJENMMUSIC/videos' 

target_url2 = 'https://www.youtube.com/feed/trending'


# 특정 채널

#get_video_link(target_url)


# 인기 리스트

get_hot_video_info(target_url2)


target_url2 은 Youtube 인기 채널 주소이고, get_hot_video_info(target_url2) 은 함수를 호출하는 부분이다.


코드에 대한 설명은 여기까지 한다.

혹시 몰라서 특정 채널에 대한 함수를 뺀 설명한 부분만 있는 소스코드는 아래와 같다.


# -*- coding: utf-8 -*-

from bs4 import BeautifulSoup 

import lxml 

import requests 

import json 

import datetime

 


video_info = { 'title':'', 'video_link':'', 'img_link':'', 'play_time':'', 'hits' : '', 'updated_time':'', 'description':'', 'reg_time':'' } 


def get_hot_video_info(target_url): 

response = requests.get(target_url) 

soup = BeautifulSoup(response.text, "lxml") 

lis = soup.find_all('li', {'class' : 'expanded-shelf-content-item-wrapper'}) 

for li in lis : 

# exception

try : 

title = li.find('a', {'title' : True})['title'] 

video_link = 'https://www.youtube.com' + li.find('a', {'href' : True})['href'] 

img_info = li.find('img', {'data-thumb' : True})

if img_info != None :

img_link = img_info['data-thumb'] 

else : 

img_link = li.find('img', {'src' : True})['src'] 

#<span class="video-time" aria-hidden="true"><span aria-label="8분, 55초">8:55</span></span>

#play_time = li.find('span', {'class' : 'video-time'}).text 

play_time_info = li.find('span', {'class' : 'video-time'})

if play_time_info != None :

play_time = play_time_info.text

else :

play_time = None

#<ul class="yt-lockup-meta-info"><li>조회수 2,902,617회</li><li>6개월 전</li></ul>

hits = li.find_all('li')[3].text 

updated_time = li.find_all('li')[2].text 

description_info = li.find('div',{'class':True, 'class':'yt-lockup-description yt-ui-ellipsis yt-ui-ellipsis-2'})

if description_info != None :

description = description_info.text

else :

description = None


now = datetime.datetime.now()

video_info = { 

'title' : title, 

'video_link' : video_link, 

'img_link' : img_link, 

'play_time' : play_time, 

'hits' : hits, 

'updated_time' : updated_time,

'description' : description,

'reg_time' : now.strftime('%Y-%m-%d %H:%M:%S')

}

print(video_info) 


except BaseException as e : 

print(e)

 


return video_info 



target_url2 = 'https://www.youtube.com/feed/trending'


# 인기 리스트

get_hot_video_info(target_url2)



Web Crawler는 xml 파싱을 해서 필요한 정보가 어느 위치인지를 분석하고 나면, 금방 만들 수 있을 것 같다.

결국 분석 능력이다....


다음엔 Youtube가 아닌 다른 사이트에 대한 Web Crawling을 해봐야겠다. 


반응형

환경은 Ubuntu 16.04에서 작업함.


우선 예제 코드를 git을 통해 가지고 온다.


git clone https://github.com/firebase/friendlychat



가지고 오면 firendlychat 폴더가 생기고 

이안데 web-start 라는 폴더에 보면 index.html 이 있다.


이 폴더에 firebase 프로젝트 생성시 나오는 스니펫(?) 소스 코드를 넣어 준다.


firebase 프로젝트 생성과 소스코드 가져오는 설명은 아래 링크 참조


https://codelabs.developers.google.com/codelabs/firebase-web/#3


index.html 에 소스코드를 입력할때 다음 순서로 넣는다. 빨간 부분이 넣은 부분이다.

<!-- Firebase -->

<!-- ***********************************************************************************************************************

     * TODO(DEVELOPER): Paste the initialization snippet from: Firebase Console > Overview > Add Firebase to your web app. *

     *********************************************************************************************************************** -->


<script src="https://www.gstatic.com/firebasejs/3.5.3/firebase.js"></script>


<script>

  // Initialize Firebase

  var config = {

    apiKey: "AIzaSyCql1C-ZzCVvSoKfnjdj48Y70THgGdYZ-4",

    authDomain: "austin-gcs.firebaseapp.com",

    databaseURL: "https://austin-gcs.firebaseio.com",

    storageBucket: "austin-gcs.appspot.com",

    messagingSenderId: "41293526285"

  };

  firebase.initializeApp(config);

</script>


<script src="scripts/main.js"></script>

</body>

</html>


index.html 에 소스코드를 입력 후, 저장한 뒤에

npm 명령어를 이용하여 firebase-tools 를 설치한다.


만약 npm 명령어가 안된다면 먼저 설치 후, 위 작업을 한다.


sudo apt install npm

sudo npm -g install firebase-tools

firebase 가 제대로 설치 되었는지 확인한다.


sudo firebase version


현재 설치 했을때의 최신 버전은 3.0.8 이다. 


로그인 명령어를 통해 firebase에 로그인 한다. 


sudo firebase login


현재 구글계정을 이용하여 로그인하기 때문에 허용여부에 대한 웹 창이 뜨고 수락하면 된다.



web-start 폴더에서 아래와 같은 명령어로 현재 로그인한 계정의 프로젝트 별명을 설정할수 있다.

sudo firebase use --add



채팅 프로그램을 실행한다.


sudo firebase serve


실행 후, http://localhost:5000 (이게 기본 로컬 주소)로 접속하면 채팅 화면창이 뜬다.

메시지 입력 후, SEND 버튼을 누르면 Sign in 해야된다는 문구가 뜰 것이다...


sign-in 하는 건 소스코드를 입력해야되는 부분이다.


https://codelabs.developers.google.com/codelabs/firebase-web/#6


위 링크를 참조하여 소스 코드를 입력하여 sign-in 버튼을 활성화시켜서 화면에 나오는지 확인한다.
만약, 나오지 않는다면 확인해야할 경우의 수가 몇가지 있다.
1. index.html 순서 확인
2. 브라우저 쿠키 삭제
3. firebase.json 파일에 아래 내용 입력 후, 재 실행 

{

  "hosting": {
    "public": "./",
    "rewrites": [
      {
        "source": "**",
        "destination": "/index.html"
      }
    ]
  }
}


3번의 경우, 안해도 잘되긴 했다...


기존 데이터 불러오기 및 메시지 전송은 아래 링크에 나와 있는 내용으로 진행하면 문제없이 진행된다.


데이터 불러오기

https://codelabs.developers.google.com/codelabs/firebase-web/#7


메시지 전송

https://codelabs.developers.google.com/codelabs/firebase-web/#9


이정도면... 실시간 채팅 앱이 되긴한다.... 룰 설정과 google 계정 외의 다른 메일로의 연동은 다음에 작성해야겠다.




반응형

Ubuntu 16.04 에서 CUDA 7.5를 설치 하려면 먼저 그래픽 드라이버를 설치해야한다. 

CUDA를 다운로드 받아서 설치할때 드라이버 설치 여부를 물어보긴 하지만,

몇 번 시도해본 결과, 시스템이 꼬여서 원복하는 상황을 맞았기 때문에... 

그래픽 드라이버를 별도로 설치 후, CUDA설치 한다.


그래픽 드라이버 설치 전에 


그래픽 드라이버 설치는 repository 등록 후, update 


sudo add-apt-repository ppa:graphics-drivers/ppa

sudo apt-get update


update 이후 apt-get 으로 그래픽 드라이버 설치 


sudo apt-get install nvidia-352*


버전은 CUDA에 맞는 버전으로 한다. 7.5에서는 위 버전으로 하면 될꺼 같다....


만약 기존에 그래픽드라이버가 설치 되어 있다면, 기존 드라이버를 제거 후에 설치해야된다.


sudo apt-get remove --purge nvidia-*



그래픽 드라이버를 설치 완료 했다면 CUDA를 설치한다. 

CUDA 설치 파일 다운로드 경로는 https://developer.nvidia.com/cuda-downloads 이다.



현재 Nvidia에서 Ubuntu 16.04에 대한 CUDA 버전이 별도로 존재 하지 않아서 Ubuntu 15.04 버전의 run 파일을 다운로드 받는다.

다운로드 받은 후, 설치전에 사전에 설치해야되는 게 있다.


sudo apt-get install libglu1-mesa libxi-dev libxmu-dev libglu1-mesa-dev freeglut3-dev


CUDA를 이용하는데 필요한 라이브러리 인거 같다... 자세한건 추후에 더 알아봐야지...


사전 설치 작업이 끝나면 ALT+CTRL+F1 로 Command Mode로 전환한다.

CUDA 설치 전에 Xorg가 실행 중이면 


sudo init 3

sudo service lightdm stop


위의 명령어로 중지 후 설치를 한다.


cd {CUDA 다운로드한 경로}

sudo sh cuda_7.5.18_linux.run


실행하면 설명화면이 뜨고 q를 누르면 accept여부를 묻는다.

accept를 입력하여 다음으로 넘어가면, 그래픽 드라이버 설치 여부를 묻는데 그건 생략을 하고, 이외의 CUDA와 관련된 설치를 묻는건 모두 y를 입력한다. 

command mode여서 별도의 스크린샷은 안올리는걸로.....(약간의 귀찮음...)


성공적으로 설치하고 나면 


sudo service lightdm start

or

reboot


둘 중 하나로 정상동작을 하게 한다. 개인적으로는 reboot이 편했다는....


CUDA 설치가 끝났으면 예제 하나를 돌려서 제대로 설치가 되었는지 확인해보자.

예제는 nbody로...

기본 경로로 설치를 했다면 /usr/local/cuda 에 설치가 되었을 것이다.

cuda 디렉토리에 보면 samples 디렉토리가 있는데, 이것을 우선 권한을 현재 계정으로 부여하자


sudo chown -R {사용자계정명}:{사용자계정명} /usr/local/cuda/samples/


그리고 CUDA 7.5는 gcc 버전이 4.9이상이면 에러를 보내는데, 이 에러를 무시하기 위해서 config 파일을 고친다.


sudo vi /usr/local/cuda-7.5/include/host_config.h


host_config.h 파일을 열면 아래와 같은 부분을 주석처리 한다.


#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ > 9)


#error -- unsupported GNU version! gcc versions later than 4.9 are not supported!


#endif


그리고 예제로 nbody를 해보기 위해 다음과 같은 순서로 실행해본다.


cd /usr/local/cuda/samples/5_Simulations/nbody

make 

./nbody


정상적으로 작동을 한다면 새로운 창이 뜨고 아래와 같은 화면이 생성된다.




CUDA 7.5 는 정상적으로 설치가 되었으니... 나중에 CuDNN 설치하고, DNN 툴을 설치해서 써봐야겠다.

오늘은 여기까지!



반응형

+ Recent posts