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
def switch_to_search_home(self): '''検索トップへ遷移する ''' # ランダムにスワイプ swipe_random(self.driver) self.driver.get(f'https://www.instagram.com/explore/')
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}/')
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
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
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}/')
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
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')