28 ноября 2016      781      0

Скрипт загрузки скриншотов с Google

Сейчас я расскажу о том, как можно автоматически загружать скриншоты нужных страниц.

Как я выяснил ранее, уникальный трехзначный хэш для каждого результата поиска передается в html-коде страницы, следовательно алгоритм работы скрипта должен быть приблизительно таким:

  1. Выполняем поиск по заданному ключевому слову — если требуется скриншот конкретной страницы, то используется конструкция «site:http://example.com»
  2. Находим все пары url-хэш в полученном html.
  3. Обрабатываем полученный JSON
  4. Собираем картинку

Скрипт загрузки скриншотов с Google

На выходе скрипта получаем набор скриншотов — по одному на каждый результат поиска (количество нужных превью можно сделать настраиваемым).
Что будет использовано в процессе написания: PHP 5.3, cUrl, JSON, base64, DOMDocument.

Для начала установим рабочее окружение

////
// Setup environment
date_default_timezone_set('GMT');
setlocale(LC_ALL, 'en_US.utf-8');

if ( php_sapi_name() == 'cli' ) {
	define('IS_CLI', TRUE);
}

error_reporting(E_ALL | E_NOTICE | E_STRICT | E_WARNING);
ini_set('display_errors', 1);

// Working directories
define('DIR', dirname(__FILE__) . '/');
define('UPLOAD_DIR', DIR . 'images/');

Некоторый функционал удобно оформить в виде отдельных функций. Google возвращает разный результат в зависимости от user-агента пользователя. В коде я использую тот же user-агент, что и в используемом мной браузере.

// Helper functions
function debug($msg = '') {
	// correct output both in browser and command-line interface
	$eol = defined('IS_CLI') ? PHP_EOL : '<br />';
	echo $msg . $eol;
	flush(); // do not buffer the output
}

// Send HTTP GET request to given url
function http_request($url) {
	$ch = curl_init();
	curl_setopt($ch, CURLOPT_HEADER, FALSE);
	curl_setopt($ch, CURLOPT_FOLLOWLOCATION, TRUE);
	curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);

	curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/5.0 (Windows NT 6.1) AppleWebKit/535.7 (KHTML, like Gecko) Chrome/16.0.912.77 Safari/535.7');
	curl_setopt($ch, CURLOPT_URL, $url);
	$response = curl_exec($ch);
	$code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
	curl_close($ch);

	return array(
		'code' => $code,
		'body' => $response,
	);
}

function decode_image($base64_encoded_img) {
	$data = substr($base64_encoded_img, strlen('data:image/jpeg;base64,'));
	return base64_decode($data);
}

Задаем входные значения (в дальнейшем эти данные можно брать из строки запроса, например).

// Emulate input

$query = 'etretat'; // could easily be 'site:http://lenta.ru'
$results_needed = 2; // how many thumbnails we gonna steal

Теперь к основной функциональности. Во-первых нам нужно собрать трёхзначные хэши..

// Main process

$url = 'http://www.google.com/search?q=' . urlencode($query);
$response = http_request($url);

// Parse response and find all SIG keys
$dom = new DOMDocument;
@$dom->loadHTML($response['body']);
$xpath = new DOMXPath($dom);

$elements_li_g = $xpath->query('//div[@id="ires"]//li[@class="g"]');
$sites = array();

if ($elements_li_g->length > 0)  {
	$index = 0;
	for ($i = 0; $i < $elements_li_g->length; $i++) {
		if ($results_needed <= $index)
			break;
		$element_div = $xpath->query('div[@class="vsc"]', $elements_li_g->item($i));
		if ($element_div->length == 0) {
			continue;
		}
		$sig = $element_div->item(0)->getAttribute('sig');
		$element_a = $xpath->query('h3/a', $element_div->item(0));
		if ($element_a->length == 0) {
			continue;
		}
		$href = $element_a->item(0)->getAttribute('href');
		$sites[] = array(
			'sig' => $sig,
			'url' => $href,
		);
		$index++;
	}
}
unset($dom, $xpath);

if (count($sites) == 0) {
	debug('No results found');
	exit;
}

Имеем массив, содержащий url страницы и ее трехзначный хэш. То есть в наличии все данные для получения превью.

$thumbnails = array();
$half_encoded_query = str_replace(array('%3A', '%2F', '%2C'), array(':', '/', ','), urlencode($query));

foreach ($sites as $site) {
	$filename_prefix = md5($site['url']);
	$thumb = array(
		'url' => $site['url'],
		'img' => '',
	);

	$url = 'http://clients1.google.com/webpagethumbnail?r=4&f=3&s=400:585&query='.$half_encoded_query.'&hl=en&gl=us&c=29&d='.urlencode($site['url']).'&b=1&a='.$site['sig'];
	$response = http_request($url);
	if ($response['code'] != '200')
		continue;

	$data = $response['body'];
	unset($response);
	$json = preg_replace('/[^\{]*(\{.*\})[^\}]*/sim', '\1', $data);
	$json = str_replace(array("\n", "\r"), '', $json);
	$js_object = json_decode($json);

	$img_files = array();
	if (isset($js_object->shards) && is_array($js_object->shards)) {
		$index = 1;
		foreach ($js_object->shards as $shard) {
			if (isset($shard->imgs) && is_array($shard->imgs)) {
				foreach ($shard->imgs as $encoded_img) {
					$filename = $filename_prefix . '_' . $index . '.jpg';
					file_put_contents(UPLOAD_DIR . $filename, decode_image($encoded_img));
					$img_files[] = $filename;
					$index++;
				}
			}
		}

		if (count($img_files) > 0) {
			$files = implode('" "', $img_files);
			$filename = $filename_prefix . '.jpg';
			shell_exec('cd '.UPLOAD_DIR.'; convert "' . $files . '" -append ' . UPLOAD_DIR . $filename);
			$thumb['img'] = $filename;

			unset($files);
			// commented for debug purposes
			/*
			foreach ($img_files as $file_to_delete) {
				if (file_exists(UPLOAD_DIR . $file_to_delete) && is_readable(UPLOAD_DIR . $file_to_delete)) {
					unlink(UPLOAD_DIR . $file_to_delete);
				}
			}*/
		}

	}
	unset($json, $js_object, $img_files);
	$thumbnails[] = $thumb;
}

Проходим по каждому элементу массива и выполняем запрос на http://clients1.google.com/webpagethumbnail. Полученные части скриншота декодируем и сохраняем в директорию UPLOAD_DIR. Для объединения нескольких картинок в одну я использовал утилиту convert из набора ImageMagick. Операция объединения довольно простая — картинки имеют одинаковую ширину и их необходимо лишь расположить друг по дружкой. После получения большого скриншота, временные файлы можно удалять.

Магия с $half_encoded_query в том, что параметр «query»  в запросе Google кодирует несколько иначе, чем urlencode. Поэтому и был применён такой грязный хак.

Выводим результат работы скрипта на экран

$count = count($thumbnails);

debug($count . ' thumbnail' . ($count > 1 ? 's' : '') . ' downloaded');
if ($count == 0) {
	exit;
}

foreach ($thumbnails as $thumb) {
	debug();
	debug($thumb['url']);
	debug($thumb['img']);

	if (!defined('IS_CLI')) {
		debug('<img src="images/'.$thumb['img'].'" alt="'.$thumb['img'].'" />');
	}
}

Спасибо за внимание!

Все темы на сайте

© 2017 BorPost · Копирование материалов сайта без разрешения запрещено