안녕하세요 씨앤텍 시스템즈의 김준형 입니다.
이번 포스트는 크롤링에 대해서 정리해 보았습니다.
그중에서도 BeautifulSoup 와 Selenium을 이용해서 인스타그램의 데이터를 받아와 보겠습니다.
셀레니움이란
Selenium은 웹사이트 테스트를 위한 도구로 브라우저 동작을 자동화할 수 있습니다. 프로그래밍으로 브라우저 동작을 제어해서 마치 사람이 이용하는 것 같이 웹페이지를 요청하고 응답을 받아올 수 있습니다. 예를들어 2페이지버튼이 단순 url이아니라 Javascript로 이루어져 있다면 시스템이 동작해서 화면전환이 이루어지기 때문에 크롤링할때 꼭 필요한 기술이라고 할 수 있습니다.
실행환경은 윈도우에서 진행하였습니다.
Python은 설치되어 있고 환경변수를 설정했다고 가정하고 진행하겠습니다.
인스타그램 크롤링은 Selenium과 Chrome을 연결해서 진행하기 때문에 Chrome 드라이버가 필요합니다.
( Chrome버전은 크롬 설정에서 -> Chrome 정보탭으로 가면 크롬 정보에서 확인할 수 있습니다. )
아래 Chrome 홈페이지에서 자신의 크롬버전에 해당하는 드라이버를 다운받아 주세요.
https://chromedriver.chromium.org/downloads
이제 크롬 드라이버 옆에 적당한 이름의 .py 파일을 만들어주세요. (ex. insta.py )
그 후에 다음과 같이 필요한 프레임워크를 추가합니다.
from bs4 import BeautifulSoup
import selenium.webdriver as webdriver
import urllib.parse
from urllib.request import Request, urlopen
from time import sleep
import pandas as pd
import pandas_csv #검색 결과를 .csv파일로 만들기 위한 파이썬 파일
검색어를 사용자에게 받아오고 해당하는 인스타그램 게시물의 정보를 받아오기 위한 소스를 추가합니다.
search = input("검색어를 입력하세요 : " )
searching = str(search)
search = urllib.parse.quote(search)
url = 'https://www.instagram.com/explore/tags/'+str(search)+'/'
driver = webdriver.Chrome('chromedriver.exe')
driver.get(url) #검색어입력한 인스타그램 url 저장
sleep(3) #로딩 시간을 위한 속도조절
SCROLL_PAUSE_TIME = 1.2 #인스타게시물 스크롤 속도 조절 ( 1.0 ~ 2.0까지 사양에 맞게 조절 )
반복문을 통해 가져올 게시물의 개수와 URL 정보를 구합니다.
크롬에서 f12를 눌러 개발자 모드로 진입하여 인스타 검색화면의 소스를 보면 class가 "Nnq7C weEfm"로 해당하는 부분을 찾을 수 있습니다. 이렇게 페이지 하나씩 해당하는 부분을 찾아 데이터를 변수에 저장하고 불러내는 방식입니다.
해당 페이지의 정보를 찾은 후 스크롤을 통해 다음페이지로 넘어갑니다.
reallink = []
while True: # 반복문 시작
pageString = driver.page_source
bs = BeautifulSoup(pageString, "lxml")
# 게시물 정보
for link1 in bs.find_all(name="div",attrs={"class":"Nnq7C weEfm"}):
title = link1.select('a')[0]
real = title.attrs['href']
reallink.append(real)
title = link1.select('a')[1]
real = title.attrs['href']
reallink.append(real)
# 페이지 스크롤
last_height = driver.execute_script("return document.body.scrollHeight")
driver.execute_script("window.scrollTo(0, document.body.scrollHeight);")
#sleep(SCROLL_PAUSE_TIME)
new_height = driver.execute_script("return document.body.scrollHeight")
if new_height == last_height:
driver.execute_script("window.scrollTo(0, document.body.scrollHeight);")
sleep(SCROLL_PAUSE_TIME)
new_height = driver.execute_script("return document.body.scrollHeight")
if new_height == last_height:
break
else:
last_height = new_height
continue
위 반복문에서 구한 Reallink의 개수로 총 데이터의 개수를 파악하고,
Reallink[ ]에 있는 URL을 하나씩 열어서 해당 게시물에 원하는 데이터를 변수에 저장합니다.
이 방식 또한 해당하는 class 명을 찾아내어 소스위치를 파악하여 원하는 데이터를 확인합니다.
hashtags2 = []
reallinknum = len(reallink)
print("총"+str(reallinknum)+"개의 데이터.")
try: # 반복문 시작 ( print 명령어로 원하는 문자열인지 하나씩 확인해보시길 바랍니다.
for i in range(0,reallinknum):
hashtags2.append([])
req = 'https://www.instagram.com/p'+reallink[i]
driver.get(req)
webpage = driver.page_source
soup = BeautifulSoup(webpage, "html.parser")
#print(soup)
soup1 = str(soup.find_all(attrs={'class': 'e1e1d'}))
#print(soup1)
user_id = soup1.split('href="/')[1].split('/">')[0]
#print(user_id)
soup1 = str(soup.find_all(attrs={'class': 'Nm9Fw'}))
#print(soup1)
subValue = 'span'
if(soup1=="[]"): #좋아요가 0개, 1개, n개 일경우 모두 소스가 다르다.
likes = '0'
elif( soup1.find(subValue)==-1):
likes = soup1.split('좋아요 ')[1].split('개')[0]
elif( soup1.find(subValue)!=-1):
likes = soup1.split('<span>')[1].split('</span>')[0]
soup1 = str(soup.find_all(attrs={'class': 'xil3i'}))
if(soup1=="[]"): #해쉬태그가 없을 경우 소스가 다르다.
hashtags = '해쉬태그없음'
insert_data = { "search" : searching,
"user_id" : user_id,
"좋아요" : likes,
"hashtags" : hashtags}
pandas_csv.to_csv(insert_data)
else:
soup2 = soup1.split(',')
soup2num = len(soup2)
for j in range(0,soup2num):
hashtags = soup2[j].split('#')[1].split('</a>')[0]
print(hashtags)
insert_data = { "search" : searching,
"user_id" : user_id,
"좋아요" : likes,
"hashtags" : hashtags}
#insert_data에 저장한 변수들 저장
pandas_csv.to_csv(insert_data)
except:
print("오류발생"+str(i+1)+"개의 데이터를 저장합니다.")
pandas_csv.to_csv(insert_data) #insert_data에 저장한 데이터를 pandas_csv.py로 보냅니다.
print("저장성공")
전체 소스입니다.
from bs4 import BeautifulSoup
import selenium.webdriver as webdriver
import urllib.parse
from urllib.request import Request, urlopen
from time import sleep
import pandas as pd
import pandas_csv #검색 결과를 .csv파일로 만들기 위한 파이썬 파일
search = input("검색어를 입력하세요 : " )
searching = str(search)
search = urllib.parse.quote(search)
url = 'https://www.instagram.com/explore/tags/'+str(search)+'/'
driver = webdriver.Chrome('chromedriver.exe')
driver.get(url) #검색어입력한 인스타그램 url 저장
sleep(3) #로딩 시간을 위한 속도조절
SCROLL_PAUSE_TIME = 1.2 #인스타게시물 스크롤 속도 조절 ( 1.0 ~ 2.0까지 사양에 맞게 조절 )
reallink = []
while True: # 반복문 시작
pageString = driver.page_source
bs = BeautifulSoup(pageString, "lxml")
# 게시물 정보
for link1 in bs.find_all(name="div",attrs={"class":"Nnq7C weEfm"}):
title = link1.select('a')[0]
real = title.attrs['href']
reallink.append(real)
title = link1.select('a')[1]
real = title.attrs['href']
reallink.append(real)
# 페이지 스크롤
last_height = driver.execute_script("return document.body.scrollHeight")
driver.execute_script("window.scrollTo(0, document.body.scrollHeight);")
#sleep(SCROLL_PAUSE_TIME)
new_height = driver.execute_script("return document.body.scrollHeight")
if new_height == last_height:
driver.execute_script("window.scrollTo(0, document.body.scrollHeight);")
sleep(SCROLL_PAUSE_TIME)
new_height = driver.execute_script("return document.body.scrollHeight")
if new_height == last_height:
break
else:
last_height = new_height
continue
hashtags2 = []
reallinknum = len(reallink)
print("총"+str(reallinknum)+"개의 데이터.")
try: # 반복문 시작 ( print 명령어로 원하는 문자열인지 하나씩 확인해보시길 바랍니다.
for i in range(0,reallinknum):
hashtags2.append([])
req = 'https://www.instagram.com/p'+reallink[i]
driver.get(req)
webpage = driver.page_source
soup = BeautifulSoup(webpage, "html.parser")
#print(soup)
soup1 = str(soup.find_all(attrs={'class': 'e1e1d'}))
#print(soup1)
user_id = soup1.split('href="/')[1].split('/">')[0]
#print(user_id)
soup1 = str(soup.find_all(attrs={'class': 'Nm9Fw'}))
#print(soup1)
subValue = 'span'
if(soup1=="[]"): #좋아요가 0개, 1개, n개 일경우 모두 소스가 다르다.
likes = '0'
elif( soup1.find(subValue)==-1):
likes = soup1.split('좋아요 ')[1].split('개')[0]
elif( soup1.find(subValue)!=-1):
likes = soup1.split('<span>')[1].split('</span>')[0]
soup1 = str(soup.find_all(attrs={'class': 'xil3i'}))
if(soup1=="[]"): #해쉬태그가 없을 경우 소스가 다르다.
hashtags = '해쉬태그없음'
insert_data = { "search" : searching,
"user_id" : user_id,
"좋아요" : likes,
"hashtags" : hashtags}
pandas_csv.to_csv(insert_data)
else:
soup2 = soup1.split(',')
soup2num = len(soup2)
for j in range(0,soup2num):
hashtags = soup2[j].split('#')[1].split('</a>')[0]
print(hashtags)
insert_data = { "search" : searching,
"user_id" : user_id,
"좋아요" : likes,
"hashtags" : hashtags}
#insert_data에 저장한 변수들 저장
pandas_csv.to_csv(insert_data)
except:
print("오류발생"+str(i+1)+"개의 데이터를 저장합니다.")
pandas_csv.to_csv(insert_data) #insert_data에 저장한 데이터를 pandas_csv.py로 보냅니다.
print("저장성공")
수집 데이터를 CSV파일로 저장하기위한 pandas_csv.py 파일입니다.
import os
import pandas as pd
from datetime import datetime, timedelta
import configparser
import glob
config = configparser.ConfigParser()
#설정파일이 있는경우 설정합니다.
#config.read('/root/crawling/crawler.conf')
def to_csv(data):
pathlink ="./"
# db create
if not os.path.isdir(pathlink):
os.mkdir(pathlink)
present_date = str(datetime.utcnow() + timedelta(hours=9))[:10] #파일명에 날짜구분하기 위한 시간
# col = ["search", "user_id", "좋아요", "hashtags"]
# CSV파일 생성
if len(glob.glob(pathlink + "/" + present_date + ".csv")) == 1:
cnt = len(pd.read_csv(pathlink + "/" + present_date + ".csv", index_col=0).index)
time_pd = pd.DataFrame(data, index=[cnt])
time_pd.to_csv(pathlink + "/" + present_date + ".csv", mode='a', header=False, encoding='utf-8-sig')
else:
cnt = 0
time_pd = pd.DataFrame(data, index=[cnt])
time_pd.to_csv(pathlink + "/" + present_date + ".csv", mode='a', encoding='utf-8-sig')
--실행화면
-- 수집 명령어 실행 및 로그확인
-- Chrome 페이지가 시작되고 수집을 시작합니다.
-- .py폴더 안에 .csv파일을 확인할 수 있습니다.
지금은 CSV파일로 저장하고 끝나지만 다음 시간에는 ElasticSearch나 RDB로 바로 저장되는 자동화 부분을 배워보도록 하겠습니다.
웹데이터 -> .CSV -> ElasticSearch,RDB
이렇게 데이터가 저장되어 축적되면 빅데이터 관련해서 많은 부분에 사용될 수 있다고 생각합니다.
감사합니다.
'Data > Bigdata' 카테고리의 다른 글
Google Cloud Platform의 BigQuery 소개 (0) | 2021.07.26 |
---|---|
하둡 완전분산 환경 설치 및 설정 (hadoop cluster setup) (1) | 2020.12.11 |
Spark을 이용한 Deeplearning (0) | 2020.06.11 |
Spark SQL(Pyspark) (0) | 2020.05.26 |
Spark DataFrame (PySpark) (0) | 2020.04.20 |