코딩코딩코딩

인스타그램 해시태그 크롤링 및 분석 - 3 본문

파이썬/텍스트마이닝

인스타그램 해시태그 크롤링 및 분석 - 3

hanshow113 2020. 7. 24. 16:49

앞에서 했던 작업들을 모두 함수로 만들어서 작업했습니다.

 

 

# Create Empty List & Parameter Setting

url = 'http://www.instagram.com'
path = 'C:\\Users\\rectworks\\Downloads\\chromedriver_win32\\chromedriver.exe'

SCROLL_PAUSE_TIME = 3.0
ID = 'user_id'
PW = 'password'

post_link = []
popularPost_len = []

id_list = []
like_list = []
tag_list = []
link_list = []
date_list = []
month_list = []
day_list = []
def instagram_login(id, pw):
    driver.get(url)
    driver.implicitly_wait(5)
    driver.find_element_by_name('username').send_keys(id)    # id 입력
    elem_pw = driver.find_element_by_name('password')    # pw 입력
    elem_pw.send_keys(pw)
    elem_pw.submit()
    
    driver.implicitly_wait(5)    # 파싱될 때까지 5초 기다림 (미리 완료되면 waiting 종료됨)
    driver.find_element_by_class_name('cmbtv').click()    # 비밀번호 저장하지 않음
    
    driver.implicitly_wait(5)
    driver.find_element_by_xpath('/html/body/div[4]/div/div/div/div[3]/button[2]').click()    # 알림설정 무시
def main_search(keyword):
    search = driver.find_element_by_xpath('//*[@id="react-root"]/section/nav/div[2]/div/div/div[2]/input')
    search.clear()
    search.send_keys(keyword)
    search_list1 = driver.find_element_by_xpath('//*[@id="react-root"]/section/nav/div[2]/div/div/div[2]/div[3]/div[2]/div/a[1]')
    search_list1.click()​
def scroll_page():
    post_link.clear()
    popularPost_len.clear()
    while True:
        pageString = driver.page_source   # page_source : 현재 렌더링된 페이지의 Elements를 모두 가져옴
        bsObj = bs(pageString, 'lxml')

        for postline in bsObj.find_all(name='div', attrs={"class":"Nnq7C weEfm"}):
            a_len = len(postline.select('a'))
            popularPost_len.append(a_len)
            # 인스타그램 게시물은 행별로 최대 3개까지 확인할 수 있는데, 최근게시물이나 마지막 게시물은 1,2개가 나올 수도 있어서 len 지정
            for post in range(a_len):   
                item = postline.select('a')[post]
                link = item.attrs['href']
                if link not in post_link:   # 스크롤을 내리고 중복된 것을 제거하지 않고 누적시키기 때문에 없는 것만 추가
                    post_link.append(link)

        last_height = driver.execute_script('return document.body.scrollHeight')   # 자바스크림트로 스크롤 길이를 넘겨줌
        driver.execute_script("window.scrollTo(0, document.body.scrollHeight);")   # selenium에서 scroll 기능 사용
        time.sleep(SCROLL_PAUSE_TIME)
        # 프로세스 자체를 지정시간동안 기다려줌(무조건 지연)
        #driver.implicitly_wait(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);")
            time.sleep(SCROLL_PAUSE_TIME)
            #driver.implicitly_wait(SCROLL_PAUSE_TIME)
            new_height = driver.execute_script("return document.body.scrollHeight")

            if new_height == last_height:
                break
            else:
                last_height = new_height
                continue
def popularPostSum():
    popularPost = 0
    for i in range(3):
        popularPost += popularPost_len[i]
    return popularPost

# 인기게시물은 시간순으로 나타나는 것이 아니므로
# 여기에 나타나는 항목들은 시간순으로 데이터프레임 만다는 데서 제외시키기 위한 함수​
def date_based_crawling(std_date):
    id_list.clear()
    like_list.clear()
    tag_list.clear()
    link_list.clear()
    date_list.clear()
    month_list.clear()
    day_list.clear()
    
    
    post_linkx = post_link[popularPostSum():]    # 스크롤 내리면서 모은 데이터 중 인기 게시물은 제외시킴
    num_of_postx = len(post_linkx)
    
    date_string = std_date
    date =datetime.strptime(date_string, "%Y.%m.%d")
    time_tuple = date.timetuple()
    std_timestamp = int(time.mktime(time_tuple))
    # 기준 날짜를 지나면 크롤링이 멈추도록 작동
    
    for i in tqdm_notebook(range(num_of_postx)):
        req = Request("https://www.instagram.com" + post_linkx[i], headers={'User-Agent': 'Mozila/5.0'})
        postpage = urlopen(req).read()
        
        req_next = Request("https://www.instagram.com" + post_linkx[i+1], headers={'User-Agent': 'Mozila/5.0'})
        postpage_next = urlopen(req_next).read()
        
        post_body = bs(postpage, 'lxml', from_encoding='utf-8')
        post_core = post_body.find('meta', attrs={'property':"og:description"})
        contents = post_core['content']

        # break할 시간 비교
        posttxt = str(postpage)    
        timestamp = int(posttxt[posttxt.find('taken_at_timestamp')+20 : posttxt.find('taken_at_timestamp')+30])
        
        posttxt_next = str(postpage_next)    
        timestamp_next = int(posttxt_next[posttxt_next.find('taken_at_timestamp')+20 : posttxt_next.find('taken_at_timestamp')+30])
        
        
        if (std_timestamp > timestamp) \
        and (datetime.fromtimestamp(timestamp).strftime('%Y/%m/%d %H:%M'))\
        == (datetime.fromtimestamp(timestamp_next).strftime('%Y/%m/%d %H:%M')): 
            break
        # 시간
        date_list.append(datetime.fromtimestamp(timestamp).strftime('%Y/%m/%d %H:%M'))
        month_list.append(datetime.fromtimestamp(timestamp).strftime("%m"))
        day_list.append(datetime.fromtimestamp(timestamp).strftime("%d"))

        # 개별 링크 리스트 
        link_list.append("https://www.instagram.com" + post_linkx[i])   
        
        # 좋아요
        try:
            likes = int(contents[: contents.find(' Likes, ')])  # Likes 문자열 앞에 있는 좋아요 개수 추출
        except:
            likes=0  # 좋아요 가 아니라 조회수로 표시되는 경우도 있어 이런 경우는 0으로 표시
        like_list.append(likes)

        # 개별 계정
        if "@" and ")" in contents:
            personal_id = contents[contents.find("@")+1 : contents.find(")")]
        elif "@" and ")" not in contents and "on Instagram" in contents:
            personal_id = contents[contents.find("@")+1 : contents.find('on Instagram')]
        else :
            personal_id = contents[1: contents.find(' posted on')]
        id_list.append(personal_id)

        '''    
        (@personal_id) on instagram, @persoanlid posted on instagram, personal_id on instgram 등의 형태로 meta 데이터에 표시되기
        때문에 여러 형식별 id 추출 if문 수행
        '''

        # 해시태그
        tag_list.append([])
        for tag_content in post_body.find_all('meta', attrs={'property':"instapp:hashtags"}):
            hashtags = tag_content['content'].rstrip(',')
            tag_list[i].append(hashtags)

 

코드 중간에 if, break문을 써놓은 부분은 게시물을 크롤링해오던 중 중간에 갑자기 한 달 전 게시물이 뜬금없이 들어가있어서 뒤 게시물과 날짜가 같으면 break하도록 작성했습니다. 대부분의 게시글은 분,시간 단위로 올라와서 날짜로 비교해도 상관없을 것 같습니다.

다만 이렇게 날짜를 비교하는 코드를 넣으니 속도가 너무 느려져셔 개선해야 할 것 같습니다.

 

 

Comments