Получить фото из Instagram без API

Так как Instagram и Facebook ограничили доступ к API, а фото с своего аккаунта всё же нужно периодически получать и выводить на сайте, остается вариант их спарсить.

Сразу нужно сказать что метод не стабильный и требует постоянного контроля и доработки.

Если открыть исходный код страницы любого профиля, то видно что весь контент хранится в JS-объекте window._sharedData, в котором можно найти 12 последних фото, описания к ним и другую информацию.

Исходный код страницы Instagram
1

У инстаграма есть огромный черный список IP-адресов, скорее всего адрес вашего хостинга заблокирован, и обычным Curl-запросом будет возвращаться 302-й редирект на страницу авторизации:

HTTP/1.1 302 Found
Content-Type: text/html; charset=utf-8
Location: https://www.instagram.com/accounts/login/
...

Решение: использовать прокси, но они тоже со временем блокируются, поэтому проблематично найти рабочий сервер.

$ch = curl_init('https://www.instagram.com/аккаунт/');
curl_setopt($ch, CURLOPT_PROXY, 'xxx.xxx.xxx.xxx:8080');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_HEADER, false); // true - чтобы вывести заголовки 
$html = curl_exec($ch);
curl_close($ch); 

echo $html;
PHP
2

Страницу получили, теперь нужно удалить из полученного кода все управляющие символы, они будут мешать в следующем этапе.

// Удаление управляющих символов
for ($i = 0; $i <= 31; ++$i) { 
	$html = str_replace(chr($i), '', $html); 
}

// Удаление символа Delete
$html = str_replace(chr(127), '', $html);
PHP

Регулярным выражением получаем объект window._sharedData и преобразуем его в массив с помощью функции json_decode(). Далее собираем массив с данными.

$data = array();

preg_match_all('/<script type="text\/javascript">window\._sharedData = \{(.*)\};<\/script>/ism', $html, $matches);
if (!empty($matches[1][0])) {
	$res = json_decode('{' . $matches[1][0] . '}', true);

	$media = $res['entry_data']['ProfilePage'][0]['graphql']['user']['edge_owner_to_timeline_media']['edges'];
	if (!empty($media)) {
		foreach ($media as $row) {
			//print_r($row['node']);
			$data[] = array(
				'id'       => $row['node']['id'],
				'text'     => $row['node']['edge_media_to_caption']['edges'][0]['node']['text'],
				'image'    => $row['node']['thumbnail_src'],
			);
		}
	}
}

print_r($data);
PHP

Результат:

Array(
	[0] => Array(
		[id] => xxxxxxxxxxxxxxxxx
		[text] => Высокая кухня и красивые друзья❤️❤️
		[image] => https://...
	)
	[1] => Array(
		[id] => xxxxxxxxxxxxxxxxx
		[text] => Спасибо за вчерашний вечер❤️❤️ Настоящее предновогоднее настроение?
		[image] => https://...
	)
    ...
)
3

Полученный массив можно добавить в БД и сохранить изображения, обрабатывая только новые записи. Структура MySQL таблицы:

CREATE TABLE IF NOT EXISTS `instagram` (
  `id` int(10) UNSIGNED NOT NULL AUTO_INCREMENT,
  `insta_id` int(11) UNSIGNED DEFAULT '0',
  `text` text NOT NULL,
  `file` varchar(255) NOT NULL DEFAULT '',
  `date` int(11) UNSIGNED NOT NULL DEFAULT '0',
  PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
SQL

Циклом проходим по массиву и проверяем наличие фото в базе, если нет – добавляем.

// Подключение к БД
$dbh = new PDO('mysql:dbname=db_name;host=localhost', 'логин', 'пароль');

if (!empty($data)) {
	foreach ($data as $row) {
		// Проверяем есть ли такой ID в БД
		$sth = $dbh->prepare("SELECT * FROM `instagram` WHERE `insta_id` = ?");
		$sth->execute(array($row['id']));
		$is_db = $sth->fetch(PDO::FETCH_ASSOC);

		if (empty($is_db)) {
			// Получаем url файла без GET-параметров и его расширение
			$url = $row['image'];
			$url = explode('?', $url);
			$ext = mb_strtolower(mb_substr(mb_strrchr($url[0], '.'), 1));				
			$file = $row['id'] . '.' . $ext;
			
			// Сохранение файла на сервере и добавление в БД
			if (copy($row['image'], __DIR__ . '/instagram/' . $file)) {
				$sth = $dbh->prepare("
					INSERT INTO 
						`instagram` 
					SET
						`insta_id` = :insta_id,
						`text`     = :text,
						`file`     = :file,
						`date`     = :date
				");

				$sth->execute(array(
					'insta_id' => $row['id'],
					'text'     => $row['text'],
					'file'     => $file,
					'date'     => time(),				
				));				
			}
		}
	}
}
PHP
4
<?php
// Подключение к БД
$dbh = new PDO('mysql:dbname=db_name;host=localhost', 'логин', 'пароль');

$sth = $dbh->prepare("SELECT * FROM `instagram` ORDER BY `date` DESC LIMIT 20");
$sth->execute();
$list = $sth->fetchAll(PDO::FETCH_ASSOC);
?>

<ul>
	<?php foreach ($list as $row): ?>
	<li>
		<a href="/instagram/<?php echo $row['file']; ?>">
			<img src="/instagram/<?php echo $row['file']; ?>" alt="">
		</a>
	</li>	
	<?php endforeach; ?>
</ul>
HTML
13.12.2019, обновлено 18.11.2020
24713

Комментарии 6

Иван Бухарин Иван Бухарин
8 января 2020 в 04:10
На сколько этот метод не стабилен?
Можно ли рассчитывать на автоматизацию доступа к изображениям и отсутствии пролем в течении месяцев?
Можно писать в личку.
Test Test Test Test
28 января 2020 в 12:51
Метод не стабилен. Если инстаграмм изменит верстку значение переменной или набор данных, то придется переписывать, а хуже если уберет из кода страницы эти данные вовсе.
Test Test Test Test
29 января 2020 в 14:39
Автору спасибо! Все работает.
Заметил особенность - на некоторых хостах при загрузке страницы курлом сохраняет страницу с логином, значит с этого IP было много запросов и инстаграм их добавил в бан, а с некоторых сохраняет реальную страницу с данными. Потому не обязательно использовать прокси.
Denis Nikolaev Denis Nikolaev
12 июня 2020 в 22:30
У меня через proxy редиректит на страницу авторизации, уже много проксей перепробовал, есть ли у Вас рабочие прокси?
Владимир Васильченко Владимир Васильченко
10 июня 2021 в 08:40
Если открыть исходный код страницы любого профиля, то видно что весь контент хранится в JS-объекте window._sharedData, в котором можно найти 12 последних фото

А больше чем 12 фото курл может увидеть. пролистать вниз?
Генезис Сервис Генезис Сервис
16 сентября 2021 в 21:06
Парни, дайте архив данной фичи!
anufrieff-a "собака" mail.ru
Заранее, благодарен!

, чтобы добавить комментарий.

Другие публикации

Использование циклов в PHP
PHP имеет четыре вида циклов и операторы управления ими, рассмотрим поподробнее.
38027
+1
Примеры использования PDO MySQL
В статье приведены основные примеры работы с расширением PHP PDO. Такие как подключение к БД, получение, изменение и...
104279
+8
PHP-класс обертка для PDO
Класс значительно упрощает работу с PDO, сокращает код. Реализован на статических классах и не требует создание экземпляра класса.
23034
+11
Таблица символов ASCII + Windows 1251
Список из 256 символов и их коды в ASCII.
939560
+58
Как включить вывод символов эмодзи на сайте
Поддержка символов Emoji в браузерах становятся все полнее и полнее, но на сайтах использующие PHP и MySQL символы...
14579
+20
Автоматическое сжатие и оптимизация картинок на сайте
Изображения нужно сжимать для ускорения скорости загрузки сайта, но как это сделать? На многих хостингах нет...
28505
+8