관리 메뉴

솜씨좋은장씨

[Python] Selenium을 활용하여 인스타그램 크롤링 하기! 본문

Programming/Python

[Python] Selenium을 활용하여 인스타그램 크롤링 하기!

솜씨좋은장씨 2021. 4. 25. 05:32
728x90
반응형

이번 글에서는 Python과 Selenium을 활용하여 인스타그램 게시물을 크롤링하는 방법에 대해서 적어보려 합니다.

해당 코드를 활용하여 발생하는 문제는 코드 사용자에게 있음을 알려드립니다.

 

% 현재 아래의 방법을 활용한다고 하여 게시물을 무한으로 크롤링 할 수 있지는 않습니다.

개인 PC사양, 네트워크 환경에 따라서 같은 코드여도 크롤링 할 수 있는 게시물의 개수가 달라짐을 알려드립니다.

제 환경 ( MacBook Pro 2017 13인치 - 4 Thunderbolt Ports 8GB + 500Mbps 인터넷 ) 에서는

약 10,000개 정도 까지 가능했습니다.

dmkym 님 댓글 남겨주셔서 감사합니다~👍

 

% 2021년 7월 기준

인스타그램 측에서 사람이 손으로 게시물을 클릭하여 접근하여도 일정 게시물 이상 (약 100~300개) 접근하게 되면 

위와 같이 페이지를 더이상 사용할 수 없다는 화면이 나오면서

더이상 크롤링이 불가합니다. 

 

추후 다른 방법을 찾게 되면 추가 업데이트를 진행하도록 하겠습니다.

감사합니다.

 

% 현재 페이스북 로그인 부분에서 문제가 있어 제대로 동작하지 않을 수 있습니다.

메일 주신 분들 답변이 늦어져 죄송합니다.

수정이 완료되면 메일 답변 및 포스팅 업데이트를 진행하려고 합니다.

감사합니다!

 

% 2021년 7월 12일 현재 로그인 불가 문제, 댓글을 크롤링 했을때 한글이 깨지는 문제 해결하였습니다.

+ 페이스북 로그인, 인스타그램 로그인 두 가지 방법 중에 선택한 방법으로 로그인이 가능하게 되었습니다.

업데이트 하여 올려둔 코드는 아래의 링크를 참고해주세요.

메일 주셨던 분들 한분 한분 차례대로 답변드리겠습니다!

기다려 주셔서 감사합니다.

 

% 2022년 2월 5일 현재 다음 게시물로 넘어가지 않는 문제를 해결하였습니다.

게시물에는 반영하지 않았지만 GitHub에 올려둔 코드는 반영하였습니다.

https://github.com/SOMJANG/Instagram_Crawler/issues/4

 

다음 게시물로 넘어가지 않는 문제 · Issue #4 · SOMJANG/Instagram_Crawler

2022년 2월 3일 메일 문의 다음 게시물로 넘어가지 않는다 100초를 기다려도 넘어가지 않고 종료된다

github.com

% 2022년 4월 28일 현재 다음 게시물로 넘어가지 않는 문제를 해결하였습니다.

게시물에는 반영하지 않았지만 GitHub에 올려둔 코드는 반영하였습니다.

https://github.com/SOMJANG/Instagram_Crawler/issues/6

 

인스타그램 태그 이외에 추출이 되지 않는 문제 · Issue #6 · SOMJANG/Instagram_Crawler

2022년 4월 26일 블로그 댓글 질문 nstagram_tag 내용은 잘 뽑히나, instagram_extract가 내용이 하나도 안 뽑히는 문제 출처: https://somjang.tistory.com/692#comment6422695 [솜씨좋은장씨]

github.com

% 2022년 7월 31일 인스타그램 페이지에서 변경된 DOM 구조를 반영하였습니다.

https://github.com/SOMJANG/Instagram_Crawler/issues/8

 

인스타그램 구조 변경 반영 필요 · Issue #8 · SOMJANG/Instagram_Crawler

블로그 댓글 중 에러가 발생한다는 댓글을 확인 #7 확인 결과 인스타그램 페이지의 구조가 변경된 것을 알게됨

github.com

% 전체 코드

 

GitHub - SOMJANG/Instagram_Crawler: 인스타그램 크롤러 (Python, Selenium)

인스타그램 크롤러 (Python, Selenium). Contribute to SOMJANG/Instagram_Crawler development by creating an account on GitHub.

github.com

1. 필요 라이브러리 설치

먼저 Python과 Selenium을 설치해야 합니다.

설치가 되어있지 않은 분들은 아래의 링크를 참고하여 설치해주세요.

 

Python 설치 - Windows

 

[Windows] Windows에 Python 3.7.3 설치하기!

1. Python 3.7.3 설치파일 다운로드 Welcome to Python.org The official home of the Python Programming Language www.python.org 먼저 Python 홈페이지로 이동합니다. Download 메뉴를 클릭하여 Download 페..

somjang.tistory.com

Python 설치 - Ubuntu

 

[Python]Ubuntu에 Python 3.7 설치하기!

1. Python 설치 전 라이브러리 설치하기 Ubuntu(또는 Putty)에서 터미널을 열어 아래의 코드를 입력합니다. 설치 중간 중간에 [ y | n ] 중에 고르라고 나오면 y를 타이핑하고 엔터를 해주시면 됩니다! $ s

somjang.tistory.com

Selenium 설치

 

[Windows]Windows10에 Selenium설치하기(20.2.13 업데이트)

1. 구글 크롬 최신으로 업데이트하기 먼저 크롬의 맨 우측 상단의 세 개의 점을 클릭하여 크롬의 설정페이지로 들어갑니다. 왼쪽 메뉴에서 Chrome 정보를 클릭하여 업데이트를 실시합니다. 다시시

somjang.tistory.com

macOS에서 Selenium 사용 시 오류가 날 경우

 

[MAC OSX] 개발자를 확인할 수 없기 때문에 'chromedriver'을(를) 열 수 없습니다. 해결 방법

인스타 그램 크롤링을 진행하기 위하여 오랜만에 Selenium으로 작성하여 사용하였던 코드를 가져와 크롤링을 시작하려고 하니 --------------------------------------------------------------------------- Web..

somjang.tistory.com

2. 코드 작성

2-1. 필요 라이브러리 import

from selenium import webdriver as wd
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
import time
import re
import json
import pandas as pd

 

크롤링을 위해 필요한 selenium

크롤링 중간 중간 시간 지연을 두기 위한 time

크롤링한 내용 중 정규식으로 전처리 하기 위한 re

댓글, 대댓글을 저장할때 json 화 시키기 위한 json

데이터를 최종적으로 csv로 저장하기 위한 pandas 

이렇게 필요한 라이브러리를 import 해줍니다.

2-2. 인스타그램 로그인 코드 ( 2021.07.12 수정 )

user_id="id", user_passwd="passwd",
login_option="facebook", # facebook or instagram
driver_path="~/chromedriver",
instagram_id_name="username", instagram_pw_name="password",
instagram_login_btn=".sqdOP.L3NKy.y3zKF     ",
facebook_login_page_css=".sqdOP.L3NKy.y3zKF     ",
facebook_login_page_css2=".sqdOP.yWX7d.y3zKF     ", 
facebook_id_form_name="email",
facebook_pw_form_name="pass",
facebook_login_btn_name="login",

인스타그램 크롤링을 하기위해서는 먼저 로그인을 해야합니다.

이번에 코드를 수정하면서 페이스북 계정 로그인, 인스타그램 계정 로그인

두 가지 방식 중 선택하는 방식으로 로그인이 가능하도록 하였습니다.

 

위의 내용은 로그인에 필요한 정보입니다. 자세한 내용은 아래의 표를 참고하세요.

구분 내용 비고
user_id 인스타그램 or 페이스북 아이디  
user_passwd 인스타그램 or 페이스북 비밀번호  
login_option 로그인 옵션 facebook or instagram
driver_path selenium chrome driver 경로  
instagram_id_name 인스타그램 아이디 입력 form 의 name  
instagram_pw_name 인스타그램 비밀번호 입력 form 의 name  
instagram_login_btn 인스타그램 로그인 버튼의 class 명  
facebook_login_page_css 페이스북 로그인 진입 버튼 class 명 1  
facebook_login_page_css2 페이스북 로그인 진입 버튼 class 명 2 때에 따라 다른 화면에 대응하기 위해 2개
facebook_id_form_name 페이스북 아이디 입력 form 의 name  
facebook_pw_form_name 페이스북 비밀번호 입력 form 의 name  
facebook_login_btn_name 페이스북 로그인 버튼의 name  

또 기존에 특정 해쉬태그의 게시물이 있는 페이지로 이동한 다음에 로그인 버튼을 눌러 로그인 화면으로 진입하던 방식에서

먼저 url을 통해 로그인 화면으로 바로 이동한 뒤 로그인을 진행하도록 하였습니다.

print(f"login start - option {login_option}")

login_url = "https://www.instagram.com/accounts/login/"
driver.get(login_url)
time.sleep(10)

가. 인스타그램 계정 로그인

if login_option == "instagram":
	try:
    	instagram_id_form = driver.find_element_by_name(instagram_id_name)
        instagram_id_form.send_keys(user_id)
        time.sleep(5)

        instagram_pw_form = driver.find_element_by_name(instagram_pw_name)
        instagram_pw_form.send_keys(user_passwd)
        time.sleep(7)
          
        login_ok_button = driver.find_element_by_css_selector(instagram_login_btn)
        login_ok_button.click()
        is_login_success = True
    except:
        print("instagram login fail")
    	is_login_success = False

인스타그램 계정 로그인은 이동한 로그인 페이지에서

바로 input_form을 찾아 아이디와 비밀번호를 입력한 뒤 로그인 버튼을 찾아 클릭하면 되어 

form을 찾는 것은 find_element_by_name을 활용하여 찾았고

버튼은 find_element_by_css_selector를 활용하여 버튼의 class명으로 찾았습니다.

 

나. 페이스북 계정 로그인

elif login_option == "facebook":
	is_facebook_btn_click = False
    try:
    	print("try click facebook login button 1")
        facebook_login_btn = driver.find_element_by_css_selector(facebook_login_page_css)
        time.sleep(5)
        facebook_login_btn.click()
        is_facebook_btn_click = True
        is_login_success = True
    except:
    	print("click facebook login button 1 fail")
        is_facebook_btn_click = False
        is_login_success = False
        
    time.sleep(10)
        
    if not is_facebook_btn_click:
    	print("try click facebook login button 2")
        try:
        	facebook_login_btn = driver.find_element_by_css_selector(facebook_login_page_css2)
            time.sleep(5)
            facebook_login_btn.click()
            is_facebook_btn_click = True
            is_login_success = True
        except:
        	print("click facebook login button 2 fail")
            is_login_success = False
                
    time.sleep(10)

 

먼저 Facebook으로 로그인 버튼을 클릭하여 Facebook로그인 화면으로 이동합니다.

종종 다른 화면이 등장하기도 하여 두가지 방법으로 시도하도록 하였습니다.

진입한 이후에는 아이디와 비밀번호를 input form에 입력하고 로그인을 실시합니다.

2-2. 인스타 그램 로그인 코드 ( 수정 전 )

먼저 로그인을 해야만 게시물에 접근이 가능하기 때문에 인스타그램 로그인하는 코드를 작성해야합니다.

로그인은 Facebook id를 통해 했습니다.

추후 업데이트를 통해 인스타그램 아이디로 로그인하는 것도 구현할 예정입니다.

user_id="facebook_id"
user_passwd="facebook_pw"

driver_path="./chromedriver", keyword="피프틴"
url = "https://www.instagram.com/explore/tags/{}/".format(keyword)
facebook_login_page_css=".sqdOP.yWX7d.y3zKF     "
facebook_id_form_id="email"
facebook_pw_form_id="pass"
facebook_login_btn_css="loginbutton"

먼저 크롤링에 필요한 값들을 변수로 설정합니다.

driver_pathselenium chromedriver의 경로

user_id, user_passwd 는 로그인을 위한 페이스북 아이디, 비밀번호
url크롤링을 희망하는 키워드의 게시물 주소

facebook_login_page_css페이스북 로그인 페이지로 이동하는 버튼 태그의 class명

facebook_id_form_id와 facebook_pw_form_id아이디와 비밀번호를 입력할 form의 id

facebook_login_btn_css로그인 버튼의 id 입니다.

driver = wd.Chrome(driver_path)
driver.get(url)
time.sleep(10)

selenium webdriver로 url을 열고 10초를 기다립니다.

그럼 위의 화면에서 10초를 기다렸다가

Facebook의 아이디와 비밀번호를 활용하여 로그인 하기 위해서 Facebook으로 로그인 버튼을 클릭합니다.

print("login start")

facebook_login_btn = driver.find_element_by_css_selector(facebook_login_page_css)

facebook_login_btn.click()

time.sleep(10)

그럼 위와 같은 화면이 나옵니다.

id_input_form = driver.find_element_by_id(facebook_id_form_id)

pw_input_form = driver.find_element_by_id(facebook_pw_form_id)

id_input_form.send_keys(user_id)
pw_input_form.send_keys(user_passwd)

time.sleep(10)

login_btn = driver.find_element_by_id(facebook_login_btn_css)

login_btn.click()

아이디 form에 아이디를 입력하고 비밀번호 form에 비밀번호를 입력한 뒤에 로그인 버튼을 클릭합니다.

그럼 위와 같이 로그인이 완료됩니다.

time.sleep(10)

driver.get(url)

time.sleep(10)

2-3. 인스타그램 게시물 크롤링 코드

앞 뒤로 10초씩 지연시간을 두고 다시 키워드에 대한 게시물이 있는 주소로 이동합니다.

 

# 첫번째 게시물
first_img_css="div.v1Nh3.kIKUG._bz0w"
driver.find_element_by_css_selector(first_img_css).click()

그 다음 첫번째 게시물을 클릭하도록 합니다.

# data lists
location_infos = []
location_hrefs = []

upload_ids = []

date_texts = []
date_times = []
date_titles = []

main_texts = []

instagram_tags = []

comments = []

check_arrow = True

count_extract = 0

wish_num = 10

instagram_tags = []
instagram_tag_dates = []

이제 크롤링 한 데이터를 저장할 변수들을 선언합니다.

몇 가지만 설명 드리면

check_arrow다음 게시물 버튼이 있는 지 없는 지 여부를 체크하는 bool 변수

count_extract현재 몇 개의 게시물을 추출했는지 체크하는 int 변수

wish_num최종적으로 몇 개의 게시물을 추출할 것인지에 대한 변수 입니다.

location_object_css="div.o-MQd.z8cbW > div.M30cS > div.JF9hh > a.O4GlU"
upload_id_object_css="div.e1e1d > span.Jv7Aj.MqpiF  > a.sqdOP.yWX7d._8A5w5.ZIAjV "
date_object_css="div.k_Q0X.NnvRN > a.c-Yi7 > time._1o9PC.Nzb55"
main_text_object_css="div.C7I1f.X7jCj > div.C4VMK > span"
tag_css=".C7I1f.X7jCj"
comment_more_btn="button.dCJp8.afkep"
comment_ids_objects_css="ul.Mr508 > div.ZyFrc > li.gElp9.rUo9f > div.P9YgZ > div.C7I1f > div.C4VMK > h3"
comment_texts_objects_css="ul.Mr508 > div.ZyFrc > li.gElp9.rUo9f > div.P9YgZ > div.C7I1f > div.C4VMK > span"
print_flag=False
next_arrow_btn_css1="._65Bje.coreSpriteRightPaginationArrow"
next_arrow_btn_css2="._65Bje.coreSpriteRightPaginationArrow"

while True:
    if count_extract > wish_num:
        break
    time.sleep(5.0)
    # 위치정보

    if check_arrow == False:
        break

    try:
        location_object = driver.find_element_by_css_selector(location_object_css)
        location_info = location_object.text
        location_href = location_object.get_attribute("href")
    except:
        location_info = None
        location_href = None

    # 올린사람 ID
    try:
        upload_id_object = driver.find_element_by_css_selector(upload_id_object_css)
        upload_id = upload_id_object.text
    except:
        upload_id = None


    # 날짜
    try:
        date_object = driver.find_element_by_css_selector(date_object_css)
        date_text = date_object.text
        date_time = date_object.get_attribute("datetime")
        date_title = date_object.get_attribute("title")
    except:
        date_text = None
        date_time = None
        date_title = None


    # 본문
    try:
        main_text_object = driver.find_element_by_css_selector(main_text_object_css)
        main_text = main_text_object.text
    except:
        main_text = None


    ## 본문 속 태그
    try:
        data = driver.find_element_by_css_selector(tag_css) # C7I1f X7jCj
        tag_raw = data.text
        tags = re.findall('#[A-Za-z0-9가-힣]+', tag_raw) 
        tag = ''.join(tags).replace("#"," ") # "#" 제거

        tag_data = tag.split()

        for tag_one in tag_data:
            instagram_tags.append(tag_one)
    except:
        continue


    # 댓글
    ## 더보기 버튼 클릭
    try:
        while True:
            try:
                more_btn = driver.find_element_by_css_selector(comment_more_btn)
                more_btn.click()
            except:
                break
    except:
        print("----------------------fail to click more btn----------------------------------")
        continue

    ## 댓글 데이터
    try:
        comment_data = {}

        comment_ids_objects = driver.find_elements_by_css_selector(comment_ids_objects_css)

        comment_texts_objects = driver.find_elements_by_css_selector(comment_texts_objects_css)



        try:
            for i in range(len(comment_ids_objects)):
                comment_data[str((i+1))] = {"comment_id":comment_ids_objects[i].text, "comment_text":comment_texts_objects[i].text} 
        except:
            print("fail")



    except:
        comment_id = None
        comment_text = None
        comment_data = {}


    try:
        if comment_data != {}:
            keys = list(comment_data.keys())

            for key in keys:
                if comment_data[key]['comment_id'] == upload_id:
                    tags = re.findall('#[A-Za-z0-9가-힣]+', comment_data[key]['comment_text']) 
                    tag = ''.join(tags).replace("#"," ") # "#" 제거

                    tag_data = tag.split()

                    for tag_one in tag_data:
                        instagram_tags.append(tag_one)
    except:
        continue



    location_infos.append(location_info)
    location_hrefs.append(location_href)

    upload_ids.append(upload_id)

    date_texts.append(date_text)
    date_times.append(date_time)
    date_titles.append(date_title)

    main_texts.append(main_text)

    comment_json = json.dumps(comment_data)

    comments.append(comment_json)

    if print_flag:
        print("location_info : ", location_info)
        print("location_href : ", location_href)
        print("upload id : ", upload_id)
        print("date : {} {} {}".format(date_text, date_time, date_title))
        print("main : ", main_text)
        print("comment : ", comment_data)

        print("insta tags : ", instagram_tags)

    try:
        WebDriverWait(driver, 100).until(EC.presence_of_element_located((By.CSS_SELECTOR, next_arrow_btn_css1)))
        driver.find_element_by_css_selector(next_arrow_btn_css2).click()
    except:
        check_arrow = False

    count_extract += 1

위치정보, 게시한 사람 ID, 날짜, 본문, 본문 속 태그, 댓글 정보를 추출합니다.

print_flag를 True로 주면 추출하는 정보를 실시간으로 출력합니다.

게시물 하나 추출할 때마다 count_extract를 1씩 증가시키고

wish_num 보다 커질 경우 추출을 멈춥니다.

2-4. 추출한 정보 저장

save_file_name="instagram_extract"
save_file_name_tag="instagram_tag"

try:
    insta_info_df = pd.DataFrame({"location_info":location_infos, "location_href":location_hrefs, "upload_id":upload_ids, "date_text":date_texts, "date_time":date_times, "date_title":date_titles, "main_text":main_texts, "comment":comments})
    insta_info_df.to_csv("{}.csv".format(save_file_name), index=False)
except:
    print("fail to save data")


try:
    insta_tag_df = pd.DataFrame({"tag":instagram_tags})
    insta_tag_df.to_csv("{}.csv".format(save_file_name_tag), index=False)
except:
    print("fail to save tag data")

driver.close()
driver.quit()

추출이 끝나면 지금까지 추출한 정보를 저장하고 Selenium Webdriver를 종료합니다.

 

저장한 정보는 위와 같이 저장됩니다.

 

3. 추출 데이터 활용

 

"피프틴" 태그가 포함된 인스타그램 게시물 개수 추이와 실제 사용량 비교해보기

1. 필요한 라이브러리 import 하기 import pandas as pd from konlpy.tag import Mecab import matplotlib.pyplot as plt import matplotlib from tqdm import tqdm from collections import Counter from PIL im..

ideans.tistory.com

 

[Python] 인스타그램 태그를 가져와 워드클라우드 만들기!

1. 주제를 선택한 계기 특정 프랜차이즈에 관련된 최근 키워드를 알려주려면 어떤 것을 참고하면 좋을까 생각하다가 인스타그램에 걸려있는 특정 주제에 대한 여러 태그들을 크롤링하여 그 태

somjang.tistory.com

 

[Python]인스타그램 크롤링을 통해 #흑당버블티 분석해보기!

요즘 들어서 대만카스테라, 벌집아이스크림 때처럼 흑당 버블티 가게가 우후 죽순 생겨나고 있습니다. 인스타그램 태그를 활용하면 흑당버블티에 대한 동향을 알 수 있을까라는 의문점이 들었

somjang.tistory.com

4. 전체 코드

 

SOMJANG/Instagram_Crawler

인스타그램 크롤러 (Python, Selenium). Contribute to SOMJANG/Instagram_Crawler development by creating an account on GitHub.

github.com

 

5. 참고사항 및 주의사항

주의할 사항은 지연시간을 너무 짧게 하거나 너무 자주하게 되면 id또는 ip가 인스타그램 측에서

크롤링 코드 또는 디도스 공격 같은 것으로 인식하여 차단을 당하므로!

주의해서 활용하기 바랍니다.

 

또한 개인정보를 수집할 수 있게 되므로 수집한 데이터는 배포시에 비식별화 하여 활용 부탁드리고

이로인하여 발생한 문제는 책임지지 않습니다.

 

10만개 20만개 추출되지는 않지만 저는 이 코드로 1만개~2만개 사이까지 추출 할 수 있었습니다.

더 많이 추출할 수 있는 방법이 있다면! 댓글로 알려주세요!

읽어주셔서 감사합니다.

Comments