From c119b28e4dce1a4189d9edbb144fa95ff43985d3 Mon Sep 17 00:00:00 2001 From: smolkik-code Date: Wed, 17 Jun 2026 11:58:02 +0700 Subject: [PATCH] fix: add mobile user-agent and random delays to bypass Instagram anti-bot - iPhone Safari user-agent + X-IG-App-ID header - 2-6s random delay between profile checks - 1-3s delay between media downloads - RateController for built-in throttling - Try/except per followee to not stop on errors --- instagram_monitor.py | 81 ++++++++++++++++++++++++++++++-------------- 1 file changed, 55 insertions(+), 26 deletions(-) diff --git a/instagram_monitor.py b/instagram_monitor.py index e89ffc6..66af0c9 100644 --- a/instagram_monitor.py +++ b/instagram_monitor.py @@ -1,7 +1,9 @@ import http.cookiejar import json import os +import random import shutil +import time from datetime import datetime from pathlib import Path from typing import AsyncIterator @@ -22,8 +24,16 @@ class InstagramMonitor: compress_json=False, post_metadata_txt_pattern="", max_connection_attempts=3, - request_timeout=30, + request_timeout=60, + rate_controller=instaloader.RateController(), ) + self.loader.context._session.headers.update({ + "User-Agent": "Mozilla/5.0 (iPhone; CPU iPhone OS 17_5 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.5 Mobile/15E148 Safari/604.1", + "X-IG-App-ID": "936619743392459", + "X-IG-WWW-Claim": "0", + "X-Requested-With": "XMLHttpRequest", + "Accept-Language": "en-US,en;q=0.9", + }) self._session_path = Path(config.session_file) self._download_dir = Path(config.download_dir) self._state_file = self._download_dir / "state.json" @@ -153,6 +163,11 @@ class InstagramMonitor: """Clear session cookies to start fresh.""" self.loader.context._session.cookies.clear() + def _random_delay(self, min_sec: float = 2.0, max_sec: float = 5.0) -> None: + """Random delay to avoid detection.""" + delay = random.uniform(min_sec, max_sec) + time.sleep(delay) + def _load_cookies_from_json(self, path: Path) -> None: with open(path) as f: cookies = json.load(f) @@ -228,6 +243,8 @@ class InstagramMonitor: logger.error("Cannot check updates without login") return + self._random_delay(3, 7) + try: # First verify who we are logged in as logger.info(f"Attempting to fetch profile: {config.instagram_username}") @@ -253,32 +270,44 @@ class InstagramMonitor: for followee in followees: username = followee.username - for story in self._get_profile_stories(username): - media_path = self._download_media(story) - if media_path: - self._state[str(story.mediaid)] = datetime.utcnow().isoformat() - self._save_state() - yield { - "type": "story", - "username": username, - "media_path": media_path, - "caption": "", - "shortcode": str(story.mediaid), - } + self._random_delay(3, 6) - for post in self._get_profile_posts(username): - caption = post.caption or "" - media_path = self._download_media(post) - if media_path: - self._state[str(post.mediaid)] = datetime.utcnow().isoformat() - self._save_state() - yield { - "type": "post", - "username": username, - "media_path": media_path, - "caption": caption[:500], - "shortcode": post.shortcode, - } + try: + for story in self._get_profile_stories(username): + media_path = self._download_media(story) + if media_path: + self._state[str(story.mediaid)] = datetime.utcnow().isoformat() + self._save_state() + yield { + "type": "story", + "username": username, + "media_path": media_path, + "caption": "", + "shortcode": str(story.mediaid), + } + self._random_delay(1, 3) + except Exception as e: + logger.error(f"Error processing stories for {username}: {e}") + + self._random_delay(2, 4) + + try: + for post in self._get_profile_posts(username): + caption = post.caption or "" + media_path = self._download_media(post) + if media_path: + self._state[str(post.mediaid)] = datetime.utcnow().isoformat() + self._save_state() + yield { + "type": "post", + "username": username, + "media_path": media_path, + "caption": caption[:500], + "shortcode": post.shortcode, + } + self._random_delay(2, 5) + except Exception as e: + logger.error(f"Error processing posts for {username}: {e}") def cleanup_downloads(self, max_age_hours: int = 24) -> None: now = datetime.utcnow()