
Предисловие
Решил выйти на новый рынок сбыта, тем более, целевая аудитория моего интернет-магазина, не имеющая аккаунтов в Instagram, давно интересовалась появлением дубликата в VK. Идея хорошая, но постов на странице сотни, соответственно вручную работать ctrl+c ctrl+v не хотелось, плюс дальнейшие перспективы обезьянней работы не впечатляли.
Уверенный, что в интернете полно бесплатный решений, я начал гуглить. Естественно, первые страницы поисковой выдачи пестрят платными сервисами, с довольно обширными функционалами. Но мне, всего лишь на всего, надо было перенести все посты со страницы Instagram в паблик VK и в дальнейшем синхронно пополнять его.
Не найдя ничего подходящего
Документация по api Instagram и VK довольно подробная и задачка не кажется сложной. Освободив себе пару вечеров, я приступил к работе. Первым делом необходимо было получить токены как в Instagram, так и в VK. С этим проблем не было, оба были получены за пару минут.
Дальше меня ждал первый подводный камень…
Кросспостинг первых 20 постов
К своему удивлению я обнаружил, что после изменений в политике Instagram, можно было получить json-словарь (ссылки на фото, описание к посту, дату публика��ии…) лишь к последним 20 постам страницы. Мне это подходило для второй задачи – обновление паблика новыми постами время от времени. Потому что новые публикации у меня появляются не так часто и 20 постов — вполне удобно. Было решено сначала взяться за эту задачку.
Получаем сессию VK и объявляем необходимые переменные:
session = vk.Session( access_token='123abc') # вместо 123abc свой токен vk_api = vk.API(session, v='5.85') groupID = '12345678' #id паблика vk upload_url = vk_api.photos.getWallUploadServer(group_id=groupID)['upload_url'] #получаем сервер vk для загрузки data = [] photo_id = 0 attachments = [] direct = 'C:/Users/jo/PycharmProjects/repost/foto' # директория для хранения фото d = date(2019, 3, 21) # дата, с которой начинать публиковать посты data_parsing = int(time.mktime(d.timetuple())) # перевод её в unix Формат
Далее пишем основную функцию. Для начала получим массив данных, с которым и будем работать:
answer = get( 'https://api.instagram.com/v1/users/[id_inst]/media/recent?access_token=[access_token]', verify=True).json()
В запросе необходимо вставить свои данные вместо квадратных скобок. Найти id страницы по username можно, например, здесь.
Если нам что-то вернули идём дальше:
if answer: for x in answer['data']: # для каждого поста из json словаря global photo_id # обнуляем массивы и переменные photo_id = 0 global attachments attachments = [] global data data = [] date_create = x['created_time'] # date_create дата создания в Unix if int(date_create) > data_parsing: n = 0 # название фото по порядку if not os.path.isdir(direct + '/' + date_create): # если нет папки создаем os.makedirs(direct + '/' + date_create) if 'carousel_media' in x: # если несколько фото в посте for a in x['carousel_media']: li = list(a.keys()) # все ключи словаря в массив if li.count('videos') == 0: # если это не видео, мне просто они не нужны req.urlretrieve(a['images']['standard_resolution']['url'], "foto/" + date_create + "/" + str(n) + ".jpg") # загружаем фото к себе в папку if x['caption'] is not None: # описание поста text = str(x['caption']['text']) else: text = ' ' n = n + 1 post_foto(date_create, text) else: # если в посте одно фото req.urlretrieve(x['images']['standard_resolution']['url'], "foto/" + date_create + "/0.jpg") if x['caption'] is not None: # описание поста text = str(x['caption']['text']) else: text = ' ' post_foto(date_create, text)
Каюсь
Знаю, что так работать с глобальными переменными плохо, но размеры скрипта позволяют не вникать в сложности
Итак, если дата публикации поста больше даты которую мы задали, создаём папку, название которой (внимание тавтология, особо впечатлительным пропустить) дата её публикации. Далее идут проверки на количество фото и на видео. Наверняка можно прикреплять и его, мне это просто не нужно. Загружаем фотографии в созданную папку. Берём описание к посту по ключу caption и переходим к функции post_foto:
def post_foto(date_create, text): quantity_foto = len([name for name in os.listdir(direct + "/" + date_create) if os.path.isfile(os.path.join(direct + "/" + date_create, name))]) # количество фото в папке append_attach(quantity_foto, date_create) params = {'attachments': attachments, 'message': text, 'owner_id': '-' + groupID, 'from_group': '1'} vk_api.wall.post(**params) # публикуем пост в VK с заданными параметрами
Определяем количество фото в папке, загружаем их на сервер VK, добавляем в параметры поста и публикуем его в паблике. Добавление в параметры осуществляется с помощью функции append_attach:
def append_attach(x, directory): # добавление в параметры поста for t in range(x): upload_foto(t, directory) global photo_id photo_id = data[t][0]['id'] global attachments attachments.append('photo' + str(data[t][0]['owner_id']) + '_' + str(photo_id)) # добавляем id фото в параметры поста
А непосредственно загрузка фото на сервер VK выполняется функцией upload_foto:
def upload_foto(num_foto, directory): # загрузка фото на сервер request = requests.post(upload_url, files={'photo': open("foto/" + directory + "/" + str(num_foto) + ".jpg", "rb")}) params = {'server': request.json()['server'], 'photo': request.json()['photo'], 'hash': request.json()['hash'], 'group_id': groupID} global data data.append(vk_api.photos.saveWallPhoto(**params))
Со второй задачей разобрались. Скрипт можно выполнять как самому, так и поставить по расписанию (например, в cron, один раз в 15 минут). А как теперь перенести все остальные сотни постов?
Перенос всей страницы
Часть скрипта у нас уже готова, та которая отвечала за сами публикации в VK. Осталось найти способ выкачать все фото и описания к ним. Я не стал заморачиваться с парсингом исходных кодов страниц Instagram и взял готовое решение. На самом деле я уверен, что таких программ много, я использовал первую попавшуюся бесплатную (4K Stogram). Интуитивно понятный интерфейс позволяет быстро справиться со скачкой всех фотографий со страницы. В меню также есть экспорт всех описаний к постам. Нам нужен «*.txt» формат. Осталось лишь разложить все фотографии по папкам (один пост – одна папка) и спарсить по регулярному выражению описания к постам из текстовика.
Разложить фото по папкам нам поможет следующий код:
i = 0 for top, dirs, files in os.walk(os.getcwd()+"\\res\\"): for nm in files: if re.findall(r'\d\d\.\d\d\.\d\d', nm): old_file = os.path.join(top, nm) frq = re.findall(r'\d\d\d\d-\d\d-\d\d \d\d\.\d\d\.\d\d', str(nm)) frq = str(frq[0]) if not os.path.exists(os.getcwd()+"\\res\\" + frq): i = 0 os.makedirs(os.getcwd() + "\\res\\" + frq) new_file = os.path.join(os.getcwd() + "\\res\\" + frq, str(i)+'.jpg') os.rename(old_file, new_file) i = i+1 else: new_file = os.path.join(os.getcwd() + "\\res\\" + frq, str(i)+'.jpg') os.rename(old_file, new_file) i = i+1
Здесь ключевым моментом является время публикации, которое и объединяет несколько фото в одну папку.
Ну а дальше все просто. Открываем каждую папку, загружаем все фотографии на сервер, прикрепляем описание и публикуем. Не забываем про ограничение VK: не более 50-ти постов в день:
f = open(os.getcwd() + "\input_opis\\gusi.txt", "rt", errors="ignore", encoding='utf-8') # открываем текстовик с описаниями data = f.read() # открываем целиком, чтобы не пропустить переносы строк result = re.findall(r'"([^\"]*)"', data, re.S) new_x = [el for el, _ in groupby(result)] # удаляем дубликаты из списка dlina = new_x.__len__() i = 1 f.close() for top, dirs, files in os.walk(os.getcwd() + "\\res\\"): #папка с фото for nm in dirs: attachments = [] photo_id = 0 data = [] DIR = 'C:/Users/jo/PycharmProjects/repost/res/' + nm #путь к папке с фото quantity_foto = len([name for name in os.listdir(DIR) if os.path.isfile(os.path.join(DIR, name))]) post_foto(quantity_foto, nm) attachments.reverse() # разворачиваем список params = {'attachments': attachments, 'message': new_x[dlina - i], 'owner_id': '-' + groupID, 'from_group': '1'} vk_api.wall.post(**params) i = i + 1
Возможно, пошёл не по самому лёгкому и быстрому пути, но результат достигнут. Всем спасибо за внимание, готов ответить на все вопросы и критические замечания по моему
Все 3 скрипта целиком:
Кросспостинг первых 20 постов
from requests import get import urllib.request as req import vk import requests import os import time from datetime import date session = vk.Session( access_token='123abc') vk_api = vk.API(session, v='5.85') groupID = '12345678' #id паблика vk upload_url = vk_api.photos.getWallUploadServer(group_id=groupID)['upload_url'] #получаем сервер vk для загрузки data = [] photo_id = 0 attachments = [] direct = 'C:/Users/jo/PycharmProjects/repost/foto' # директория для хранения фото d = date(2019, 3, 21) # дата, с которой начинать публиковать посты data_parsing = int(time.mktime(d.timetuple())) # перевод её в unix Формат def upload_foto(num_foto, directory): # загрузка фото на сервер request = requests.post(upload_url, files={'photo': open("foto/" + directory + "/" + str(num_foto) + ".jpg", "rb")}) params = {'server': request.json()['server'], 'photo': request.json()['photo'], 'hash': request.json()['hash'], 'group_id': groupID} global data data.append(vk_api.photos.saveWallPhoto(**params)) def append_attach(x, directory): # загрузка на сервер каждой фото из папки и добавление в параметры поста for t in range(x): upload_foto(t, directory) global photo_id photo_id = data[t][0]['id'] global attachments attachments.append('photo' + str(data[t][0]['owner_id']) + '_' + str(photo_id)) # добавляем id фото в параметры поста def post_foto(date_create, text): quantity_foto = len([name for name in os.listdir(direct + "/" + date_create) if os.path.isfile(os.path.join(direct + "/" + date_create, name))]) # количество фото в папке append_attach(quantity_foto, date_create) params = {'attachments': attachments, 'message': text, 'owner_id': '-' + groupID, 'from_group': '1'} vk_api.wall.post(**params) # публикуем пост в VK с заданными параметрами def get_all(): answer = get( 'https://api.instagram.com/v1/users/12345678/media/recent?access_token=123abc', verify=True).json() # получаем большой массив из instagram if answer: for x in answer['data']: # для каждого поста из json словаря global photo_id # обнуляем массивы и переменные photo_id = 0 global attachments attachments = [] global data data = [] date_create = x['created_time'] # date_create дата создания в Unix if int(date_create) > data_parsing: n = 0 # название фото по порядку if not os.path.isdir(direct + '/' + date_create): # если нет папки создаем os.makedirs(direct + '/' + date_create) if 'carousel_media' in x: # если несколько фото в посте for a in x['carousel_media']: li = list(a.keys()) # все ключи словаря в массив if li.count('videos') == 0: # если это не видео, мне просто они не нужны req.urlretrieve(a['images']['standard_resolution']['url'], "foto/" + date_create + "/" + str(n) + ".jpg") # загружаем фото к себе в папку if x['caption'] is not None: # описание поста text = str(x['caption']['text']) else: text = ' ' n = n + 1 post_foto(date_create, text) else: # если в посте одно фото req.urlretrieve(x['images']['standard_resolution']['url'], "foto/" + date_create + "/0.jpg") if x['caption'] is not None: # описание поста text = str(x['caption']['text']) else: text = ' ' post_foto(date_create, text) get_all()
Сортировка фото по папкам
import re import os i = 0 for top, dirs, files in os.walk(os.getcwd()+"\\res\\"): for nm in files: if re.findall(r'\d\d\.\d\d\.\d\d', nm): old_file = os.path.join(top, nm) frq = re.findall(r'\d\d\d\d-\d\d-\d\d \d\d\.\d\d\.\d\d', str(nm)) frq = str(frq[0]) if not os.path.exists(os.getcwd()+"\\res\\" + frq): i = 0 os.makedirs(os.getcwd() + "\\res\\" + frq) new_file = os.path.join(os.getcwd() + "\\res\\" + frq, str(i)+'.jpg') os.rename(old_file, new_file) i = i+1 else: new_file = os.path.join(os.getcwd() + "\\res\\" + frq, str(i)+'.jpg') os.rename(old_file, new_file) i = i+1
Постинг в VK всех публикаций
import os import vk import requests import re from itertools import groupby session = vk.Session( access_token='123abc') vk_api = vk.API(session, v='5.85') groupID = '12345678' upload_url = vk_api.photos.getWallUploadServer(group_id=groupID)['upload_url'] data = [] photo_id = 0 attachments = [] def upload_foto(num_foto, direc): request = requests.post(upload_url, files={'photo': open('res/' + direc + "/" + str(num_foto) + ".jpg", "rb")}) params = {'server': request.json()['server'], 'photo': request.json()['photo'], 'hash': request.json()['hash'], 'group_id': groupID} global data data.append(vk_api.photos.saveWallPhoto(**params)) def post_foto(x, direc): for i in range(x): upload_foto(i, direc) global photo_id photo_id = data[i][0]['id'] global attachments attachments.append('photo' + str(data[i][0]['owner_id']) + '_' + str(photo_id)) f = open(os.getcwd() + "\input_opis\\gusi.txt", "rt", errors="ignore", encoding='utf-8') # открываем текстовик с описаниями data = f.read() # открываем целиком, чтобы не пропустить переносы строк result = re.findall(r'"([^\"]*)"', data, re.S) new_x = [el for el, _ in groupby(result)] # удаляем дубликаты из списка dlina = new_x.__len__() i = 1 f.close() for top, dirs, files in os.walk(os.getcwd() + "\\res\\"): #папка с фото for nm in dirs: attachments = [] photo_id = 0 data = [] DIR = 'C:/Users/jo/PycharmProjects/repost/res/' + nm #путь к папке с фото quantity_foto = len([name for name in os.listdir(DIR) if os.path.isfile(os.path.join(DIR, name))]) post_foto(quantity_foto, nm) attachments.reverse() # разворачиваем список params = {'attachments': attachments, 'message': new_x[dlina - i], 'owner_id': '-' + groupID, 'from_group': '1'} vk_api.wall.post(**params) i = i + 1
