Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- <?php
- // Безопасный вывод строк
- function safeHtml(?string $string): string {
- return htmlspecialchars($string ?? '', ENT_QUOTES, 'UTF-8');
- }
- // Очистка Google-ссылок (goto?url=...)
- function cleanGoogleUrl(?string $url): string {
- if (!$url) return '';
- if (strpos($url, 'google.com/goto?url=') !== false) {
- $parts = parse_url($url);
- if (isset($parts['query'])) {
- parse_str($parts['query'], $query);
- if (!empty($query['url'])) {
- return urldecode($query['url']);
- }
- }
- }
- return $url;
- }
- // Выбор языка интерфейса (по умолчанию русский)
- $uiLang = $_POST['ui_lang'] ?? 'ru';
- if (!in_array($uiLang, ['ru', 'uk'])) {
- $uiLang = 'ru';
- }
- // Словари локализации
- $translations = [
- 'ru' => [
- 'title' => 'SERP & AI Insights',
- 'subtitle' => 'Анализ поисковой выдачи Google и интеллектуальных ответов AI Overview',
- 'query_placeholder' => 'Введите поисковый запрос...',
- 'search_btn' => 'Анализировать',
- 'country_label' => 'Целевая страна (gl)',
- 'lang_label' => 'Язык поиска (hl)',
- 'ui_lang_label' => 'Язык интерфейса',
- 'no_ai_msg' => '⚠️ <strong>AI Overview не был сгенерирован Google для этого запроса.</strong><br>Ниже представлена стандартная органическая выдача.',
- 'serp_title' => 'Органическая поисковая выдача (SERP)',
- 'no_results' => 'Результаты поиска не найдены.',
- 'view_raw_json' => 'Показать необработанные данные JSON ответа',
- 'sources_title' => 'Источники AI Overview:',
- 'no_title' => 'Без названия',
- 'countries' => [
- 'ua' => 'Украина (UA)',
- 'us' => 'США (US)',
- 'gb' => 'Великобритания (GB)',
- 'de' => 'Германия (DE)',
- 'fr' => 'Франция (FR)',
- 'it' => 'Италия (IT)',
- 'es' => 'Испания (ES)',
- 'pl' => 'Польша (PL)',
- 'ca' => 'Канада (CA)',
- 'au' => 'Австралия (AU)',
- 'nl' => 'Нидерланды (NL)',
- 'tr' => 'Турция (TR)',
- 'br' => 'Бразилия (BR)',
- 'in' => 'Индия (IN)',
- 'jp' => 'Япония (JP)',
- 'sg' => 'Сингапур (SG)',
- 'ch' => 'Швейцария (CH)',
- 'at' => 'Австрия (AT)',
- 'be' => 'Бельгия (BE)',
- 'il' => 'Израиль (IL)',
- 'se' => 'Швеция (SE)',
- 'no' => 'Норвегия (NO)',
- 'fi' => 'Финляндия (FI)',
- 'cz' => 'Чехия (CZ)',
- 'ro' => 'Румыния (RO)',
- 'kz' => 'Казахстан (KZ)',
- ],
- 'languages' => [
- 'ru' => 'Русский (ru)',
- 'uk' => 'Украинский (uk)',
- 'en' => 'Английский (en)',
- 'de' => 'Немецкий (de)',
- 'fr' => 'Французский (fr)',
- 'es' => 'Испанский (es)',
- 'it' => 'Итальянский (it)',
- 'pl' => 'Польский (pl)',
- 'tr' => 'Турецкий (tr)',
- 'pt' => 'Португальский (pt)',
- 'ja' => 'Японский (ja)',
- 'zh-CN' => 'Китайский (zh-CN)',
- 'hi' => 'Хинди (hi)',
- 'nl' => 'Нидерландский (nl)',
- 'ar' => 'Арабский (ar)',
- 'sv' => 'Шведский (sv)',
- 'cs' => 'Чешский (cs)',
- 'ro' => 'Румынский (ro)',
- 'kk' => 'Казахский (kk)',
- ]
- ],
- 'uk' => [
- 'title' => 'SERP & AI Insights',
- 'subtitle' => 'Аналіз пошукової видачі Google та інтелектуальних відповідей AI Overview',
- 'query_placeholder' => 'Введіть пошуковий запит...',
- 'search_btn' => 'Аналізувати',
- 'country_label' => 'Цільова країна (gl)',
- 'lang_label' => 'Мова пошуку (hl)',
- 'ui_lang_label' => 'Мова інтерфейсу',
- 'no_ai_msg' => '⚠️ <strong>AI Overview не був згенерований Google для цього запиту.</strong><br>Нижче виведена стандартна органічна видача.',
- 'serp_title' => 'Органічна пошукова видача (SERP)',
- 'no_results' => 'Результати пошуку не знайдені.',
- 'view_raw_json' => 'Показати необроблені дані JSON відповіді',
- 'sources_title' => 'Джерела AI Overview:',
- 'no_title' => 'Без назви',
- 'countries' => [
- 'ua' => 'Україна (UA)',
- 'us' => 'США (US)',
- 'gb' => 'Великобританія (GB)',
- 'de' => 'Німеччина (DE)',
- 'fr' => 'Франція (FR)',
- 'it' => 'Італія (IT)',
- 'es' => 'Іспанія (ES)',
- 'pl' => 'Польща (PL)',
- 'ca' => 'Канада (CA)',
- 'au' => 'Австралія (AU)',
- 'nl' => 'Нідерланди (NL)',
- 'tr' => 'Туреччина (TR)',
- 'br' => 'Бразилія (BR)',
- 'in' => 'Індія (IN)',
- 'jp' => 'Японія (JP)',
- 'sg' => 'Сінгапур (SG)',
- 'ch' => 'Швейцарія (CH)',
- 'at' => 'Австрія (AT)',
- 'be' => 'Бельгія (BE)',
- 'il' => 'Ізраїль (IL)',
- 'se' => 'Швеція (SE)',
- 'no' => 'Норвегія (NO)',
- 'fi' => 'Фінляндія (FI)',
- 'cz' => 'Чехія (CZ)',
- 'ro' => 'Румунія (RO)',
- 'kz' => 'Казахстан (KZ)',
- ],
- 'languages' => [
- 'ru' => 'Російська (ru)',
- 'uk' => 'Українська (uk)',
- 'en' => 'Англійська (en)',
- 'de' => 'Німецька (de)',
- 'fr' => 'Французька (fr)',
- 'es' => 'Іспанська (es)',
- 'it' => 'Італійська (it)',
- 'pl' => 'Польська (pl)',
- 'tr' => 'Турецька (tr)',
- 'pt' => 'Португальська (pt)',
- 'ja' => 'Японська (ja)',
- 'zh-CN' => 'Китайська (zh-CN)',
- 'hi' => 'Хінді (hi)',
- 'nl' => 'Нідерландська (nl)',
- 'ar' => 'Арабська (ar)',
- 'sv' => 'Шведська (sv)',
- 'cs' => 'Чеська (cs)',
- 'ro' => 'Румунська (ro)',
- 'kk' => 'Казахська (kk)',
- ]
- ]
- ];
- $t = $translations[$uiLang];
- $aiOverview = null;
- $organicResults = [];
- $searchedQuery = '';
- $selectedGl = 'ua';
- $selectedHl = 'uk';
- $rawJson = null;
- if ($_SERVER['REQUEST_METHOD'] === 'POST') {
- $searchedQuery = isset($_POST['query']) ? trim($_POST['query']) : '';
- $selectedGl = trim($_POST['gl'] ?? 'ua');
- $selectedHl = trim($_POST['hl'] ?? 'uk');
- if (!empty($searchedQuery)) {
- set_time_limit(90);
- $apiKey = '';
- $encodedQuery = urlencode($searchedQuery);
- $targetUrl = "https://www.google.com/search?q={$encodedQuery}&gl={$selectedGl}&hl={$selectedHl}&brd_ai_overview=2";
- $data = [
- 'zone' => 'serp_api1',
- 'url' => $targetUrl,
- 'format' => 'json',
- 'data_format' => 'parsed'
- ];
- $ch = curl_init('https://api.brightdata.com/request');
- curl_setopt_array($ch, [
- CURLOPT_POST => true,
- CURLOPT_POSTFIELDS => json_encode($data),
- CURLOPT_RETURNTRANSFER => true,
- CURLOPT_TIMEOUT => 60,
- CURLOPT_SSL_VERIFYPEER => false,
- CURLOPT_HTTPHEADER => [
- 'Authorization: Bearer ' . $apiKey,
- 'Content-Type: application/json'
- ]
- ]);
- $response = curl_exec($ch);
- curl_close($ch);
- if ($response) {
- $decoded = json_decode($response, true);
- $body = $decoded['body'] ?? [];
- if (is_string($body)) {
- $body = json_decode($body, true);
- }
- if (isset($body['ai_overview'])) {
- $aiOverview = $body['ai_overview'];
- }
- if (isset($body['organic']) && is_array($body['organic'])) {
- $organicResults = $body['organic'];
- }
- $rawJson = json_encode($decoded, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
- }
- }
- }
- ?>
- <!DOCTYPE html>
- <html lang="<?= safeHtml($uiLang) ?>">
- <head>
- <meta charset="UTF-8">
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
- <title><?= safeHtml($t['title']) ?></title>
- <style>
- :root {
- --primary: #1a73e8;
- --primary-hover: #1557b0;
- --bg-main: #f8fafc;
- --bg-card: #ffffff;
- --text-dark: #1e293b;
- --text-muted: #64748b;
- --border: #e2e8f0;
- --ai-accent: #7c3aed;
- --ai-bg: #f5f3ff;
- --ai-border: #ddd6fe;
- --radius: 12px;
- --font: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
- }
- body {
- font-family: var(--font);
- margin: 0;
- padding: 0;
- color: var(--text-dark);
- background: var(--bg-main);
- line-height: 1.5;
- }
- /* Header Navigation */
- .header {
- background: var(--bg-card);
- border-bottom: 1px solid var(--border);
- padding: 15px 30px;
- position: sticky;
- top: 0;
- z-index: 100;
- }
- .header-container {
- max-width: 1000px;
- margin: 0 auto;
- display: flex;
- justify-content: space-between;
- align-items: center;
- }
- .logo-area {
- display: flex;
- align-items: center;
- gap: 10px;
- }
- .logo-title {
- font-size: 20px;
- font-weight: 700;
- color: var(--text-dark);
- text-decoration: none;
- display: flex;
- align-items: center;
- gap: 8px;
- }
- .logo-title span {
- background: linear-gradient(135deg, var(--primary), var(--ai-accent));
- -webkit-background-clip: text;
- -webkit-text-fill-color: transparent;
- }
- .lang-selector-form select {
- padding: 6px 12px;
- font-size: 13px;
- border: 1px solid var(--border);
- border-radius: 6px;
- background: var(--bg-card);
- color: var(--text-dark);
- cursor: pointer;
- outline: none;
- }
- /* Container */
- .container {
- max-width: 850px;
- margin: 40px auto;
- padding: 0 20px;
- }
- .service-intro {
- text-align: center;
- margin-bottom: 30px;
- }
- .service-intro h1 {
- font-size: 28px;
- font-weight: 800;
- margin-bottom: 8px;
- color: var(--text-dark);
- }
- .service-intro p {
- color: var(--text-muted);
- margin: 0;
- font-size: 15px;
- }
- /* Search Form Container */
- .search-box-card {
- background: var(--bg-card);
- padding: 24px;
- border-radius: var(--radius);
- box-shadow: 0 4px 12px rgba(15, 23, 42, 0.05);
- border: 1px solid var(--border);
- margin-bottom: 30px;
- }
- .form-main-row {
- display: flex;
- gap: 12px;
- margin-bottom: 20px;
- }
- input[type="text"] {
- flex-grow: 1;
- padding: 14px 20px;
- font-size: 16px;
- border: 1px solid var(--border);
- border-radius: var(--radius);
- outline: none;
- background: #fdfdfd;
- transition: border-color 0.2s, box-shadow 0.2s;
- }
- input[type="text"]:focus {
- border-color: var(--primary);
- box-shadow: 0 0 0 3px rgba(26, 115, 232, 0.15);
- background: #fff;
- }
- button[type="submit"] {
- padding: 14px 28px;
- font-size: 15px;
- font-weight: 600;
- background: var(--primary);
- color: white;
- border: none;
- border-radius: var(--radius);
- cursor: pointer;
- transition: background-color 0.2s;
- }
- button[type="submit"]:hover {
- background: var(--primary-hover);
- }
- /* Grid Parameters */
- .form-options-grid {
- display: grid;
- grid-template-columns: repeat(2, 1fr);
- gap: 16px;
- }
- .option-field {
- display: flex;
- flex-direction: column;
- gap: 6px;
- }
- .option-field label {
- font-size: 12px;
- font-weight: 700;
- text-transform: uppercase;
- letter-spacing: 0.5px;
- color: var(--text-muted);
- }
- .option-field select {
- padding: 10px 14px;
- border: 1px solid var(--border);
- border-radius: 8px;
- font-size: 14px;
- background: var(--bg-card);
- color: var(--text-dark);
- outline: none;
- transition: border-color 0.2s;
- }
- .option-field select:focus {
- border-color: var(--primary);
- }
- /* AI Overview Card */
- .ai-card {
- background: var(--ai-bg);
- border: 1px solid var(--ai-border);
- padding: 24px;
- border-radius: var(--radius);
- margin-bottom: 35px;
- box-shadow: 0 4px 15px rgba(124, 58, 237, 0.04);
- }
- .ai-title {
- font-size: 16px;
- font-weight: 700;
- color: var(--ai-accent);
- margin-bottom: 14px;
- display: flex;
- align-items: center;
- gap: 8px;
- text-transform: uppercase;
- letter-spacing: 0.5px;
- }
- .ai-text {
- line-height: 1.7;
- font-size: 15px;
- color: #334155;
- }
- .ai-text p { margin: 0 0 12px 0; }
- .ai-text p:last-child { margin-bottom: 0; }
- .ai-text ul { margin: 0 0 12px 20px; padding: 0; }
- .ai-text li { margin-bottom: 6px; }
- .ai-sources {
- margin-top: 20px;
- border-top: 1px solid var(--ai-border);
- padding-top: 14px;
- }
- .ai-sources-title {
- font-weight: 700;
- font-size: 13px;
- margin-bottom: 10px;
- color: var(--text-muted);
- text-transform: uppercase;
- letter-spacing: 0.5px;
- }
- .source-chip {
- display: inline-flex;
- align-items: center;
- background: var(--bg-card);
- padding: 6px 12px;
- border-radius: 20px;
- border: 1px solid var(--ai-border);
- font-size: 13px;
- margin-right: 8px;
- margin-bottom: 8px;
- text-decoration: none;
- color: var(--ai-accent);
- font-weight: 500;
- transition: background-color 0.2s, transform 0.1s;
- }
- .source-chip:hover {
- background: #ede9fe;
- transform: translateY(-1px);
- }
- /* Organic Results Section */
- .section-title {
- font-size: 18px;
- font-weight: 700;
- margin-bottom: 20px;
- color: var(--text-dark);
- border-bottom: 2px solid var(--border);
- padding-bottom: 8px;
- }
- .organic-card {
- background: var(--bg-card);
- padding: 20px;
- border-radius: var(--radius);
- margin-bottom: 16px;
- box-shadow: 0 2px 6px rgba(15, 23, 42, 0.02);
- border: 1px solid var(--border);
- transition: box-shadow 0.2s, transform 0.2s;
- }
- .organic-card:hover {
- box-shadow: 0 4px 12px rgba(15, 23, 42, 0.05);
- transform: translateY(-1px);
- }
- .organic-site {
- font-size: 12px;
- color: var(--text-muted);
- margin-bottom: 6px;
- font-family: monospace;
- }
- .organic-title {
- font-size: 18px;
- font-weight: 600;
- margin: 0 0 8px 0;
- }
- .organic-title a {
- color: var(--primary);
- text-decoration: none;
- }
- .organic-title a:hover {
- text-decoration: underline;
- }
- .organic-snippet {
- font-size: 14px;
- color: #475569;
- line-height: 1.6;
- }
- .no-ai {
- background: #fffbeb;
- color: #b45309;
- padding: 16px 20px;
- border-radius: var(--radius);
- margin-bottom: 30px;
- border: 1px solid #fef3c7;
- font-size: 14px;
- }
- /* Raw Data Toggle */
- details {
- margin-top: 40px;
- background: var(--bg-card);
- border: 1px solid var(--border);
- border-radius: var(--radius);
- padding: 12px;
- }
- summary {
- font-weight: 600;
- font-size: 14px;
- color: var(--text-muted);
- cursor: pointer;
- outline: none;
- user-select: none;
- }
- details[open] summary {
- margin-bottom: 12px;
- }
- </style>
- </head>
- <body>
- <header class="header">
- <div class="header-container">
- <div class="logo-area">
- <a href="" class="logo-title">🔍 <span><?= safeHtml($t['title']) ?></span></a>
- </div>
- <div class="lang-selector-top">
- <!-- Кнопка смены языка интерфейса отправляет скрытую форму для сохранения текущего запроса -->
- <form id="ui_lang_form" class="lang-selector-form" method="POST" action="">
- <input type="hidden" name="query" value="<?= safeHtml($searchedQuery) ?>">
- <input type="hidden" name="gl" value="<?= safeHtml($selectedGl) ?>">
- <input type="hidden" name="hl" value="<?= safeHtml($selectedHl) ?>">
- <select name="ui_lang" onchange="document.getElementById('ui_lang_form').submit()">
- <option value="ru" <?= $uiLang === 'ru' ? 'selected' : '' ?>>Русский (RU)</option>
- <option value="uk" <?= $uiLang === 'uk' ? 'selected' : '' ?>>Українська (UK)</option>
- </select>
- </form>
- </div>
- </div>
- </header>
- <div class="container">
- <div class="service-intro">
- <h1><?= safeHtml($t['title']) ?></h1>
- <p><?= safeHtml($t['subtitle']) ?></p>
- </div>
- <div class="search-box-card">
- <form method="POST" action="">
- <input type="hidden" name="ui_lang" value="<?= safeHtml($uiLang) ?>">
- <div class="form-main-row">
- <input
- type="text"
- name="query"
- placeholder="<?= safeHtml($t['query_placeholder']) ?>"
- value="<?= safeHtml($searchedQuery ?: '') ?>"
- required
- >
- <button type="submit"><?= safeHtml($t['search_btn']) ?></button>
- </div>
- <div class="form-options-grid">
- <div class="option-field">
- <label for="gl"><?= safeHtml($t['country_label']) ?></label>
- <select name="gl" id="gl">
- <?php foreach ($t['countries'] as $code => $name): ?>
- <option value="<?= safeHtml($code) ?>" <?= $selectedGl === $code ? 'selected' : '' ?>>
- <?= safeHtml($name) ?>
- </option>
- <?php endforeach; ?>
- </select>
- </div>
- <div class="option-field">
- <label for="hl"><?= safeHtml($t['lang_label']) ?></label>
- <select name="hl" id="hl">
- <?php foreach ($t['languages'] as $code => $name): ?>
- <option value="<?= safeHtml($code) ?>" <?= $selectedHl === $code ? 'selected' : '' ?>>
- <?= safeHtml($name) ?>
- </option>
- <?php endforeach; ?>
- </select>
- </div>
- </div>
- </form>
- </div>
- <?php if ($_SERVER['REQUEST_METHOD'] === 'POST' && !empty($searchedQuery)): ?>
- <!-- 1. AI OVERVIEW -->
- <?php if ($aiOverview): ?>
- <div class="ai-card">
- <div class="ai-title">✨ AI Overview</div>
- <div class="ai-text">
- <?php
- if (isset($aiOverview['texts']) && is_array($aiOverview['texts'])) {
- foreach ($aiOverview['texts'] as $textBlock) {
- if (isset($textBlock['snippet'])) {
- echo '<p>' . nl2br(safeHtml($textBlock['snippet'])) . '</p>';
- }
- if (isset($textBlock['list']) && is_array($textBlock['list'])) {
- echo '<ul>';
- foreach ($textBlock['list'] as $li) {
- $itemText = is_array($li) ? ($li['text'] ?? $li['snippet'] ?? '') : $li;
- if ($itemText) echo '<li>' . safeHtml($itemText) . '</li>';
- }
- echo '</ul>';
- }
- }
- } elseif (isset($aiOverview['text'])) {
- echo '<p>' . nl2br(safeHtml($aiOverview['text'])) . '</p>';
- }
- ?>
- </div>
- <!-- Сбор источников -->
- <?php
- $finalSources = [];
- $rawSources = array_merge(
- $aiOverview['references'] ?? [],
- $aiOverview['sources'] ?? []
- );
- if (isset($aiOverview['texts']) && is_array($aiOverview['texts'])) {
- foreach ($aiOverview['texts'] as $tb) {
- if (!empty($tb['links']) && is_array($tb['links'])) {
- foreach ($tb['links'] as $l) {
- $rawSources[] = $l;
- }
- }
- }
- }
- foreach ($rawSources as $src) {
- $url = cleanGoogleUrl($src['link'] ?? $src['url'] ?? '');
- $title = $src['title'] ?? $src['source'] ?? $src['display_link'] ?? '';
- if (!$url && !empty($organicResults)) {
- foreach ($organicResults as $org) {
- $orgSource = strtolower($org['source'] ?? '');
- $orgLink = strtolower($org['link'] ?? '');
- $searchTitle = strtolower($title);
- if ($searchTitle && (strpos($orgSource, $searchTitle) !== false || strpos($orgLink, $searchTitle) !== false)) {
- $url = $org['link'];
- if (!$title) $title = $org['title'];
- break;
- }
- }
- }
- if ($url) {
- $finalSources[$url] = [
- 'url' => $url,
- 'title' => $title ?: parse_url($url, PHP_URL_HOST)
- ];
- }
- }
- if (!empty($finalSources)):
- ?>
- <div class="ai-sources">
- <div class="ai-sources-title"><?= safeHtml($t['sources_title']) ?></div>
- <?php foreach ($finalSources as $source): ?>
- <a href="<?= safeHtml($source['url']) ?>" target="_blank" rel="noopener noreferrer" class="source-chip">
- 🔗 <?= safeHtml($source['title']) ?>
- </a>
- <?php endforeach; ?>
- </div>
- <?php endif; ?>
- </div>
- <?php else: ?>
- <div class="no-ai">
- <?= $t['no_ai_msg'] ?>
- </div>
- <?php endif; ?>
- <!-- 2. ОРГАНИЧЕСКИЙ РЕЗУЛЬТАТ -->
- <h2 class="section-title"><?= safeHtml($t['serp_title']) ?></h2>
- <?php if (!empty($organicResults)): ?>
- <?php foreach ($organicResults as $item): ?>
- <div class="organic-card">
- <div class="organic-site">
- <?= safeHtml($item['source'] ?? $item['display_link'] ?? '') ?>
- </div>
- <h3 class="organic-title">
- <a href="<?= safeHtml($item['link'] ?? '#') ?>" target="_blank" rel="noopener noreferrer">
- <?= safeHtml($item['title'] ?? $t['no_title']) ?>
- </a>
- </h3>
- <div class="organic-snippet">
- <?= safeHtml($item['description'] ?? '') ?>
- </div>
- </div>
- <?php endforeach; ?>
- <?php else: ?>
- <p><?= safeHtml($t['no_results']) ?></p>
- <?php endif; ?>
- <details>
- <summary><?= safeHtml($t['view_raw_json']) ?></summary>
- <pre><?= safeHtml($rawJson) ?></pre>
- </details>
- <?php endif; ?>
- </div>
- </body>
- </html>
Advertisement
Add Comment
Please, Sign In to add comment