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秒間間隔のアクセスなら、問題ないレベルということでしょう。
そもそも、人間が手動でスクロールする方が速いはずです。
指でシュッシュッとスクロールしますよね。
短時間においてなら、プログラムより人間の方がサーバーへの負荷が大きいのでは?
以上より、適切に考慮すれば、スクレイピングは何も危険ではありません。




