Как из html-кода страницы в Instagram вывести id аккаунта?
Я знаю, как получить id через инструменты разработчика: f12 -> ctrl+f -> «id». Необходимая информация будет содержаться в словаре следующего вида:
Как эту же информацию получить с помощью python? Использовала следующий код:
import urllib import requests f = urllib.request.urlopen("https://www.instagram.com/user007/").read() output = f.decode('utf-8') print(output)
Но в выводе не было нужной информации. P.S. user007 — выдуманное имя пользователя
Отслеживать
Елена Сергеева
задан 24 фев 2022 в 13:46
Елена Сергеева Елена Сергеева
495 3 3 серебряных знака 10 10 бронзовых знаков
Вы вывели страницу в консоль print(output) и там не было того id, который ожидали?
24 фев 2022 в 14:02
не было, почему то в output не вся информация включается, либо она каким то образом еще кодируется
24 фев 2022 в 14:26
Возможно, она js добавляется
24 фев 2022 в 14:30
извините, не поняла про что вы
24 фев 2022 в 14:48
Кст, для вашей ссылке нашлось в браузере: «owner»: <"id":"8079564","username":"user007">. Но проверить кодом не удалось, т.к. инстаграм отвечал редиректом к instagram.com/accounts/login/?next=/user007, чтобы сначала сделал авторизацию, а на странице по редиректу не было того id юзера. Проверял через requests : import requests rs = requests.get(‘https://www.instagram.com/user007/’) print(rs.url) print(‘»owner»:
24 фев 2022 в 20:25
0
Сортировка: Сброс на вариант по умолчанию
Знаете кого-то, кто может ответить? Поделитесь ссылкой на этот вопрос по почте, через Твиттер или Facebook.
- python
- instagram-api
Робот для автоматизированного просмотра Instagram на Python и Selenium
Недавно мы начали вести Instagram — подписывайтесь, чтобы не пропустить контент, которого нет в блоге и Telegram!
Многие из нас ежедневно заходят в Instagram, чтобы посмотреть истории друзей и полистать ленту постов и рекомендаций. Предлагаем действенный способ сохранить своё время — напишем на Python и Selenium робота, который возьмёт на себя рутинную задачу проверки свежих новостей друзей и подсчитает число новых историй и входящих сообщений.
Авторизация в аккаунт
При переходе в браузерную версию сайта, нас встречает такое окно:

Но просто вставить логин, пароль и нажать на кнопку «Войти» недостаточно: впереди будет ещё два окна. Во-первых, предложение сохранить данные — здесь мы тактично жмём «Не сейчас». Instagram тщательно следит за каждым нашим действием и малейшие аномалии в поведении приводят к блокировке, поэтому любые предложения по сохранению данных будем на всякий случай пропускать.

Следующим препятствием будет предложение включить уведомление, которое мы тоже пропустим:

Первым делом импортируем библиотеки:
from selenium import webdriver from webdriver_manager.chrome import ChromeDriverManager from bs4 import BeautifulSoup as bs import time import random
И описываем функцию authorize — она будет принимать driver в качестве аргумента, отправлять в нужные поля логин и пароль, нажимать на кнопку «Войти», затем ждать десять секунд на загрузку страницы, нажимать на кнопку «Не сейчас», снова ждать загрузки страницы и пропускать уведомления:
def authorize(driver): username = 'login' password = 'password' driver.get('https://www.instagram.com') time.sleep(5) driver.find_element_by_name("username").send_keys(username) driver.find_element_by_name("password").send_keys(password) driver.execute_script("document.getElementsByClassName('sqdOP L3NKy y3zKF ')[0].click()") time.sleep(10) driver.execute_script("document.getElementsByClassName('sqdOP L3NKy y3zKF ')[0].click()") time.sleep(10) driver.execute_script("document.getElementsByClassName('aOOlW HoLwm ')[0].click()")
Новые сообщения
В Instagram могут прийти сообщения двух видов. В случае, если вы не подписаны на отправителя — придёт запрос на диалог. Если подписаны — придёт входящее сообщения. Оба случая обрабатываются по-разному. Число входящих сообщений можно получить с главной страницы — это число над иконкой бумажного самолётика:

А число запросов можно забрать текстом заголовка h5 из раздела «Сообщения». Сперва перейдём в этот раздел и попробуем найти строку с запросами на сообщение. Затем вернёмся на главную страницу и возьмём то самое число новых сообщений.
def messages_count(driver): driver.get('https://www.instagram.com/direct/inbox/') time.sleep(2) inbox = bs(driver.page_source) try: queries_text = inbox.find_all('h5')[0].text except Exception: queries_text = None driver.get('https://www.instagram.com') time.sleep(2) content = bs(driver.page_source) try: messages_count = int(content.find_all('div', attrs=)[0].text) except Exception: messages_count = 0 return queries_text, messages_count
Подсчёт числа новых сторис
Все истории хранятся в одном блоке:

Это список с одинаковым классом, но в каждом элементе списка лежит ещё один div-блок. У новых историй это класс eebAO h_uhZ, у просмотренных — eebAO.

Ещё есть такая кнопка, которая показывает следующую пачку историй:

При этом Instagram динамически прогружает код страницы, и в нём не найти те элементы, которые вы не видите своими глазами. Поэтому мы возьмём первые 8 видимых новых историй, добавим в список, нажмём на кнопку «Показать следующие истории» и будем продолжать так, пока кнопка ещё отображается. А затем подсчитаем число уникальных элементов, чтобы избежать возможных дубликатов.
def get_stories_count(driver): stories_divs = [] scroll = True while scroll: try: content = bs(driver.page_source) stories_divs.extend(content.find_all('div', attrs=)) driver.execute_script("document.getElementsByClassName(' _6CZji oevZr ')[0].click()") time.sleep(1) except Exception as E: scroll = False return len(set(stories_divs))
Просмотр сторис
Следующее, чем может заняться реальный пользователь после авторизации — просмотр свежих историй. Для того, чтобы зайти в блок историй, нужно просто нажать на кнопку класса OE3OK:

Есть еще две кнопки, о которых мы должны знать. Это кнопка для переключения на следующую историю — она в классе FhutL и кнопка закрытия блока историй — класс wpO6b. Пускай одна история будет отнимать у нас от 10 до 15 секунд, и с вероятностью 1/5 мы переключим на следующую. При этом зададим переменные counter и limit — пусть сейчас мы хотим посмотреть случайное число историй от 5 до 45, и если мы уже посмотрели столько, то выходим из функции и историй.
def watch_stories(driver): watching = True counter = 0 limit = random.randint(5, 45) driver.execute_script("document.getElementsByClassName('OE3OK ')[0].click()") try: while watching: time.sleep(random.randint(10, 15)) if random.randint(1, 5) == 5: driver.execute_script("document.getElementsByClassName('FhutL')[0].click()") counter += 1 if counter > limit: driver.execute_script("document.getElementsByClassName('wpO6b ')[1].click()") watching = False except Exception as E: print(E) watching = False
Скроллинг ленты
После просмотра актуальных историй можно поскроллить ленту — это действие ничем не отличается от классического скроллинга страниц в Selenium. Запоминаем последнюю доступную длину страницы, скроллим до неё, ожидаем прогрузки, получаем новую. Прекратим просматривать ленту в двух случаях — если в random.randint() сгенерировалась единица или если лента кончилась.
def scroll_feed(driver): scrolling = True last_height = driver.execute_script("return document.body.scrollHeight") while scrolling: driver.execute_script("window.scrollTo(0, document.body.scrollHeight);") time.sleep(random.randint(4,10)) new_height = driver.execute_script("return document.body.scrollHeight") if new_height == last_height or random.randint(1, 10) == 1: scrolling = False last_height = new_height
Просмотр рекомендуемых аккаунтов
Instagram в заглавной странице сам рекомендует нам для подписки некоторые аккаунты. Выглядит она так:

И на ней тоже придётся скроллить, чтобы дойти до конца. Заходим на страницу и ожидаем 5 секунд прогрузки, затем снова получаем длину страницы и скроллим вниз. Выходим тоже с вероятностью 1/10 или если страница кончилась, но ещё с вероятностью 1/2 подписываемся на некоторые из первых 100 аккаунтов рекомендаций:
def scroll_recomendations(driver): driver.get('https://www.instagram.com/explore/people/suggested/') time.sleep(5) scrolling = True last_height = driver.execute_script("return document.body.scrollHeight") while scrolling: driver.execute_script("window.scrollTo(0, document.body.scrollHeight);") time.sleep(random.randint(4,10)) new_height = driver.execute_script("return document.body.scrollHeight") if new_height == last_height or random.randint(1, 10) == 1: scrolling = False last_height = new_height if random.randint(0, 1): try: driver.execute_script(f"document.getElementsByClassName('sqdOP L3NKy y3zKF ')[].click()") except Exception as E: print(E)
Просмотр рекомендуемых постов
Помимо ленты, которая сформирована из наших подписок, Instagram собирает ленту рекомендаций. Туда входят все посты, которые потенциально могут вам понравиться — мы просто пройдём вниз по этой ленте. Выйдем с вероятностью 1/5 или когда кончится, чтобы долго не засиживаться.
def scroll_explore(driver): driver.get('https://www.instagram.com/explore') time.sleep(3) scrolling = True last_height = driver.execute_script("return document.body.scrollHeight") while scrolling: driver.execute_script("window.scrollTo(0, document.body.scrollHeight);") time.sleep(random.randint(4,10)) new_height = driver.execute_script("return document.body.scrollHeight") if new_height == last_height or random.randint(1, 5) == 1: scrolling = False last_height = new_height
Итог
Теперь можно собрать все функции вместе — создаём новый driver, проводим авторизацию, считаем число новых сторис и сообщений, просматриваем сторис, переходим в рекомендуемые подписки и листаем ленту. В конце печатаем полученные данные — число новых сообщений, запросов и историй друзей.
driver = webdriver.Chrome(ChromeDriverManager().install()) authorize(driver) queries_text, messages_count = messages_count(driver) stories_count = get_stories_count(driver) watch_stories(driver) scroll_recomendations(driver) scroll_feed(driver) scroll_explore(driver) if queries_text is not None: print(queries_text) else: print('Нет новых запросов на диалог') print('Новых сообщений:', messages_count) print('Новых историй:', stories_count)
Вытаскиваем данные из Instagram
Давайте разберемся, как с помощью достаточно простого кода на python можно вытащить из инстаграмма разные данные, находящиеся в открытом доступе.
Intro
При фазовом переходе из состояния Employed в Self-Employed я погрузился в собственные проекты, которые давно хотел сделать. После парочки телеграм ботов с e-acquiring я решил попытать счастья с Instagram. Как человеку, прежде работавшему только с готовыми и подчищенными данными, мне было интересно познакомиться с процессом добычи данных поближе.
Если вам лень читать всё, то вот демонстрационный бот в Телеграм, который может вытаскивать некоторые данные из Instargam.
На данный момент бот может обрабатывать несколько запросов
- Случайный выбор подписчика / подписчиков аккаунта
- Случайный выбор пользователя / пользователей, поставивших лайк под постом
- Случайный выбор комментатора / комментаторов, оставивших комментарий под постом
- Общие подписчики для двух пользователей
- user-info JSON (информация о пользователе Instagram в том виде в котором она хранится на серверах)
- media-info JSON (информация о публицации в Instagram в том виде, в котором она хранится на серверах)
P.S. Надеюсь, он не упадет под натиском запросов
P.P.S И надеюсь, что профиль в инсте, через который проводятся запросы не заблочат
Inspiration
Во многом меня вдохновила статья, где анализируются самые популярные геотеги Москвы, и телеграм канал её автора. Мне стало интересно, а как вообще вытаскиваются данные из социальных сетей.
Какое API выбрать?
Начнем с того, какие библиотеки использовать. Так как я пишу на питоне, то библиотеки выбирал под него.
У Facebook есть официальное API для взаимодействия с Instagram. Это API Graph и API Instagram Basic Display. Процесс его настройки и использования показался мне чрезмерно сложным, поэтому я решил поискать решение попроще.
Из неофициальных API есть сравнительно популярный InstaPy (12k GitHub), работающий на базе Selenium. Мне такой фреймворк показался громоздким.
После нескольких часов поисков мой выбор пал на достаточно удобную библиотеку instabot, сама библиотека, документация.
Прежде чем мы начнем разбираться с кодом, стоит сделать пару замечаний. Сразу оговорюсь, что я достаточно скептически отношусь к использованию таких фреймворков для автоматизации активности (лайки, комментарии, подписки) с целью увеличения аудитории.
Инстраграм не особо хорошо относится к использованию такого рода библиотек в целях раскрутки, да и в целом, к «нечеловеческой» активности относится негативно. Поэтому не рекомендую использовать их на своём основном аккаунте. Не знаю, какая вероятность того, что вас могут заблокировать, но она явно отлична от нуля.
Мой основной интерес был в том, чтобы поиграться с данными.
Что можно сделать?
В рамках данной статьи я расскажу о том, как можно получать следующую информацию:
- Подписки и подписчики определенного аккаунта
- Пользователи поставившие лайк / оставившие комментарий
- Посты определенного пользователя
- Информация о пользователе
- Загрузка изображений из Instagram
Гораздо интереснее рассматривать такой процесс сбора информации не как изолированную задачу, а как задачу прикладную. Поэтому для каждого пункта я нашёл некоторые реальные задачи и показал, как их можно решить.
Список подписчиков
Представим ситуацию, что вы юный блоггер, и для расширения аудитории решили провести розыгрыш. Новый год, тем более скоро, так что пример актуальный. Допустим, что основной критерий розыгрыша — быть подписанным на вас.
Таким образом мы можем сформулировать задачу — как случайным образом выбрать одного или несколько подписчиков, чтобы вручить им подарки.
Давайте посмотрим, как это можно сделать. Для начала необходимо авторизоваться. К слову, чтобы не подвергать риску свой основной аккаунт я создал новый и все запросы проводил через него.
from instabot import Bot bot = Bot() bot.login(username = INST_USERNAME, password = INST_PASSWORD)
После того, как мы авторизовались — мы можем получить список подписчиков и список подписок для любого пользователя с открытым аккаунтом. Осуществляется это следующим образом.
user_followers = bot.get_user_followers(username) # Список подписчиков user_following = bot.get_user_following(username) # Список подписок
Стоит заметить, что в данном случае мы увидим что-то вида
['1537613519', '7174630295', '5480786626', . , '6230009450', '4294562266', '27518898596']
Это user_id пользователей. Для того чтобы получить юзернеймы пользователей нужно сделать следующее:
user_id = user_followers[i] username = bot.get_username_from_user_id(user_id)
Однако стоит иметь ввиду, что запрос get_username_from_user_id работает не мгновенно и внутри программы лучше работать с user_id а резолвить его в юзернейм только при необходимости.
Выбрать случайным образом несколько подписчиков username можно, например, вот так
user_followers = bot.get_user_followers(username) amount = len(user_followers) winners = np.random.choice(amount, N, replace=False) winners_usernames = [bot.get_username_from_user_id(users_followers[i]) for i in winners]
Учитывая, что блоггеры любят проводить коллективные розыгрыши — можно получить списки подписчиков для нескольких аккаунтов и уже среди множества пользователей, подписанных на все необходимые профили, выбирать победителей.
Список людей, поставивших лайк
Продолжая эксплуатировать блогерскую тематику, представим, что вы проводите розыгрыш не только среди пользователей, подписанных на вас, но и оставивших лайк под вашим постом. Как получить список пользователей в таком случае?
Для начала нужно получить media_pk из ссылки на ваш пост:
media_link = 'https://www.instagram.com/p/CJQRFj4Jq1G/?utm_source=ig_web_copy_link' media_pk = bot.get_media_id_from_link(media_link)
Тогда для списка людей, поставивших лайк:
users_liked = bot.get_media_likers(media_pk)
Список людей, оставивших комментарий:
users_commented = bot.get_media_commenters(media_pk)
Также можно получить список комментариев под постом
comments = bot.get_media_comments(media_pk) # 20 last comments all_comments = bot.get_media_comments_all(media_pk) #all comments
Дальше с этими списками можно работать точно также, как и в предыдущем пункте. Например, можно выбирать победителей среди тех пользователей, которые подписаны на вас и которые оставили лайк и комментарий под последними N публикациями.
Посты пользователя
Давайте перейдём к более интересным вещам и посмотрим, какую информацию о наших постах предоставляет Instagram. В библиотеке есть методы, позволяющие получить информацию о постах пользователя.
Например вот так можно получить идентификаторы последних 20 постов пользователя:
twony_last_medias = bot.get_user_medias(username, filtration = None)
Параметр filtration отвечает за фильтрацию постов. Он выбрасывает посты, количество лайков которых либо меньше bot.min_likes_to_like , либо больше bot.max_likes_to_like Эти параметры можно настроить и поставить filtration = True .
В данном случае twony_last_medias будет иметь вид:
twony_last_medias = [ '2442850452985735104_381142195', . , '2242166462844436702_381142195']
Где первая часть, до _ — это идентификатор поста, а вторая часть — user_id , то есть media_id = _
Посмотрим, какую информацию о постах нам может дать Instagram:
media_id = twony_last_medias[i] media_info = bot.get_media_info(media_id)[0] # [0] - потому что функция возвращает массив, где всего один элемент
media_info — это JSON следующего формата:
Пример полноценного JSONa
Давайте посмотрим как выглядит JSON для поста в инстаграме. Вы можете получить такой же JSON для любой своей публикации, обратившись к боту.
В media_info содержится вся доступная о посте информация, будь это геопозиция, отмеченные пользователи, комментарии и количество лайков.
Ниже пример поста и JSON соответсвующий ему.

< "taken_at": 1584960820, "pk": 2271138434299490600, "id": "2271138434299490618_381142195", "device_timestamp": 1584960663222320, "media_type": 1, "code": "B-EtF98KHE6", "client_cache_key": "MjI3MTEzODQzNDI5OTQ5MDYxOA==.2", "filter_type": 0, "location": < "pk": 500801171, "short_name": "Bolshaya Nikitskaya Street", "facebook_places_id": 103200906400409, "external_source": "facebook_places", "name": "Bolshaya Nikitskaya Street", "address": "", "city": "", "lng": 37.598333333333, "lat": 55.7575 >, "lat": 55.7575, "lng": 37.598333333333, "user": < "pk": 381142195, "username": "oak.raw", "full_name": "ℕ. . ", "is_private": false, "profile_pic_url": "https://scontent-hel3-1.cdninstagram.com/v/t51.2885-19/s150x150/83355342_622022825292167_3709019308832063488_n.jpg?_nc_ht=scontent-hel3-1.cdninstagram.com&_nc_ohc=dvMUAIilXsIAX9jbV-1&tp=1&oh=7fdc985600993dfa2b1a5da2a117f58c&oe=6011B3AD", "profile_pic_id": "2233042169810667649_381142195", "friendship_status": < "following": true, "outgoing_request": false, "is_bestie": false, "is_restricted": false >, "is_verified": false, "has_anonymous_profile_picture": false, "is_unpublished": false, "is_favorite": false, "latest_reel_media": 1608919065, "account_badges": [], "story_reel_media_ids": [] >, "can_viewer_reshare": true, "caption_is_edited": false, "comment_likes_enabled": true, "comment_threading_enabled": true, "has_more_comments": true, "next_max_id": 18104082043182496, "max_num_visible_preview_comments": 2, "preview_comments": [ < "pk": 17865118795863322, "user_id": 1232001680, "text": "О, магазинус Лебедева:)", "type": 0, "created_at": 1590937324, "created_at_utc": 1590937324, "content_type": "comment", "status": "Active", "bit_flags": 0, "did_report_as_spam": false, "share_enabled": false, "user": < "pk": 1232001680, "username": "juliam_moroz", "full_name": "Julia Moroz", "is_private": false, "profile_pic_url": "https://scontent-hel3-1.cdninstagram.com/v/t51.2885-19/s150x150/130863979_442658400430632_6468083327923303795_n.jpg?_nc_ht=scontent-hel3-1.cdninstagram.com&_nc_ohc=QoXEey9rFtsAX-KQPXk&tp=1&oh=925b051f9b220184e2c6da8b13dced83&oe=600F1F50", "profile_pic_id": "2463153806610276064_1232001680", "is_verified": false >, "is_covered": false, "media_id": 2271138434299490600, "has_translation": true, "has_liked_comment": false, "comment_like_count": 1, "private_reply_status": 0 >, < "pk": 18104082043182496, "user_id": 381142195, "text": "@juliam_moroz да)", "type": 2, "created_at": 1590937557, "created_at_utc": 1590937557, "content_type": "comment", "status": "Active", "bit_flags": 0, "did_report_as_spam": false, "share_enabled": false, "user": < "pk": 381142195, "username": "oak.raw", "full_name": "ℕ. . ", "is_private": false, "profile_pic_url": "https://scontent-hel3-1.cdninstagram.com/v/t51.2885-19/s150x150/83355342_622022825292167_3709019308832063488_n.jpg?_nc_ht=scontent-hel3-1.cdninstagram.com&_nc_ohc=dvMUAIilXsIAX9jbV-1&tp=1&oh=7fdc985600993dfa2b1a5da2a117f58c&oe=6011B3AD", "profile_pic_id": "2233042169810667649_381142195", "is_verified": false >, "is_covered": false, "media_id": 2271138434299490600, "parent_comment_id": 17865118795863322, "has_liked_comment": false, "comment_like_count": 0, "private_reply_status": 0 > ], "can_view_more_preview_comments": true, "comment_count": 7, "inline_composer_display_condition": "impression_trigger", "inline_composer_imp_trigger_time": 5, "image_versions2": < "candidates": [ < "width": 1080, "height": 1350, "url": "https://scontent-hel3-1.cdninstagram.com/v/t51.2885-15/e35/p1080x1080/90302974_101307918155463_880925762909832594_n.jpg?_nc_ht=scontent-hel3-1.cdninstagram.com&_nc_cat=103&_nc_ohc=p4i6ab9qK3MAX_Je38D&tp=1&oh=47acaea745cfc8601a20d95e0d847370&oe=6011BB64&ig_cache_key=MjI3MTEzODQzNDI5OTQ5MDYxOA%3D%3D.2", "scans_profile": "e35", "estimated_scans_sizes": [ 34282, 68565, 102848, 137131, 171414, 191812, 244088, 277418, 308546 ] >, < "width": 360, "height": 450, "url": "https://scontent-hel3-1.cdninstagram.com/v/t51.2885-15/e35/p360x360/90302974_101307918155463_880925762909832594_n.jpg?_nc_ht=scontent-hel3-1.cdninstagram.com&_nc_cat=103&_nc_ohc=p4i6ab9qK3MAX_Je38D&tp=1&oh=7911d48a74cbf2c1c05bf0ef5b16e666&oe=60121616&ig_cache_key=MjI3MTEzODQzNDI5OTQ5MDYxOA%3D%3D.2", "scans_profile": "e35", "estimated_scans_sizes": [ 4342, 8685, 13028, 17370, 21713, 26141, 754408, 39084, 39084 ] >] >, "original_width": 1440, "original_height": 1800, "like_count": 108, "has_liked": false, "like_and_view_counts_disabled": false, "top_likers": [ "yar.ok" ], "facepile_top_likers": [ < "pk": 1226384634, "username": "yar.ok", "full_name": "YAROSLAV OKATEV", "is_private": false, "profile_pic_url": "https://scontent-hel3-1.cdninstagram.com/v/t51.2885-19/s150x150/91143836_1482909661889518_2687135057402920960_n.jpg?_nc_ht=scontent-hel3-1.cdninstagram.com&_nc_ohc=ZAsnt6I4_7sAX88ZA8A&tp=1&oh=88639d9237f3fc4d578890d57e66f21a&oe=601281EA", "profile_pic_id": "2275072905746358933_1226384634", "is_verified": false >, < "pk": 306914465, "username": "nastianastacia", "full_name": "Nastia", "is_private": false, "profile_pic_url": "https://scontent-hel3-1.cdninstagram.com/v/t51.2885-19/s150x150/15802713_1158590314260662_7281072673235402752_n.jpg?_nc_ht=scontent-hel3-1.cdninstagram.com&_nc_ohc=0lg_5gFmnsUAX-07OSA&tp=1&oh=875accdaf1bc2d1bb72d9442445450a9&oe=6011653B", "profile_pic_id": "1423562998005751579_306914465", "is_verified": false >, < "pk": 236827679, "username": "terzivladida", "full_name": "Терзи Владислав | Фотограф", "is_private": false, "profile_pic_url": "https://scontent-hel3-1.cdninstagram.com/v/t51.2885-19/s150x150/122422351_3763193670359540_4602498721748722003_n.jpg?_nc_ht=scontent-hel3-1.cdninstagram.com&_nc_ohc=df9YioDKtSsAX8kLHJT&tp=1&oh=952d54cb77d1029bc88070ccaf6d99bb&oe=600FFBAD", "profile_pic_id": "2425286083698432257_236827679", "is_verified": false >], "photo_of_you": false, "usertags": < "in": [ < "user": < "pk": 253581477, "username": "temalebedev", "full_name": "Artemy Lebedev", "is_private": false, "profile_pic_url": "https://scontent-hel3-1.cdninstagram.com/v/t51.2885-19/s150x150/58411085_1127266294129169_3154601655836606464_n.jpg?_nc_ht=scontent-hel3-1.cdninstagram.com&_nc_ohc=aTk3egGzdp4AX8aexRy&tp=1&oh=b689c9d264aae438a0430d5d34a0c64c&oe=600FB372", "profile_pic_id": "2032755399167097999_253581477", "is_verified": true >, "position": [ 0.6723027252, 0.18760061960000002 ], "start_time_in_video_in_sec": null, "duration_in_video_in_sec": null > ] >, "can_see_insights_as_brand": false, "caption": < "pk": 17866702039715754, "user_id": 381142195, "text": "ТАКС. ДАВНО НИЧЕГО НЕ ПИСАЛ ⠀\n\nТак получается, что по возвращению из путешествий сложно заставить себя что-либо написать и выложить. Нет необходимых инфоповодов, мотивирующих это делать. Нет внутреннего ощущения насыщенности жизни, которое и побуждает делиться классными кадрами и интересным историями. ⠀\n\nВ данном случае в качестве инфоповода послужил классный респиратор.", "type": 1, "created_at": 1584960823, "created_at_utc": 1584960823, "content_type": "comment", "status": "Active", "bit_flags": 0, "did_report_as_spam": false, "share_enabled": false, "user": < "pk": 381142195, "username": "oak.raw", "full_name": "ℕ. . ", "is_private": false, "profile_pic_url": "https://scontent-hel3-1.cdninstagram.com/v/t51.2885-19/s150x150/83355342_622022825292167_3709019308832063488_n.jpg?_nc_ht=scontent-hel3-1.cdninstagram.com&_nc_ohc=dvMUAIilXsIAX9jbV-1&tp=1&oh=7fdc985600993dfa2b1a5da2a117f58c&oe=6011B3AD", "profile_pic_id": "2233042169810667649_381142195", "friendship_status": < "following": true, "outgoing_request": false, "is_bestie": false, "is_restricted": false >, "is_verified": false, "has_anonymous_profile_picture": false, "is_unpublished": false, "is_favorite": false, "latest_reel_media": 1608919065, "account_badges": [], "story_reel_media_ids": [] >, "is_covered": false, "media_id": 2271138434299490600, "has_translation": true, "private_reply_status": 0 >, "can_viewer_save": true, "organic_tracking_token": "eyJ2ZXJzaW9uIjo1LCJwYXlsb2FkIjp7ImlzX2FuYWx5dGljc190cmFja2VkIjp0cnVlLCJ1dWlkIjoiYTk2MWE2OWMzY2ViNDQ2NGE1NDMzMWRlMjNiNWEyODAyMjcxMTM4NDM0Mjk5NDkwNjE4Iiwic2VydmVyX3Rva2VuIjoiMTYwODk5MzA4ODMxMXwyMjcxMTM4NDM0Mjk5NDkwNjE4fDQ1Mzc1MjU2NzQzfDgxMzI2NWY4YzM2MmIwZDVhMzg3ZTdmNWQ2YjlmYmQyNmQ3ODQ5NzUzMWIzYTk0ZDhkM2VlZmUzNTVkMjQ4MzEifSwic2lnbmF0dXJlIjoiIn0=", "sharing_friction_info": < "should_have_sharing_friction": false, "bloks_app_url": null >, "is_in_profile_grid": false, "profile_grid_control_enabled": false, "is_shop_the_look_eligible": false, "deleted_reason": 0, "integrity_review_decision": "pending" >
Для постов другого формата
В постах содержащих видео или несколько изображений (карусель) содержатся ещё такие поля:
"video_versions" "video_duration" "carousel_media" "number_of_qualities" "title" "video_dash_manifest" "view_count" "product_type" "video_codec" "is_post_live" "media_cropping_info" "thumbnails" "is_dash_eligible" "carousel_media_count" "has_audio" "nearly_complete_copyright_match"
Как уже было замечено ранее, в media_info содержится вся доступная о посте информация, будь это геопозиция, отмеченные пользователи, комментарии (а если быть точным их превью, preview_comments ) и количество лайков.
Ниже расшифровка для некоторых полей:
- taken_at , pk , id , device_timestamp — время создания поста и его идентификаторы.
- media_type — тип контента внутри поста. Одно изображение / несколько изображений / видео / пост в IGTV.
- location , lng , lat — всё что относится к геопозиции, указанной в посте. Объект location содержит информацию о геотеге (например facebook_places_id , address , city ), lng и lat — координаты геотега. Интересно кстати, что координаты дублируются, они есть как в объекте location , так и в самом media_info .
- usertags — объект, содержащий информацию об отмеченных пользователях.
Давайте посмотрим на некоторые необычные поля, которые также есть в этом JSON. Больше всего меня смутили два поля — top_likers и facepile_top_likers . Как оказалось, это лайки тех пользователей, которые отображаются непосредственно до количества лайков («Нравится username и еще N пользователям», в мобильной версии есть еще три маленьких круглых картинки до этой надписи). Готов предположить, что Instagram показывает таким образом лайки тех пользователей, которых он считает наиболее интересными и важными для нас.
top_likers содержит в себе только один username и используется для текстовой аннотации. facepile_top_likers отвечает за визуальную аннотацию лайков, этот объект содержит в себе три профиля пользователей, где у каждого профиля указана ссылка на его аватарку ( profile_pic_url ).
Как это выглядит?
Интересно, кстати, что media_info содержит поле organic_tracking_token . Как утверждает Инстраграм — таким образом происходит поддержка брендов, занимающихся производством органической продукции.
А какие данные можно получить о пользователе?
Посмотрим, какую информацию Instagram предоставляет о нас — рядовых пользователях. На примере моего любимого фотографа.
Такая структура данных содержит основные данные о профиле (в том случае, если он открытый, если профиль закрытый — то данных будет гораздо меньше). Также тут есть некоторые поля для отображения общих подписчиков. Ещё в такой структуре данных Инстаграм может присылать список аккаунтов, на которые он рекомендует вам подписаться.
< "pk": 566674424, "username": "shortstache", "full_name": "Garrett King", "is_private": false, "profile_pic_url": "https://scontent-hel3-1.cdninstagram.com/v/t51.2885-19/s150x150/130951392_133135768583933_6026107970965456642_n.jpg?_nc_ht=scontent-hel3-1.cdninstagram.com&_nc_ohc=gmxi213GpY4AX8cbdpz&tp=1&oh=ad1ca4820958615dc5e99859ef101b1d&oe=60134F33", "profile_pic_id": "2463797374104100463_566674424", "is_verified": false, "has_anonymous_profile_picture": false, "media_count": 2594, "geo_media_count": 0, "follower_count": 485647, "following_count": 1090, "following_tag_count": 0, "biography": "?Huntington Beach, CA\nBehind the scenes @longstache \ninfo@shortstache.com", "biography_with_entities": < "raw_text": "?Huntington Beach, CA\nBehind the scenes @longstache \ninfo@shortstache.com", "entities": [ < "user": < "id": 2264445914, "username": "longstache" >> ] >, "external_url": "http://santogallery.com/garrett-king", "external_lynx_url": "https://l.instagram.com/?u=http%3A%2F%2Fsantogallery.com%2Fgarrett-king&e=ATOlDiBCCUHXljpYDutSTWgfd8g3dL5JwubDPnxKKowaNqriJSOcQSmZoZp6cEyK1gYK5DnB3Zo5UXaFQfTISio&s=1", "total_igtv_videos": 30, "has_igtv_series": false, "total_clips_count": 1, "total_ar_effects": 0, "usertags_count": 7399, "is_favorite": false, "is_favorite_for_stories": false, "is_favorite_for_igtv": false, "is_favorite_for_highlights": false, "live_subscription_status": "default", "is_interest_account": true, "has_chaining": true, "hd_profile_pic_versions": [ < "width": 320, "height": 320, "url": "https://scontent-hel3-1.cdninstagram.com/v/t51.2885-19/s320x320/130951392_133135768583933_6026107970965456642_n.jpg?_nc_ht=scontent-hel3-1.cdninstagram.com&_nc_ohc=gmxi213GpY4AX8cbdpz&tp=1&oh=2947433220502b82b19ddc9c7931bb43&oe=601512CB" >, < "width": 640, "height": 640, "url": "https://scontent-hel3-1.cdninstagram.com/v/t51.2885-19/s640x640/130951392_133135768583933_6026107970965456642_n.jpg?_nc_ht=scontent-hel3-1.cdninstagram.com&_nc_ohc=gmxi213GpY4AX8cbdpz&tp=1&oh=43803ae7d9967b4195b1b6751227723f&oe=60161EF2" >], "hd_profile_pic_url_info": < "url": "https://scontent-hel3-1.cdninstagram.com/v/t51.2885-19/130951392_133135768583933_6026107970965456642_n.jpg?_nc_ht=scontent-hel3-1.cdninstagram.com&_nc_ohc=gmxi213GpY4AX8cbdpz&oh=b8f622f8d82b633018a28b4c5b59b24b&oe=6016354B", "width": 1080, "height": 1080 >, "mutual_followers_count": 1, "profile_context": "Followed by oak.raw", "profile_context_links_with_user_ids": [ < "start": 12, "end": 19, "username": "oak.raw" >], "profile_context_mutual_follow_ids": [ 381142195 ], "show_shoppable_feed": false, "shoppable_posts_count": 0, "can_be_reported_as_fraud": false, "merchant_checkout_style": "none", "seller_shoppable_feed_type": "none", "has_highlight_reels": true, "has_guides": false, "is_eligible_for_smb_support_flow": true, "displayed_action_button_partner": null, "smb_delivery_partner": null, "smb_support_partner": null, "smb_donation_partner": null, "smb_support_delivery_partner": null, "displayed_action_button_type": "", "direct_messaging": "", "address_street": "", "business_contact_method": "", "category": "Photographer", "city_id": 0, "city_name": "", "contact_phone_number": "", "is_call_to_action_enabled": false, "latitude": null, "longitude": null, "public_email": "info@shortstache.com", "public_phone_country_code": "", "public_phone_number": "", "zip": "", "instagram_location_id": "", "is_business": false, "account_type": 3, "professional_conversion_suggested_account_type": 3, "can_hide_category": true, "can_hide_public_contacts": true, "should_show_category": true, "should_show_public_contacts": true, "account_badges": [], "whatsapp_number": "", "include_direct_blacklist_status": true, "is_potential_business": true, "show_post_insights_entry_point": true, "is_bestie": false, "has_unseen_besties_media": false, "show_account_transparency_details": true, "auto_expand_chaining": false, "highlight_reshare_disabled": false, "is_memorialized": false, "open_external_url_with_in_app_browser": true >
Как скачать изображение
Для того чтобы скачать изображение необходимо найти в этом JSON ссылку на фотографию, которая хранится либо в image_versions2 , в случае когда в посте всего одна фотография, либо в carousel_media , когда в посте фотографии несколько. carousel_media содержит в себе изображения, которые хранятся в виде объектов image_versions2
Пример объекта image_versions2 :
Можно заметить, что Инстаграм хранит у себя две версии фотографии: большего и меньшего разрешения. Судя по всему, фотография меньшего разрешения нужна для того чтобы отображать пост в ленте.
Так как в библиотеке не работала функция загрузки изображений, я написал свой вариант. Тут учтено, что в одном посте может быть как одна, так и несколько фотографий. Если же пост содержит видео, то этот код скачает фотографию, которая стоит на обложке видео.
""" filename goes without an extention """ import requests def donwnload_photo(media_id, filename): media = bot.get_media_info(media_id)[0] if ("image_versions2" in media.keys()): url = media["image_versions2"]["candidates"][0]["url"] response = requests.get(url) with open(filename + ".jpg", "wb") as f: response.raw.decode_content = True f.write(response.content) elif("carousel_media" in media.keys()): for e, element in enumerate(media["carousel_media"]): url = element['image_versions2']["candidates"][0]["url"] response = requests.get(url) with open(filename + str(e) + ".jpg", "wb") as f: response.raw.decode_content = True f.write(response.content)
Представим себе ситуацию, что мы хотим скачать некоторое количество изображений из профиля фотографа, который нам нравится:
twony_last_medias = bot.get_user_medias("shortstache", filtration = None) for e,media_id in enumerate(twony_last_medias): donwnload_photo(media_id, "img_" + str(e))
Несколько загруженных таким образом фотографий невероятного shortstache. Удивительно, насколько Инстаграм сжал эти фотографии без видимой потери качества.



Outro

С каждым часом, проведённым в социальных сетях, мы предоставляем информационным компаниям всё больше и больше информации о себе. А развитие технологий анализа этих данных позволяет уже не просто знать что-то о нас и нашем поведении, но и предсказывать наши наиболее вероятные действия, или даже ненавязчиво влиять на процесс совершения этих действий. Если честно, то меня всегда завораживала та сила, которая есть у компаний, возможно, знающих о нас больше, чем мы сами.
Надеюсь, у меня получилось вдохновить вас на какой-нибудь интересный проект, базирующийся на данных из Instagram. Лично у меня чешутся руки поискать какие-нибудь нетривиальные закономерности, например, сравнить как отличается контент и геотеги публикаций travel блоггеров за 2019 и 2020 год. Ммм, А если ещё и прикрутить куда-нибудь нейросетки и попробовать самому сделать рекомендательную систему для классных фотографий (не зря же два года CV занимался). Ненавязчиво оставлю ссылку на мой телеграм канал, где я буду писать про дальнейшие свои изыскания в этой области.
К слову, если вы вдруг будете делать что- то связанное с Instagram, то вот максимально актуальная на сегодняшний день библиотека.
Saved searches
Use saved searches to filter your results more quickly
Cancel Create saved search
You signed in with another tab or window. Reload to refresh your session. You signed out in another tab or window. Reload to refresh your session. You switched accounts on another tab or window. Reload to refresh your session.
instagram bot detection
SergioCSC/instabot
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Switch branches/tags
Branches Tags
Could not load branches
Nothing to show
Could not load tags
Nothing to show
Name already in use
A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
Cancel Create
- Local
- Codespaces
HTTPS GitHub CLI
Use Git or checkout with SVN using the web URL.
Work fast with our official CLI. Learn more about the CLI.
Sign In Required
Please sign in to use Codespaces.
Launching GitHub Desktop
If nothing happens, download GitHub Desktop and try again.
Launching GitHub Desktop
If nothing happens, download GitHub Desktop and try again.
Launching Xcode
If nothing happens, download Xcode and try again.
Launching Visual Studio Code
Your codespace will open once ready.
There was a problem preparing your codespace, please try again.
Latest commit
Git stats
Files
Failed to load latest commit information.
Latest commit message
Commit time
README.md
Определение, какие из заданных аккаунтов инстаграма являются ботами, какие бизнес аккаунтами, и какие аккаунтами пользователей.
Использованы сторонние библиотеки:
- https://fasttext.cc для определения языка постов и комментариев. Лицензия Creative Commons Attribution-Share-Alike License 3.0.
- https://github.com/cjhutto/vaderSentiment (часть nltk) для определения эмоциональной окраски постов и комментариев. Лицензия MIT License
- https://github.com/merely-useful/py-rse/blob/book/zipf/bin/plotcounts.py для определения степени соответствия закону Ципфа у распределения частотности употребленных пользователем слов. Лицензирование отстутствует, но в этом проекте использован код под лицензией MIT Licence
- Общая информация
- Установка
- Парсинг аккаунтов с инстаграма
- Подготовка данных для нейросети в виде json
- Деление на бот/не бот/бизнес с помощью обученной модели
- Либо обучение модели на размеченных аккаунтах
- Известные проблемы
Все промежуточные данные попадают в папку data кроме одного исключения: при подготовке данных для обучения json складываются в папку learning_datasets.
В папке data/samples есть подпапки с примерами исходных данных и данных промежуточных этапов.
В файле config.py настройки. Например,
SLOW_MODE = 0
— быстрый режим обучения (~5 минут на размеченных аккаунтах из learning_datasets.zip)
SLOW_MODE = 1
— средний режим обучения (с определением тональности постов на русском и английском, ~30 минут)
SLOW_MODE = 2
— медленный режим обучения (~6 часов, используется библиотека huggingface, с определением тональности постов на 10 основных языках)
Остальные важные настройки снабжены комментариями в самом файле. Также есть настройки парсинга (parser_im/parse.py) в файле parser_im/parser_cfg.py
- Linux или Windows. Проверял на на Windows 10 и на Ubuntu 20.04 (внутри WSL).
- python 3.9. Не 3.10, т.к. либа определения языка текста fasttext не устанавливается на 3.10
- минимум 4 гб свободного места на диске, 4 гб оперативной памяти
- git
- авторизация на сервере
- API ключ на сайте parser.im. Его нужно положить в переменную KEY в файл parser_im/parser_cfg.py
- (желательно) архив learning_datasets.zip — набор json с чуть менее 2000 размеченных аккаунтов и постов самых разных ботов, не ботов и бизнес. Либо просто список размеченных названий аккаунтов, как в файле data/samples/input_for_parse/accounts_marked.txt и готовность ждать, пока parser.im напарсит достаточное для обучения (желательно >1000) количество аккаунтов и их постов из этого списка.
Установка в Linux:
mkdirs /create/path/to/project/ cd /create/path/to/project/ git clone ssh://instal@84.252.139.114/home/instal/instabot cd instabot git checkout master python -m venv env # be sure it's python 3.9, not python 3.10 source env/bin/activate python -m pip install -r requirements.txt
Установка в Windows (из-под PowerShell):
mkdirs create\path\to\project\ cd create\path\to\project\ git clone ssh://instal@84.252.139.114/home/instal/instabot cd instabot git checkout master python -m venv env # be sure it's python 3.9, not python 3.10 Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser .\env\Scripts\activate.ps1 python -m pip install -r requirements.txt
Парсинг аккаунтов с инстаграма
Используемые данные: - сайт *parser.im* - файл списка аккаунтов или размеченного списка аккаунтов Выходные данные: - data/accounts.txt - data/posts.txt
Cписок аккаунтов или, в случае обучения нейросети, размеченный список аккаунтов подаются на вход parse.py, он использует сайт parser.im для скачивания с инстаграма признаков и постов по каждому аккаунту (на выходе файлы data/accounts.txt и data/posts.txt). parse.py может работать много часов, постоянно опрашивая parser.im и почти никогда не скачивает всё, так уж работает parser.im. Поэтому можно прервать в любой момент parse.py и довольствоваться тем, что насобиралось.
python parser_im/parse.py data/samples/input_for_parse/accounts_non_marked.txt
Либо (для обучения):
python parser_im/parse.py data/samples/input_for_parse/accounts_marked.txt
Подготовка данных для нейросети в виде json
Используемые данные: - data/accounts.txt - data/posts.txt - (в случае обучения) исходный размеченный файл списка аккаунтов Выходные данные: - data/accounts_with_posts.json (в случае инференса) - learning_datasets/accounts_with_posts.json (в случае обучения) (Если parse.py так и не создал файлы data/accounts.txt и data/posts.txt из-за пробем с parser.im, можно скопировать их в data/ из data/samples/output_of_parse_and_input_for_prepare) Инференс. Полученные c инстаграма признаки и посты в виде файлов data/accounts.txt и data/posts.txt объединяются по аккаунтам в файл data/accounts_with_posts.json, пригодный для нейросети. В нём у всех аккаунтов ключ bot будет установлен в -1 (то есть, неразмеченный аккаунт). Пример команды: python parser_im/prepare.py 3б: Обучение. Если нам нужно создать размеченный json (в целях обучения нейросети), то нужно дополнительно указать исходный файл (т.е. тот, который подавался на вход parse.py) списка аккаунтов, он должен быть размеченным (см. пример data/samples/input_for_parse/accounts_marked.txt). В этом случае json будет помещён не в папку data, а в папку learning_datasets. Также, в случае обучения, если такой файл уже существует -- будет создан новый с названием типа accounts_with_posts_1.json Пример команды:
python parser_im/prepare.py data/samples/input_for_parse/accounts_marked.txt
Инференс (деление на бот/не бот/бизнес с помощью обученной модели)
Используемые данные: - data/accounts_with_posts.json - model/features.pickle - model/depended_features.pickle - model/model.pt - папка third_party_models Выходные данные: - папка third_party_models - model/test_dataframe.h5 - результат инференса (бот/не бот/бизнес) в консоли
python inference.py
Эта команда запускает скрипт (collect.py) сбора и очистки признаков из data/accounts_with_posts.json, используя только признаки, которые использовались при обучении (из файла model/features.pickle). Использование некоторых признаков зависит не только от конкретного аккаунта, но и от других в обучающей выборке — данные о том, как их использовать сохранены в model/depended_features.pickle.
При первом запуске в папку third_party_models скачиваются сторонние модели (~1 Гб) для распознавания языка текста, тональности и т.д.
Очищенные признаки сохраняются в тестовый датафрейм model/test_dataframe.h5. Затем запускается собственно инференс, он использует этот тестовый датафрейм model/test_dataframe.h5, а также модель model/model.pt На выходе получаются разметка модели (бот/не бот/бизнес) по оценке модели в колонке dectected_bot:
0 — не бот (человек) 1 — бот 2 — бизнес аккаунт
В колонке bot везде стоит -1, это значит, что исходные данные не размечены.
Обучение модели на размеченных аккаунтах
Если же нам надо обучить модель на новых данных (включая или нет данные прошлых парсингов инстаграма — модель будет обучаться на всех json из папки learning_datasets), то четыре шага:
- смотрим папку learning_datasets и добавляем/удаляем туда json файлы, на которых мы хотим проводить обучение. Если мы туда сами ничего не добавляли, то там лежит только файл accounts_with_posts.json (или несколько таких с названиями типа accounts_with_posts_1.json, если парсинг размеченных аккаунтов выполнялся несколько раз). Рекомендуется распаковать в эту папку архив learning_datasets.zip, он содержит более 1000 размеченных аккаунтов и постов самых разных ботов, не ботов и бизнес.
-
сбор данных в датафрейм, очистка:
Используемые данные: - все json из папки learning_datasets - папка third_party_models Выходные данные: - папка third_party_models - model/features.pickle - model/depended_features.pickle - model/train_dataframe.h5 - model/test_dataframe.h5
Пример команды:
python collect.py
Используемые данные: - model/train_dataframe.h5 Выходные данные: - model/model.pt
Пример команды без рисования графика:
python learning.py
Пример команды с графиком:
python plot.py
-
оценка качества обучения на тестовом датасете: Входные данные:
- model/model.pt - model/test_dataframe.h5
Выходные данные (в консоли):
- процент правильных ответов accuracy - таблица с оценкой качества обучения. Если данных в тестовом датафрейме оказалось слишком мало (присутствуют размеченные аккаунты не всех типов из бот/не бот/бизнес), то таблица не выводится. - результат инференса (бот/не бот/бизнес)
Пример команды:
python inference.py -l # или python inference.py --after-learning
Пример вывода в консоли:
accuracy: 0.93896484375 precision recall f1-score support human 0.89 0.86 0.87 97 bots 0.99 0.99 0.99 144 business 0.92 0.94 0.93 152 saved_pk saved_username bot detected_bot 49263250197 vfs_dieerste 2 2 2197455134 alfiyatorty 2 0 1662267159 juvidesigns 2 2 52367178006 iubzn101 1 1 50012820036 rozaanikina18 1 1
- Иногда при нарушении порядка запуска скриптов происходит нарушение консистентности данных: количество признаков модели из файла model/model.pt не соответствует количеству признаков в файле model/test_dataframe.h5, откуда inference.py берёт данные аккаунтов для вывода результата бот/не бот/бизнес. Выглядит это в виде исключения RuntimeError при исполнении inference.py:
RuntimeError: Error(s) in loading state_dict for InstaNet: size mismatch for fc1.weight: copying a param with shape torch.Size([5, 146]) from checkpoint, the shape in current model is torch.Size([5, 143]).
-
Иногда при установке библиотек:
python -m pip install -r requirements.txt
Возникает ошибка, содержащая строку:
Running setup.py install for fasttext did not run successfully.
Это происходит, если используется python, отличный от версии 3.9. Нужно установить эту версию питона и (необязательно) установить её в качестве дефолтной. Например, под Linux:
apt install python3.9 update-alternatives --install /usr/bin/python3 python3 /usr/bin/python3.9 42 update-alternatives --config python3
python.exe plot.py
python learning.py
возникает ошибка
OSError: ``../instabot/model/train_dataframe.h5`` does not exist
Это означает, что предварительно не был запущен сбор данных:
python collect.py
python collect.py
возникает ошибка:
ValueError: No objects to concatenate
python.exe inference.py -l
таблица оценки качества обучения не выводится, а колонка bot, вместо того, чтобы содержать размеченные значения для аккаунтов, содержит -1. Это происходит, если нарушен порядок запуска скриптов, то есть между запуском
python collect.py
python inference.py -l
был вызван
python inference.py # (без параметров)