LoginSignup
0
1

More than 3 years have passed since last update.

instagramの投稿から#を取得する

Last updated at Posted at 2021-03-07

(まだ途中だけどもとりあえず投稿)

流れ

1.ブラウザを起動
2.インスタグラムにログイン
3.対象のアカウントのHTMLを取得
4.info辞書作成
5.投稿のURLを開く
6.HTML取得
7.#のリスト獲得

インスタグラムは直接スクレイピングを行うことが出来ない。
だから、ブラウザを介する必要がある。

インストールするもの

・selenium
>クリックとかキーボード入力などの操作をプログラムで命令できる

・bs4(BeautifulSoup)
>取得したHTMLを解析する

・chromedriver-binary(バージョンはchromeより前のもの)
>ブラウザをプログラムで操作する
>chrome用のwebdriver

*webdriver:ブラウザを操作するライブラリ

ソースコード

main.py
#個人の過去の投稿を取得
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from selenium import webdriver
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
import chromedriver_binary

LOGIN_ID = "**********"
PASSWORD = "********"
INSTAGRAM_DOMAIN = "https://www.instagram.com/"
MIN_COUNT = 10
ACCOUNT_ID = "#を取得したいアカウントのID"

#検索結果のurlを取得する
def get_url():
    element = driver.find_element_by_id('react-root')
    aTag    = element.find_element_by_tag_name("a")
    url     = aTag.get_attribute("href")
    return url

# driver取得
def get_driver():

    # ヘッドレスモードでブラウザを起動
    options = Options()
    options.add_argument('--headless')
    # ブラウザーを起動
    driver = webdriver.Chrome(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

#tag_info取得.
def get_info_tag(text):
    soup = bs4.BeautifulSoup(text,features='lxml')
    tags = []
    try:
        #(xil3iに#がある)
        elems = soup.find_all(class_ = 'xil3i')
        #print("elems",elems)
        for elem in elems:
            tag = elem.get_text()
            tags.append(tag)
            #print('繰り返しelem:',tag)
        return tags
    except:
        return None


# info取得
def get_info_from_text(text):
    soup = bs4.BeautifulSoup(text, features='lxml')
    try:
        info = {}
        id_ = []
        # 投稿(v1Nh3 kIKUG  _bz0w)
        elems = soup.find_all(class_="v1Nh3")
        #print("info_elems:",elems)
        for elem in elems:
            a_elem = elem.find("a")
            href = a_elem["href"]
            url = INSTAGRAM_DOMAIN + href
            post_id = get_search_value("\/p\/(.*)\/", href)

            #辞書からurlを引き出すためのid
            id_.append(post_id)

            # 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,id_

    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

#インスタグラムログイン
def do_login(driver):
    # ログインURL
    login_url = INSTAGRAM_DOMAIN + "accounts/login/"
    driver.get(login_url)

    # 電話、メールまたはユーザー名のinput要素が読み込まれるまで待機(最大10秒)
    elem_id = WebDriverWait(driver, 10).until(
        EC.visibility_of_element_located((By.NAME, "username"))
    )

    try:
        # パスワードのinput要素
        elem_password = driver.find_element_by_name("password")

        if elem_id and elem_password:
            # ログインID入力
            elem_id.send_keys(LOGIN_ID)

            # パスワード入力
            elem_password.send_keys(PASSWORD)


            # ログインボタンクリック
            elem_btn = WebDriverWait(driver, 10).until(
                EC.visibility_of_element_located((By.XPATH, '//*[@id="loginForm"]/div/div[3]/button'))
            )

            actions = ActionChains(driver)
            actions.move_to_element(elem_btn)
            actions.click(elem_btn)
            actions.perform()

            # 適当(3秒間待つように対応しています)
            time.sleep(3)

            # 遷移
            # 遷移後のURLでログイン可否をチェック
            perform_url = driver.current_url

            if perform_url.find(login_url) == -1:
                # ログイン成功
                return True
            else:
                # ログイン失敗
                return False

        else:
            return False
    except:
        return False 

if __name__ == '__main__':

    # url
    url = "https://www.instagram.com/" + ACCOUNT_ID + "/"

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

    # 対象ページのhtmlソース取得
    text_0 = get_text_from_target_page(driver, True, url)

    #element = driver.find_element_by_link_text('helf')

    post_count = get_post_count(text_0)
    print("合計:" + str(post_count))

    info_all = {}
    count_info = 0
    buf_count_info = 0
    tag_list = []
    while count_info < MIN_COUNT:

        # スクロール後対象ページのhtmlソース取得
        text_1 = driver.page_source
        info,id_ = get_info_from_text(text_1)
        for i in id_:
            url = info[i]["url"]
            #個人の投稿ページのURLを開きHTMLを取得
            html_account = get_text_from_target_page(driver,True,url)
            #次に#の情報を取得する(xil3i)
            tags = get_info_tag(html_account)
            tag_list += tags

        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
    #作成した#のリストの
    result_tag = set(tag_list)
    result_tag = list(result_tag)
    print(result_tag)
    driver.quit()

ブラウザを起動

options.add_argument('--headless')を書くと裏でブラウザが起動され表示されない。
多分--headlessを他の物に変更すると別の機能があるんだろうと思う。

# driver取得
def get_driver():

    # ヘッドレスモードでブラウザを起動
    options = Options()
    options.add_argument('--headless')
    # ブラウザーを起動
    driver = webdriver.Chrome(options=options)

    return driver

インスタグラムにログイン

driver.get()

urlを開いている。引数は開きたいurl。

login_url = INSTAGRAM_DOMAIN + "accounts/login/"
:インスタグラムのログイン画面のurlを指定。

elem_id=WebDriverWait(driver,10).until(EC.visibility_of_element_located((By.NAME,"username")))

elem_idに電話番号やユーザーネームを入れる場所の情報(input要素)が入る。

下記参考サイト

try:

実行したい処理

except:

tryが出来なかったときに実行する処理

send.key()

input要素に値を入力する
引数を変更することでEnterkeyを押すこともできる

参考

xpathとは

htmlを指定するpath
こんなやつ↓↓
/bookstore/book/author

Image from Gyazo

#インスタグラムログイン
def do_login(driver):
    # ログインURL
    login_url = INSTAGRAM_DOMAIN + "accounts/login/"
    driver.get(login_url)

    # 電話、メールまたはユーザー名のinput要素が読み込まれるまで待機(最大10秒)
    elem_id = WebDriverWait(driver, 10).until(
        EC.visibility_of_element_located((By.NAME, "username"))
    )

    try:
        # パスワードのinput要素
        elem_password = driver.find_element_by_name("password")

        if elem_id and elem_password:
            # ログインID入力
            elem_id.send_keys(LOGIN_ID)

            # パスワード入力
            elem_password.send_keys(PASSWORD)


            # ログインボタン
            elem_btn = WebDriverWait(driver, 10).until(
                EC.visibility_of_element_located((By.XPATH, '//*[@id="loginForm"]/div/div[3]/button'))
            )

            actions = ActionChains(driver)
            actions.move_to_element(elem_btn) #ログインボタンまで移動
            actions.click(elem_btn) #クリックの命令
            actions.perform() #実行

            # 適当(3秒間待つように対応しています)
            time.sleep(3)

            # 遷移
            # 遷移後のURLでログイン可否をチェック
            perform_url = driver.current_url

            if perform_url.find(login_url) == -1:
                # ログイン成功
                return True
            else:
                # ログイン失敗
                return False

        else:
            return False
    except:
        return False 
0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1