일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- 자연어처리
- 우분투
- 편스토랑 우승상품
- gs25
- Real or Not? NLP with Disaster Tweets
- ChatGPT
- AI 경진대회
- PYTHON
- 금융문자분석경진대회
- Baekjoon
- 데이콘
- 편스토랑
- Kaggle
- dacon
- 프로그래머스 파이썬
- Git
- hackerrank
- 백준
- 맥북
- ubuntu
- 프로그래머스
- 더현대서울 맛집
- leetcode
- 캐치카페
- 코로나19
- github
- programmers
- 파이썬
- SW Expert Academy
- Docker
- Today
- Total
솜씨좋은장씨
[Python] Selenium을 활용하여 인스타그램 크롤링 하기! 본문
이번 글에서는 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
% 2022년 4월 28일 현재 다음 게시물로 넘어가지 않는 문제를 해결하였습니다.
게시물에는 반영하지 않았지만 GitHub에 올려둔 코드는 반영하였습니다.
https://github.com/SOMJANG/Instagram_Crawler/issues/6
% 2022년 7월 31일 인스타그램 페이지에서 변경된 DOM 구조를 반영하였습니다.
https://github.com/SOMJANG/Instagram_Crawler/issues/8
% 전체 코드
1. 필요 라이브러리 설치
먼저 Python과 Selenium을 설치해야 합니다.
설치가 되어있지 않은 분들은 아래의 링크를 참고하여 설치해주세요.
Python 설치 - Windows
Python 설치 - Ubuntu
Selenium 설치
macOS에서 Selenium 사용 시 오류가 날 경우
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_path는 selenium 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. 추출 데이터 활용
4. 전체 코드
5. 참고사항 및 주의사항
주의할 사항은 지연시간을 너무 짧게 하거나 너무 자주하게 되면 id또는 ip가 인스타그램 측에서
크롤링 코드 또는 디도스 공격 같은 것으로 인식하여 차단을 당하므로!
주의해서 활용하기 바랍니다.
또한 개인정보를 수집할 수 있게 되므로 수집한 데이터는 배포시에 비식별화 하여 활용 부탁드리고
이로인하여 발생한 문제는 책임지지 않습니다.
10만개 20만개 추출되지는 않지만 저는 이 코드로 1만개~2만개 사이까지 추출 할 수 있었습니다.
더 많이 추출할 수 있는 방법이 있다면! 댓글로 알려주세요!
읽어주셔서 감사합니다.