БЕРЕГИСЬ! Instagram отказался от подписок в реальном времени на теги 17 ноября 2015 года, поэтому больше нельзя следовать этому руководству.
Эта статья была рецензирована Джейми Шилдсом , Эдвином Рейносо и Томом Греко . Спасибо всем рецензентам SitePoint за то, что сделали контент SitePoint как можно лучше!
Instagram — это онлайновая сеть для обмена фотографиями, которая позволяет пользователям делать фотографии и видео и обмениваться ими на различных платформах социальных сетей. Пользователи могут персонализировать каждую загружаемую фотографию (например, добавляя различные эффекты) и классифицировать их с помощью хэштега .
В этом уроке мы будем создавать приложение для обновления фотографий в реальном времени. Это позволит пользователям вводить хэштег, на который они хотели бы подписаться. Затем, каждый раз, когда новая фотография с этим хэштегом публикуется в Instagram, она отправляется в наше приложение, которое будет отображать его соответствующим образом.
Мы реализуем наше приложение в Node.js, используя Express в качестве фреймворка. Часть в реальном времени будет реализована с помощью Instagram Photo Updates и Socket.io в реальном времени . Как всегда, код для этого урока доступен в нашем репозитории Github .
Как работают обновления фотографий в реальном времени
Instagram Фото Обновления в режиме реального времени работают, отправляя уведомления на ваш сервер каждый раз, когда новая фотография публикуется в выбранной вами подписке. Вот разбивка:
- Сначала ваш сервер отправляет запрос на подписку в Instagram.
- Instagram получает запрос и подтверждает, что вы действительно хотите подписаться, отправляя ответ, который ваш сервер должен отправить обратно.
- Ваш сервер получает данные и отправляет их обратно в Instagram.
- Если данные совпадают, Instagram начнет отправлять уведомления на ваш сервер.
Существует четыре типа подписок, из которых вы получаете обновления в режиме реального времени: пользователи, теги, местоположения и географическое положение. Вы можете прочитать больше о каждом из них в документации . В этом уроке мы будем использовать только подписку на теги. Это позволяет получать уведомления, когда новая фотография помечена любыми указанными вами тегами.
Зарегистрировать приложение
Первое, что нам нужно сделать, это создать учетную запись Instagram, а затем зарегистрироваться в качестве разработчика .
Далее нам нужно зарегистрировать новое приложение Instagram . Вы можете указать любой действительный URL-адрес для веб-сайта и URL-адрес перенаправления, так как они не нужны для правильной работы нашего приложения.
Как только приложение будет создано, запишите CLIENT ID
CLIENT SECRET
как они понадобятся позже при отправке запросов в API Instagram.
Настройка на стороне сервера
Следующее, что нужно сделать, это клонировать репозиторий и установить зависимости с помощью npm.
git clone [email protected]:sitepoint-editors/express-instagramrealtime.git cd express-instagramrealtime npm install
Это приведет к следующим зависимостям:
- Express — это де-факто стандартная платформа сервера веб-приложений для Node.js. Он используется для обслуживания общедоступной части приложения, а также для получения фото-уведомлений из Instagram.
- Express Handlebars используется для реализации представлений в Express.js.
- body-parser используется для анализа данных формы, предоставленных пользователем. В этом случае данные — это тег, на который пользователь хочет подписаться.
- instagram-node-lib — это библиотека Node.js для работы с API Instagram. Когда у нас есть тег, предоставленный пользователем, эта библиотека используется для подписки на тег.
- socket.io — после подписки на определенный тег Instagram отправляет на сервер уведомления каждый раз, когда публикуется новая фотография с тегом, который мы использовали. Вот где приходит socket.io. Он используется для отправки данных фотографии во внешний интерфейс каждый раз, когда сервер получает новое уведомление.
- момент используется для форматирования метки времени, предоставляемой API Instagram.
Теперь мы готовы посмотреть на приложение. В app.js
сначала нам нужно установить зависимости, которые мы установили.
var express = require('express'); var exphbs = require('express-handlebars'); var moment = require('moment'); var bodyParser = require('body-parser'); var instagram = require('instagram-node-lib'); var app = express(); app.use(bodyParser.json()); app.use(bodyParser.urlencoded({ extended: true })); // set the file name of the default layout app.engine('handlebars', exphbs({defaultLayout: 'main'})); // set the expressJS view engine to handlebars app.set('view engine', 'handlebars'); // set the path to the front-end assets app.use(express.static('public'));
Теперь, когда нам потребовались необходимые зависимости, нам нужно установить CLIENT ID
CLIENT SECRET
в Instagram и CLIENT SECRET
. Вы замените эти значения на значения, созданные при регистрации вашего приложения.
var instagram_client_id = 'YOUR-INSTAGRAM-CLIENT-ID'; var instagram_client_secret = 'YOUR-INSTAGRAM-CLIENT-SECRET'; instagram.set('client_id', instagram_client_id); instagram.set('client_secret', instagram_client_secret);
После того, как мы настроим наш ID и секрет, следующее, что мы собираемся сделать, — это создать экземпляр сервера, который будет работать на порте 4000. Вы можете проверить, запущен ли сервер, используя console.log
для вывода хоста и порта.
var server = app.listen(4000, function(){ var host = server.address().address var port = server.address().port console.log('Example app listening at http://%s:%s', host, port) });
Затем включите socket.io для прослушивания сервера Express. Это связывает socket.io с тем же портом, что и ваш сервер Express, поэтому в дальнейшем вы сможете использовать порт 4000 при подключении к этому сокету на стороне клиента.
var io = require('socket.io').listen(server);
Теперь давайте перейдем к созданию нового маршрута для домашней страницы приложения. Все, что он делает, это визуализирует домашний шаблон.
app.get('/', function(req, res){ res.render('home'); });
Создание видов
В соответствии с конфигурацией рулей по умолчанию все файлы представлений должны храниться в каталоге представлений. Файл home.handlebars
будет отображать элементы управления формы, в которые пользователь home.handlebars
хэштег, который будет использоваться для обновления фотографий в реальном времени:
<div id="form-wrapper"> <div class="form-group"> <label for="tag" class="control-label">Hashtag</label> <input type="text" class="form-control input-lg" id="tag" name="tag" autofocus> </div> <div class="form-group"> <button id="start" class="btn btn-lg btn-block btn-primary">Start</button> </div> </div> <div id="results" class="hidden"> <div class="row"></div> </div>
Каждый раз, когда любой пользователь Instagram публикует новую фотографию с таким хэштегом, она сразу же отображается приложением внутри div с идентификатором results
.
Ранее app.js
файла app.js
мы задали имя файла для макета по умолчанию с помощью следующего кода:
app.engine('handlebars', exphbs({defaultLayout: 'main'}));
В руле файлы макетов хранятся в views/layouts
. В этом каталоге файл main.handlebars
служит основным макетом. Основное содержимое отображается внутри div с идентификатором wrapper
:
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>Real-time Photo Updates</title> <link rel="stylesheet" href="/css/bootstrap.min.css"> <link rel="stylesheet" href="/css/style.css"> </head> <body> <div id="wrapper"> {{{body}}} </div> <script src="/js/jquery.min.js"></script> <script src="/js/jquery.backstretch.min.js"></script> <script src="/js/jquery.imagepreload.min.js"></script> <script src="/js/vague.min.js"></script> <script src="/js/socket.io.min.js"></script> <script src="/js/handlebars.min.js"></script> <script src="/js/moment.min.js"></script> <script src="/js/livestamp.min.js"></script> <script src="/js/script.js"></script> </body> </html>
Как видите, требуется несколько внешних зависимостей. Вот краткое описание каждого:
- Bootstrap — это интерфейсная часть. Это мой личный выбор почти для каждой веб-вещи, которую я создаю. Если вы также хотите использовать Bootstrap, вы можете найти бесплатные темы на bootswatch.com .
- jQuery используется для манипулирования HTML и прослушивания событий кликов на странице. Это также зависимость от 3 библиотек ниже.
- jQuery BackStretch используется для превращения текущей фотографии в фоновое изображение на всю страницу.
- Плагин jQuery Image Preload используется для предварительной загрузки изображения перед его показом пользователю.
- Livestamp помогает поддерживать актуальность текста времени, когда новая фотография не поступает со стороны сервера в течение слишком долгого времени.
- Vague.js применяет фильтр размытия к фоновому изображению.
- руль используется для генерации HTML, который будет использоваться для отображения фотографий.
- момент используется для отображения текста времени на основе метки времени.
Эти зависимости можно найти в public/js
и public/css
. Это потому, что мы указали, что эта папка ( public
) должна содержать наши внешние ресурсы:
app.use(express.static('public'));
Вы также можете использовать Bower для загрузки и управления этими ресурсами. Если вы так решили, обязательно обновите файл .bowerrc, чтобы он использовал указанную вами статическую директорию.
Теперь мы готовы создать файл script.js
.
Мясо и Картофель
Внутри файла script.js
находятся все script.js
действия. Здесь нам нужно использовать функцию $.get
jQuery для извлечения шаблона руля с сервера. После того, как он был выбран, он должен быть скомпилирован и сохранен в переменной. Это шаблон, используемый для построения HTML-кода для отображения фотографии.
var template; $.get('/templates/row.hbs', function(data){ template = Handlebars.compile(data); }, 'html');
Вот как выглядит шаблон руля ( public/templates/row.hbs
):
<div class="row"> <div class="photo-container"> <img src="{{image}}" class="photo"> </div> <div class="photo-details"> <div class="timestamp" data-livestamp="{{created_time}}">{{human_time created_time}}</div> <img src="{{profile_pic}}" class="userphoto" alt="{{user}}"> <a href="http://instagram.com/{{user}}" target="_blank" class="username">{{user}}</a> <div class="caption">{{caption}}</div> </div> </div>
Он содержит код для отображения фотографий нашего приложения.
Вернувшись в script.js
нам нужно подключиться к серверу socket.io.
var socket = io.connect('http://your-server.com:4000');
И зарегистрируйте помощника для преобразования меток времени Unix в удобную для человека форму:
Handlebars.registerHelper('human_time', function(timestamp){ return moment.unix(timestamp).fromNow(); });
Когда нажата кнопка запуска, нам нужно отправить введенный пользователем хэштег на сервер. Как только это будет успешно, мы хотим скрыть форму и показать контейнер с фотографиями.
$('#start').click(function(){ var tag = $('#tag').val(); $.post( '/tag/subscribe', { 'tag': tag }, function(response){ if(response.type == 'success'){ $('#form-wrapper').addClass('hidden'); $('#results').removeClass('hidden'); } } ) });
На серверной стороне (в app.js
) наше приложение должно отписаться от всех текущих подписок в реальном времени, а затем подписаться на новый хэштег, предоставленный пользователем. Мы можем сделать это, используя метод subscribe
в объекте tags
предоставляемом библиотекой instagram-node-lib. Как только сервер получил действительный ответ от Instagram, мы отправляем ответ, что подписка была завершена.
var current_tag; app.post('/tag/subscribe', function(req, res){ current_tag = req.body.tag; console.log('current tag: ' + current_tag); instagram.tags.unsubscribe_all({ complete: function(unsubscribe_data) { if(unsubscribe_data == null){ console.log('unsubscribed from everything!'); instagram.tags.subscribe({ object_id: current_tag, callback_url: 'https://xxxxxxxx.ngrok.io/subscribe', complete: function(subscribe_data){ if(subscribe_data){ res.send({type: 'success'}); } } }); } } }); });
Когда Instagram получает ваш запрос на подписку на новый тег, он отправляет запрос GET
ваш URL обратного вызова. Этот запрос содержит параметр запроса. Все, что нужно сделать серверу, — это отправить его обратно в Instagram для подтверждения.
app.get('/subscribe', function(req, res){ res.send(req.query['hub.challenge']); });
Каждый раз, когда новая фотография с этим тегом публикуется в Instagram, она автоматически отправляет уведомление на ваш сервер. На этот раз это POST
запрос к callback_url
который вы указали (вам придется изменить это в разделе развертывания). Обратите внимание, что этот запрос не содержит никаких данных о размещенной фотографии. Он содержит только данные о времени и подписке, из которой он возник. Вот почему вы должны сделать отдельный запрос, чтобы получить недавно опубликованную фотографию. После получения ответа создайте новый объект с именем photo
а затем сохраните все данные, которые вы хотите вернуть, в новой переменной. В этом случае требуется только следующее: имя пользователя, фотография профиля, отметка времени, с которой было размещено изображение, URL-адрес фотографии и текст заголовка. Наконец, сообщите клиенту, что новая фотография доступна.
app.post('/subscribe', function(req, res){ instagram.tags.recent({ name: current_tag, count: 1, complete: function(data){ var photo = { 'user': data[0].user.username, 'profile_pic': data[0].caption.from.profile_picture, 'created_time': data[0].created_time, 'image': data[0].images.standard_resolution.url, 'caption': data[0].caption.text }; io.sockets.emit('new_photo', photo); } }); });
Отображение результата
Возвращаясь к клиентской стороне (script.js), давайте использовать плагин jQuery Image Preloader для предварительной загрузки изображения при появлении новой фотографии. Это полностью загрузит изображение на клиентской стороне, прежде чем показывать его пользователю. Как только изображение будет предварительно загружено, создайте новый HTML-код, используя template
и данные фотографии. Далее мы собираемся использовать плагин jQuery Backstretch для установки изображения в качестве фонового изображения на всю страницу, а также vague.js для размытия фона. После этого вы можете добавить HTML- fadeIn
на страницу и затем показать его с эффектом fadeIn
. Наконец, удалите последнее изображение, которое было показано.
socket.on('new_photo', function(data){ $.imgpreload(data.image, function() { console.log('loaded a new image'); var first_row = $('#wrapper .row:first'); var html = template(data); $.backstretch(data['image']); var vague = $('.backstretch').Vague({ intensity: 10, forceSVGUrl: false }); vague.blur(); $(html).hide().insertBefore(first_row).fadeIn('slow'); $('#wrapper .row:last').remove(); }); });
По мере того как мы начнем оборачиваться, давайте быстро добавим немного CSS в наше приложение. Вы можете увидеть это в public/css/style.css
. Посмотреть файл на GitHub .
развертывание
Теперь вы можете запустить приложение:
node app.js
Однако, когда вы перейдете по адресу http: // localhost: 4000 / , введите хэштег и нажмите START , ничего не произойдет. И если вы посмотрите на консоль, то увидите следующую ошибку:
APISubscriptionError occurred: Invalid response in _request
Хм! Что дает?
Проблема в том, что приложение должно быть доступно через Интернет, чтобы получить ответ Instagram. Поскольку мы запускаем приложение на локальном хосте, это, к сожалению, не сработает. К счастью, мы можем использовать ngrok для демонстрации нашего приложения в Интернете. После того как вы скачали и установили ngrok, вы можете запустить его, выполнив следующую команду в своем терминале:
ngrok http 4000
Это выставляет сервер Express в интернет. Обязательно измените callback_url
в файле app.js
, чтобы использовать URL-адрес https, который возвращает ngrok. Если вы планируете развернуть приложение позже, оно также должно быть https URL.
instagram.tags.subscribe({ object_id: tag, callback_url: 'https://xxxxxxxx.ngrok.io/subscribe', ... });
Просто скопируйте URL пересылки. Вот скриншот:
Теперь, если вы перезапустите сервер, все должно работать как запланировано:
После того как пользователь подписался, приложение начнет получать фотографии с сервера через socket.io, а затем отобразит их.
Вещи дальше
Если вы хотите поэкспериментировать с этим приложением и внести некоторые свои изменения, вы можете захотеть взглянуть на nodemon . Это автоматически перезапускает сервер каждый раз, когда вы вносите изменения в приложение Node, и очень удобно для разработки.
Тогда есть вопрос постоянства. Как только вы довольны работой, и если вы работаете в системе на основе Unix, вы можете установить Supervisor на свой сервер. Это позволяет вам запускать приложение постоянно. Простого запуска приложения с помощью nodemon будет недостаточно, поскольку процесс завершается в момент выхода из сервера.
В текущем окне терминала выполните следующую команду:
sudo apt-get install supervisor
Создайте файл конфигурации для приложения:
sudo nano /etc/supervisor/conf.d/instagram-realtime.conf
[program:instagram-realtime] command=nodemon app.js directory=/home/ubuntu/www stdout_logfile=/home/ubuntu/logs/instagram-realtime.log redirect_stderr=true
А затем добавьте его в Supervisor, выполнив следующие команды:
sudo supervisorctl reread add instagram-realtime start instagram-realtime
Последние мысли
Это оно! В этом руководстве вы научились работать с возможностями API Instagram в реальном времени, используя socket.io. Просто имейте в виду ограничения , а именно вызовы API, которые вы можете совершать в Instagram (это означает, что количество пользователей, которые могут подписаться на разные теги, ограничено — особенно, если теги популярны). Если это так, то сервер будет получать много уведомлений из Instagram, и количество вызовов API, которые вы можете сделать, будет легко заканчиваться. Кроме того, вы можете использовать Instagram API, как вам нравится.
У этого API есть много других возможностей, вы можете встраивать посты в Instagram или интегрировать их в свои мобильные приложения . Что касается нашего приложения, то идеальным вариантом использования будет событие, когда вы попросите участников опубликовать фотографии с определенным тегом. Организатор мероприятия может подписаться на этот тег и спроецировать приложение на экран, чтобы каждый мог видеть фотографии, которыми делятся.
Я хотел бы услышать ваши мысли об этом приложении и API Instagram в целом в комментариях ниже.