[Python] Instagramの自動フォローツールの作成

皆さんお久しぶりです。ちょっと色々立て込んでて長い間更新できていませんでした。それで、今回はinstagramの自動フォロー、フォロー解除ツールを作っていこうと思います。エラーハンドリング処理などはchatgptなどをうまく使って追加しました！最近はAIの成長がすごいですよね。まあそんな話は置いといて、始めます。

- ### 実行環境
- OS: Ubuntu 22.04LTS
 python-library: selenium

とりまコード出します。
```ruby:Python
from time import sleep
from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.common.by import By
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.common.action_chains import ActionChains

def get_user_input():
    try:
        accounts = input("フォローするアカウント名をカンマ区切りで入力してください: ").split(',')
        follow_count = int(input("フォローするアカウントの数を指定してください: "))
        multi_window = input("多窓で実行しますか？ (y/n): ").lower() == 'y'
        wait_time = int(input("フォローの待機時間（秒）を入力してください: "))
        follow_private = input("非公開アカウントもフォローしますか？ (y/n): ").lower() == 'y'
        headless_mode = input("ヘッドレスモードで実行しますか？ (y/n): ").lower() == 'y'
        return accounts, follow_count, multi_window, wait_time, follow_private, headless_mode
    except Exception as e:
        print(f"入力エラー: {e}")
        return [], 0, False, 0, False, False

class InstaFollower:
    def __init__(self, driver_path, headless_mode):
        try:
            chrome_options = Options()
            if headless_mode:
                chrome_options.add_argument("--headless")
                chrome_options.add_argument("--no-sandbox")
                chrome_options.add_argument("--disable-dev-shm-usage")
            self.driver = webdriver.Chrome(service=Service(executable_path=driver_path), options=chrome_options)
        except Exception as e:
            print(f"ドライバ初期化エラー: {e}")

    def add_cookies(self, sessionid, csrftoken):
        try:
            cookies = [
                {'name': 'sessionid', 'value': sessionid, 'domain': '.instagram.com'},
                {'name': 'csrftoken', 'value': csrftoken, 'domain': '.instagram.com'}
            ]
            for cookie in cookies:
                self.driver.add_cookie(cookie)
        except Exception as e:
            print(f"クッキー追加エラー: {e}")

    def login(self, url, sessionid, csrftoken):
        try:
            self.driver.get(url)
            sleep(5)
            self.add_cookies(sessionid, csrftoken)
            self.driver.refresh()
            sleep(5)
        except Exception as e:
            print(f"ログインエラー: {e}")

    def find_followers(self, url, account):
        try:
            self.driver.get(f"{url}{account}")
            sleep(8)
            followers = self.driver.find_element(By.CSS_SELECTOR, 'ul li a._a6hd')
            followers.click()
            sleep(7)
            xpaths = [
                '/html/body/div[7]/div[1]/div/div[2]/div/div/div/div/div[2]/div/div/div[3]',
                '/html/body/div[6]/div[1]/div/div[2]/div/div/div/div/div[2]/div/div/div[3]'
            ]
            for xpath in xpaths:
                try:
                    self.modal = self.driver.find_element(By.XPATH, xpath)
                    if self.modal:
                        break
                except:
                    continue
        except Exception as e:
            print(f"フォロワー検索エラー: {e}")

    def is_private_account(self, index):
        try:
            xpaths = [
                f'/html/body/div[6]/div[1]/div/div[2]/div/div/div/div/div/div/div[4]/div[{index}]/div/div/div/div/div/div/div[2]/div/div/div/div/div/a/div/div/span',
                f'/html/body/div[7]/div[1]/div/div[2]/div/div/div/div/div/div/div[4]/div[{index}]/div/div/div/div/div/div/div[2]/div/div/div/div/div/a/div/div/span'
            ]
            element = None
            for xpath in xpaths:
                try:
                    element = self.driver.find_element(By.XPATH, xpath)
                    if element:
                        break
                except:
                    continue

            if element:
                self.driver.execute_script("arguments[0].scrollIntoView();", element)
                sleep(1)
                hover = ActionChains(self.driver).move_to_element(element)
                hover.perform()
                sleep(1)
                return "非公開" in element.text
            else:
                return False
        except Exception as e:
            print(f"非公開アカウント判別エラー: {e}")
            return False

    def follow(self, wait_time, follow_count, follow_private):
        try:
            followed = 0
            index = 1
            while followed < follow_count:
                buttons = self.driver.find_elements(By.CSS_SELECTOR, "button._acan._acap._acas._aj1-._ap30")
                button_found = False
                for button in buttons:
                    if followed >= follow_count:
                        break
                    if 'フォロー中' not in button.text:
                        if not self.is_private_account(index) or follow_private:
                            try:
                                button.click()
                                followed += 1
                                button_found = True
                                sleep(wait_time)
                            except Exception as e:
                                print(f"ボタンクリックエラー: {e}")
                        else:
                            print("非公開アカウントです。次のボタンに進みます。")
                    else:
                        print("既にフォロー中です。次のボタンに進みます。")
                    index += 1

                if not button_found:
                    sleep(2)
                    self.driver.execute_script("arguments[0].scrollTop = arguments[0].scrollHeight", self.modal)
        except Exception as e:
            print(f"フォローボタン検索エラー: {e}")

if __name__ == "__main__":
    sessionid = "594653684386XXXXX"
    csrftoken = "CgAFgh397rfhajkf3h8g39r3uj9rfa"
    chrome_driver_path = r"/usr/local/bin/chromedriver-linux64/chromedriver"
    accounts, follow_count, multi_window, wait_time, follow_private, headless_mode = get_user_input()
    bots = []
    
    for account in accounts:
        bot = InstaFollower(driver_path=chrome_driver_path, headless_mode=headless_mode)
        bot.login(url="https://www.instagram.com/", sessionid=sessionid, csrftoken=csrftoken)
        bot.find_followers(url="https://www.instagram.com/", account=account)
        bots.append(bot)
        
        if multi_window:
            try:
                bot.driver.execute_script("window.open('');")
                bot.driver.switch_to.window(bot.driver.window_handles[-1])
            except Exception as e:
                print(f"新しいウィンドウを開くエラー: {e}")
    
    for bot in bots:
        bot.follow(wait_time, follow_count, follow_private)
```
これです。とりあえず実装しているものについてを解説します。
## 必要な情報
instagramのアカウントにログインするためにCookie情報を取得する必要があります。
インスタの自動フォローしたいアカウントにログインした状態でF12でdevtoolsを開きます。その後にApplicationタブに行ってcookiesに行きます。これです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3742490/1f2e6121-2f50-e4cc-02fe-8f875dc432fd.png)
このカーソルに当てている部分をクリックして右の方にcookie情報が表示されるのでその名前がcsrftokenのものとsessionidのものを探して、それぞれcsrftokenであればCgAFgh397r3h8g39r3uj9rfaの部分に値を入れて、sessionidであれば594653684386XXXXXの部分に入れてください。
## 主な機能
- #### アカウント名の入力
これはカンマで区切って複数選択できます。それと言ってませんでしたがアカウント名を入力して、そのアカウントのフォロワーをフォローするツールです。
- #### フォロー数の指定
自動検知防止のためです。1日150-200くらいの値を選ぶのが望ましいです。
- #### 多窓での実行
この機能つけましたがマジでいりません。テストもしてませんから使えるかわかりません。
- #### フォローの待ち時間
-  これも自動検知防止のためです。30秒で行ってましたがだめでした。不審なアクティビティとか言われます。なので今度は300秒にしたら自動化したなと叱られました。今は安定の560秒にしています。これは毎日150フォローするというのを基準にしてやってます。86400/150っすね。それの大体をとって560秒です。今の所検知はされてません。本当は何秒から何秒って指定して乱数かなんか使って不規則的にフォローするほうが良かったんですがやってません。そこら辺は自由に改変しちゃってください。
- #### 非公開アカウントのフォロー
つけましたが非公開アカウントと判別する方法が毎回対象のユーザーのページに行かなくてはならない(今までのはフォロワー欄のページ)とかいうちょっと俺の好きじゃないやつになるので実装は辞めました。
- #### ヘッドレスモード
これはまあブラウザを表示するかしないかを決めるやつです。ヘッドレスモードにしたらリソース減ります。少しだけ。デバッグ用でつけてます。
- #### フォロワー検索
当たり前ですが指定されたアカウントのフォロワーを取得するために/followers
ページに飛んでフォローボタンのxpathでクリックします。
- #### エラー処理
本当にしょぼいですがエラーハンドリングはしてます。フォローボタン検索エラー発生したときはただxpathが変わっているだけとかが多いので確かめてみてください。他にもログインエラーが発生したらそのログインセッションがなくなっている可能性があるので新しいcookieにしたりしてください。

てな感じです。最後までご視聴ありがとうございました。