Beispiel #1
0
    def fav(self):
        '''投稿をfavする

        Returns:
            bool: favに成功 -> True

        Conditions:
            [投稿写真 or 投稿動画]
        '''
        # 写真が大きいとfavボタンとポップがぶつかってしまうので画面をずらす
        # あんまりずらすとオススメ投稿が見えるのでちょっとだけ動かす
        self.driver.execute_script('window.scrollBy(0, 100)')

        already_faved = self.driver.find_elements_by_xpath('//*[contains(@aria-label, "取り消す")]')
        if already_faved:
            return False

        fav_btn = self.driver.find_elements_by_xpath('//*[contains(@aria-label, "いいね")]')
        if not fav_btn:
            return False
        fav_btn[0].click()

        # アクションブロック確認を、アクション更新前に入れる
        self.mediator.modal.check_action_block()

        # アクション回数を更新
        self.action_counters_repository.increase_action_count({'fav': 1})

        # ランダムにスワイプ
        swipe_random(self.driver)
        return True
Beispiel #2
0
    def switch_to_search_home(self):
        '''検索トップへ遷移する
        '''
        # ランダムにスワイプ
        swipe_random(self.driver)

        self.driver.get(f'https://www.instagram.com/explore/')
Beispiel #3
0
    def search_tags(self, keyword):
        '''keywordで検索して、結果へ遷移する
        実際は[検索ホーム]から始める必要はないけど、念のため正規ルートに近い画面遷移を推薦

        WARN:
            URLエンコーディングして直接飛ぶため、`#` 無しの文字列が必要です

        Args:
            keyword (str): 検索するワード. タグの場合は#付きで渡す (e.g. '#猫')

        Condition:
            [検索ホーム]
        '''
        #

        # 何回もdictを参照させて失敗してるから、チェック機構を入れよう
        if type(keyword) is not str:
            self.mediator.logger.error(f'文字列ではない検索ワードが参照されました: {keyword}')
            raise Exception

        # 念のため # 消し処理を加える
        kw_cleaned = keyword.replace('#', '')

        # ランダムにスワイプ
        swipe_random(self.driver)
        self.driver.get(
            f'https://www.instagram.com/explore/tags/{kw_cleaned}/')
Beispiel #4
0
    def follow(self, insta_id, insert_into_table=True):
        '''フォローする

        Args:
            insta_id (str): プロフィールから取るより早くて安全
            insert_into_table (bool): following_status テーブルへレコード追加する

        Returns:
            bool: フォロー成功 -> True

        Conditions:
            [プロフィール]
        '''
        has_followed = False
        el = self.pick_follow_btn()
        if el:
            el.click()
            has_followed = True

        # アクションブロック確認を、アクション更新前に入れる
        self.mediator.modal.check_action_block()

        # アクション更新
        if has_followed:
            # ステータスを更新
            if insert_into_table:
                self.mediator.following_status_repository.add_following(
                    insta_id, has_followed=1, is_follower=0)
            # アクション回数を更新
            self.action_counters_repository.increase_action_count(
                {'follow': 1})

        # ランダムにスワイプ
        swipe_random(self.driver)
        return has_followed
Beispiel #5
0
    def unfollow(self, insta_id, stop_private=False):
        '''指定されたインスタIDをアンフォローする


        WARN:
            鍵アカの場合はちょっと考えてない... automataは現状鍵アカをスルーする方針

        Args:
            insta_id (str): 画面からもインスタID取れるけど、もらった方が早い
            stop_private (bool): 鍵アカのアンフォロー止める -> True

        Returns:
            bool: アンフォローに成功 -> True
        '''
        unfollow_btn = self.pick_unfollow_btn()
        if unfollow_btn:
            # フォローボタンが見つかった場合はアンフォロー
            unfollow_btn.click()
            if stop_private:
                if self.mediator.modal.check_unfollow_dialog_if_private():
                    self.mediator.logger.debug(f'鍵アカのためアンフォローを中止: {insta_id}')
                    return False
            self.mediator.modal.press_unfollow_at_profile_home()
            self.mediator.logger.debug(f'アクション unfollow: {insta_id}')
        elif self.pick_follow_btn():
            # フォローボタンが見つかった場合: 相手先から解除された
            self.mediator.logger.debug(
                f'アクション unfollow: 相手から解除された. フォロー中リストから削除: {insta_id}')
        elif self.pick_followback_btn():
            # フォローバックボタンが見つかった場合: 相手先から解除された?
            self.mediator.logger.debug(
                f'アクション unfollow: 削除された?(フォローバックボタンを確認). フォロー中リストから削除: {insta_id}'
            )
        elif self.is_deleted_page:
            # アカウント削除済み
            self.mediator.logger.debug(
                f'アクション unfollow: アカ削除済み. フォロー中リストから削除: {insta_id}')
        else:
            # その他: 理由不明。画面呼び出しの失敗 or リクエスト中?
            self.mediator.logger.debug(
                f'アクション unfollow: 要素の取得失敗 or リクエスト中 のためskip: {insta_id}')
            return False

        # アクションブロック確認を、アクション更新前に入れる
        self.mediator.modal.check_action_block()

        # アクション更新
        self.mediator.following_status_repository.delete_following(insta_id)
        self.action_counters_repository.increase_action_count({'unfollow': 1})

        # ランダムにスワイプ
        swipe_random(self.driver)
        return True
Beispiel #6
0
    def switch_to_user_profile(self, insta_id):
        '''指定されたインスタグラムID の[プロフィール]へ遷移する
        アカ削除済等で遷移できないケースがある

        WARN:
            URL直書きで飛べるけど、変なアクセスに見られないように注意が必要かも

        Args:
            insta_id (str): インスタグラムID
        '''
        # ランダムにスワイプ
        swipe_random(self.driver)

        # 何回もdictを参照させて失敗してるから、チェック機構を入れよう
        if type(insta_id) is not str:
            self.mediator.logger.error(f'文字列ではないURLが参照されました: {insta_id}')
            raise Exception

        self.driver.get(f'https://www.instagram.com/{insta_id}/')
Beispiel #7
0
    def load_popular_post_userlist(self, hash_tag):
        '''タグ検索から、人気投稿のユーザリストを取得する
        取得アカ数は表示される9アカから取得できた分で固定
        '''
        self.ab.search.switch_to_search_home()
        self.ab.search.search_tags(hash_tag)

        popular_ids = []
        for img_link in self.ab.search.load_popular_posts():

            # ブロック対策のため、画面遷移前にはランダムにスワイプ
            swipe_random(self.ab.driver)
            self.ab.driver.get(img_link)

            insta_id_i = self.ab.post.estimate_insta_id()
            popular_ids.append(insta_id_i)

            # # 取得成功と参照可能をチェックする ※ 何回か回してみて上手くいきそうなら削除
            # self.ab.profile.switch_to_user_profile(insta_id_i)
            # follower_cnt = self.ab.profile.pick_follower_num()
            # following_cnt = self.ab.profile.pick_following_num()
            # if follower_cnt and following_cnt:
            #     popular_ids.append(insta_id_i)
        return popular_ids
Beispiel #8
0
    def follow_by_searching(self,
                            actions,
                            keywords,
                            fav_rate=0.7,
                            only_japanese=True):
        '''投稿検索で見つけたフォロワーをフォローする

        followは個人アカか確かめるけど、favは未確認でも良いでしょう

        Args:
            actions (int): 実行する action の回数
            keywords (str[]): 検索するキーワード ハッシュタグでも `#` は消しておく
            fav_rate (float): フォローの代わりにfavする確率
        '''
        self.ab.logger.debug('start operation: キーワード検索の最新投稿アカを follow or fav')
        self.ab.search.switch_to_search_home()

        keywords_shuffled = random.sample(keywords, len(keywords))
        followed_cnt = 0
        fav_cnt = 0
        is_enough = False
        for kw in keywords_shuffled:
            self.ab.logger.debug(
                f'次のキーワード検索 - 開始: {kw} アクション残: {fav_cnt + followed_cnt}/{actions}'
            )
            self.ab.search.search_tags(kw)

            fav_skip_cnt = 0
            for img_link in self.ab.search.load_latest_posts():
                # 必要分のアクションが終わったら離脱
                if (followed_cnt + fav_cnt) >= actions:
                    is_enough = True
                    break

                # fav が連続して弾かれたら新規投稿枯渇とみなして次のkeywordへ移動
                if fav_skip_cnt >= 3:
                    self.ab.logger.debug('fav済が続くため、新規投稿枯渇とみなして次のkeywordへ移動')
                    break

                # ブロック対策のため、画面遷移前にはランダムにスワイプ
                swipe_random(self.ab.driver)
                self.ab.driver.get(img_link)
                insta_id_i = self.ab.post.estimate_insta_id()

                # 同一アクションの連続はブロックの危険があがるので、fav or follow をランダムで変える
                is_private = False
                if random.random() < 0.2:
                    # 一定の確率で何もしない -> 気持ちだけでも周遊しているように見せかける
                    continue
                if random.random() <= fav_rate:
                    if self.ab.post.fav():
                        fav_cnt += 1
                        fav_skip_cnt = 0
                    else:
                        fav_skip_cnt += 1
                    self.ab.logger.debug(f'アクション fav: {insta_id_i}, cnt is 1')
                else:
                    if only_japanese:
                        post_msg = self.ab.post.read_post_msg()
                        if (type(post_msg) is
                                str) and (not would_be_japanese(post_msg)):
                            self.ab.logger.debug(
                                f'投稿コメントが日本語と推定できないためskip: {insta_id_i}')
                            continue

                    self.ab.profile.switch_to_user_profile(insta_id_i)
                    is_valid, reason_msg = self.check_kojin()
                    if is_valid:
                        has_followed = self.ab.profile.follow(insta_id_i)
                        self.ab.logger.debug(f'アクション follow: {insta_id_i}')
                        if has_followed:
                            followed_cnt += 1
                    else:
                        is_private = True if '鍵アカ' in reason_msg else False
                        self.ab.logger.debug(
                            f'無効なユーザ: {insta_id_i}, {reason_msg}')
                self.recent_touched_histories_repository.add_recent_touched_user(
                    insta_id_i, int(is_private))
            self.ab.logger.debug(
                f'次のキーワード検索 - 終了: keyword -> {kw}, follow -> {followed_cnt}, fav -> {fav_cnt}'
            )
            if is_enough:
                break
        self.ab.logger.debug('end operation: キーワード検索の最新投稿アカを follow or fav')