Instagramハッシュタグ検索の結果をスクレイピングする【Python】

Instagramハッシュタグ検索の結果をスクレイピングする【Python】 プログラミング

Instagramハッシュタグ検索の結果をスクレイピングしていきます。
ただ、それなりの難易度です。

Pythonはもちろん、Seleniumにも精通しておく必要があります。
でも、安心してください。

コピペで動くサンプルコードを載せています。
これを使えば、ハッシュタグ検索の結果を一気に取得できます。

本記事の内容

  • Instagramのスクレイピングについて
  • ハッシュタグ検索結果のスクレイピング仕様
  • ハッシュタグ検索結果をスクレイピングするサンプルコード

それでは、上記に沿って解説していきます。

Instagramのスクレイピングについて

次の記事をご覧ください。
Instagramのスクレイピングの可否などについて説明しています。

スクレイピングは、問題のある技術だと言われることがあります。
でも、上の記事内ではそれを真っ向から否定しています。

スクレイピングに関して、経験や知識のない方は是非とも上記記事を事前にご覧ください。
そして、その内容を理解してください。

おそらく、そうしないとこれ以降の説明も理解できないかもしれません。
「急いでスクレイピングをしたい」と言う方は、注意事項に気を付けてサンプルコードを利用してください。

ハッシュタグ検索結果のスクレイピング仕様

今回は、「それ以降の10件」を取得します。
つまり、スクロールして初めて表示される投稿のことです。
詳細を知りたい場合は、上記参考記事をご覧ください。

やはり、InstagramはTwitterと同じようにスクレイピング対策を熱心にしてきています。
そのため、Seleniumの知識が必要です。

PythonからSeleniumを使って、スクロールをするのです。
そうすることにより、「それ以降の10件」が表示されます。

ここまでをまとめます。

例えば、「#家系ラーメン」でハッシュタグ検索を実施したとします。
総数が280709件(2020年09月20日時点)です。

この時点では、画面内(htmlソース)には33件(9件ぐらいしか実際には見えない)の投稿(htmlタグ)が表示されています。
そして、それ以降の投稿を取得するためにスクロールします。

約4行、12件の投稿数分だけのスクロールとなります。
この時点で33件+12件で、45件の投稿データを取得できます。

あとは、これを繰り返すだけです。
基本的には、1スクロールで12個のデータが追加されていきます。

以上でスクレイピング仕様は終わりです。
ここで説明したスクロールを伴うスクレピングを次の記事でも解説しています。

ハッシュタグ検索結果をスクレイピングするサンプルコード

コピペで動くサンプルコードです。
「#家系ラーメン」でハッシュタグ検索した結果をスクレイピングするコードです。
2020年09月20日時点では、モリモリと動いています。

from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By
from selenium.webdriver.common.action_chains import ActionChains
import urllib.parse
import re
import bs4
import json
import time

INSTAGRAM_DOMAIN = "https://www.instagram.com"
MIN_COUNT = 100
KEYWORD = "家系ラーメン"
CHROMEDRIVER = "chromedriver.exeのパス"

# driver取得
def get_driver():
     
    # ヘッドレスモードでブラウザを起動
    options = Options()
    #options.add_argument('--headless')
    # ブラウザーを起動
    driver = webdriver.Chrome(CHROMEDRIVER, options=options)
     
    return driver

# 対象ページ取得
def get_text_from_target_page(driver, first_flg, url):
     
    # ターゲット
    driver.get(url)
    
    if first_flg:
        # articleタグが読み込まれるまで待機(最大10秒)
        WebDriverWait(driver, 10).until(EC.visibility_of_element_located((By.TAG_NAME, 'article')))
    else:
        driver.implicitly_wait(10)  # 見つからないときは、10秒まで待つ
     
    text = driver.page_source
         
    return text

# 正規表現で値を抽出
def get_search_value(ptn, str):
     
    result = re.search(ptn, str)
     
    if result:
        return result.group(1)
    else:
        return None
    
# info取得
def get_info_from_text(text):
    soup = bs4.BeautifulSoup(text, features='lxml')
     
    try:
        info = {}
        # 投稿(v1Nh3 kIKUG  _bz0w)
        elems = soup.find_all(class_="v1Nh3")
        
        for elem in elems:
            a_elem = elem.find("a")
            href = a_elem["href"]    
            url = INSTAGRAM_DOMAIN + href
            post_id = get_search_value("\/p\/(.*)\/", href)
            
            # img情報
            img_elem = elem.find("img")
            alt = img_elem["alt"]
            src = img_elem["src"]
            
            post_dic = {}
            post_dic["url"] = url
            post_dic["alt"] = alt
            post_dic["src"] = src
            
            info[post_id] = post_dic
             
        return info
        
    except:
        return None
    
# 最後の要素までスクロール
def scroll_to_elem(driver, footer_move_flg):
    
    try:
        if footer_move_flg:
            last_elem = driver.find_element_by_id("fb-root")   


            actions = ActionChains(driver);
            actions.move_to_element(last_elem);
            actions.perform();
        else:
            # 最後の要素の一つ前までスクロール
            elems = driver.find_elements_by_class_name("weEfm")
            last_elem = elems[-1]
             
            actions = ActionChains(driver);
            actions.move_to_element(last_elem);
            actions.perform();
         
        return True
    except:
        return False

# 投稿件数取得
def get_post_count(text):
    try:
        json_str = get_search_value("window._sharedData = (.*);<\/script>", text)
        dict = json.loads(json_str)
        post_count = dict["entry_data"]["TagPage"][0]["graphql"]["hashtag"]["edge_hashtag_to_media"]["count"]
        return post_count
    except:
        return MIN_COUNT
    
if __name__ == '__main__':
    
    # url
    url = "https://www.instagram.com/explore/tags/" + urllib.parse.quote(KEYWORD) + "/"

    # ブラウザーを起動
    driver = get_driver()

    # 対象ページのhtmlソース取得
    text_0 = get_text_from_target_page(driver, True, url)
    post_count = get_post_count(text_0)
    print("合計:" + str(post_count))
    
    info_all = {}
    count_info = 0
    buf_count_info = 0
    while count_info < MIN_COUNT:

        # スクロール後対象ページのhtmlソース取得
        text_1 = driver.page_source
        info = get_info_from_text(text_1)
        
        if not None:
            info_all.update(info)
        
        count_info = len(info_all)
        time.sleep(1)
        print(count_info)
        
        if buf_count_info==count_info:
            time.sleep(3)
            
        result_flg = scroll_to_elem(driver, False)
        buf_count_info = count_info
        
        if post_count <= count_info:
            break
            
    driver.quit()

詳細はコードを見てください。
このレベルのPythonを理解できない方は、利用するのはやめておいた方がよいでしょう。

スクレイピングは、やりかたによっては相手サーバーに負荷を与えてしまいますので。
そのため、理解できないコードは利用すべきではありません。

ただ、Seleniumに関する部分は理解必須とは言えません。
これを機に理解してみるか程度でよいかと思います。
あと、以下の定数を説明しておきます。

MIN_COUNT = 100
KEYWORD = "家系ラーメン"
CHROMEDRIVER = "chromedriver.exe"

MIN_COUNT

必要最低限取得したい投稿数です。
ハッシュタグによっては、何百万件も投稿があります。

制限がないと何百万件も取得し続けるわけです。
そのための制限でもあると考えてください。

現状は、100を設定しています。
よって、100件以上取得した段階でプログラムは終了します。

KEYWORD

これは説明不要でしょうね。
ハッシュタグのキーワードです。

CHROMEDRIVER

Seleniumに関する部分です。
詳細は、次の記事を確認してください。

まとめ

あとは、printに関して補足で説明しておきます。
このコードを実行すると、以下のような結果が表示されます。

合計:280709
33
45
57
69
81

合計の「280709」は、ハッシュタグ検索の結果の総件数です。
それ以外の数字は、スクロールする度に取得した投稿の総件数を表示しています。
12件づつスクレイピングできているのが、わかると思います。

なお、スクロールは基本的に1秒間間隔で行っています。
現時点では、InstagramからIP制限を受けていません。
1秒間間隔のアクセスなら、問題ないレベルということでしょう。

そもそも、人間が手動でスクロールする方が速いはずです。
指でシュッシュッとスクロールしますよね。
短時間においてなら、プログラムより人間の方がサーバーへの負荷が大きいのでは?

以上より、適切に考慮すれば、スクレイピングは何も危険ではありません。

タイトルとURLをコピーしました