top of page

Metehan: Visual Query Fan-Out Analysis in Screaming Frog

  • Autorenbild: th3s3rp4nt
    th3s3rp4nt
  • 27. Okt. 2025
  • 7 Min. Lesezeit

Key Takeaways:

  • Metehan developed a script to run via Screaming Frog that analysis meaningful images of websites automatically

  • Google’s investing heavily in visual understanding. Their visual search fan-out technique runs multiple queries in the background based on comprehensive image analysis.

  • With this tool you can check whether your images provide meaning to search engines and LLMs to add value to your content OR rather whether your visuals meet your product or brand identity



Here’s what happens when you run it:

  1. Find the important images – It skips logos, UI elements, tracking pixels and focuses on product images, content images, galleries

  2. Comprehensive visual analysis – The AI identifies everything: primary subjects, secondary objects, visible text, colors, materials, styles, implied use cases

  3. Generate search queries – For each element and combination of elements, it creates queries real people might actually search



Code for the full analysis in Screaming Frogs custom JS executions:

// Screaming Frog Custom JavaScript - Visual Query Fan-out using OpenAI Vision API

// Enhanced version with per-image query breakdown


const OPENAI_API_KEY = 'sk-proj-xxxxx'; // Replace with your OpenAI API key

const MODEL = 'gpt-4o-mini'; // Options: gpt-4o, gpt-4o-mini, gpt-4-turbo


// Configuration

const CONFIG = {

maxImages: 5, // Limit images to process

minImageSize: 200, // Minimum dimension in pixels

maxTokens: 1000, // Max response tokens per image

mainContentSelectors: [ // Selectors for main content areas

'main img',

'article img',

'.product-image img',

'.product-gallery img',

'.content img',

'#content img',

'.post-content img',

'[role="main"] img',

'.product img',

'.gallery img',

'[data-testid*="product"] img',

'.pdp-image img', // Product detail page

'.product-photo img'

]

};


// Helper to check if image is in main content area

function isMainContentImage(img) {

// Check if image matches any main content selector

for (const selector of CONFIG.mainContentSelectors) {

if (img.matches(selector)) {

return true;

}

}

// Check parent containers

const parent = img.closest('main, article, .product, .content, #content');

if (parent) return true;

// Check if it's a product image by URL/attributes

const src = (img.src || '').toLowerCase();

const className = (img.className || '').toLowerCase();

if (src.includes('product') || src.includes('item') ||

className.includes('product') || className.includes('gallery')) {

return true;

}

// Skip UI elements

if (src.includes('logo') || src.includes('icon') || src.includes('sprite') ||

src.includes('pixel') || src.includes('tracking') || src.includes('analytics')) {

return false;

}

return false;

}


// Get absolute URL for image

function getAbsoluteUrl(url) {

if (!url) return null;

if (url.startsWith('data:')) return null; // Skip data URLs

if (url.startsWith('http://') || url.startsWith('https://')) return url;

if (url.startsWith('//')) return window.location.protocol + url;

if (url.startsWith('/')) return window.location.origin + url;

return new URL(url, window.location.href).href;

}


// Extract main content images from page

function extractMainContentImages() {

const images = [];

const processedUrls = new Set();

const allImages = document.querySelectorAll('img');

for (let img of allImages) {

if (!isMainContentImage(img)) continue;

if (img.naturalWidth >= CONFIG.minImageSize && img.naturalHeight >= CONFIG.minImageSize) {

const src = getAbsoluteUrl(img.src || img.dataset.src || img.dataset.lazySrc);

if (!src || processedUrls.has(src)) continue;

processedUrls.add(src);

images.push({

url: src,

alt: img.alt || '',

title: img.title || '',

width: img.naturalWidth,

height: img.naturalHeight

});

if (images.length >= CONFIG.maxImages) break;

}

}

return images;

}


// Call OpenAI Vision API with image URL directly

function analyzeImageWithOpenAI(imageUrl, imageInfo) {

const prompt = 'Analyze this e-commerce/website image and provide a comprehensive visual search fan-out analysis.\n\n' +

'Tasks:\n' +

'1. Identify ALL objects, products, and visual elements in the image\n' +

'2. Extract any visible text, brands, or labels\n' +

'3. Determine the image context (product shot, lifestyle, detail view, etc.)\n' +

'4. Generate search queries that users might use to find similar items\n\n' +

'For each detected object/product, provide:\n' +

'- Specific descriptive label (e.g., "men\'s blue denim jacket" not just "jacket")\n' +

'- Visual attributes (color, material, style, pattern, size)\n' +

'- Category classification\n' +

'- Brand if identifiable\n' +

'- 3-5 search queries someone might use to find this item\n\n' +

'Return your analysis as a JSON object with this exact structure:\n' +

'{\n' +

' "objects": [\n' +

' {\n' +

' "label": "specific item name",\n' +

' "category": "category",\n' +

' "attributes": {\n' +

' "color": "color",\n' +

' "material": "material",\n' +

' "style": "style",\n' +

' "brand": "brand if visible"\n' +

' },\n' +

' "search_queries": ["query1", "query2", "query3"]\n' +

' }\n' +

' ],\n' +

' "text_detected": ["any visible text"],\n' +

' "scene_analysis": {\n' +

' "type": "product/lifestyle/studio",\n' +

' "composition": "description",\n' +

' "purpose": "hero/gallery/detail"\n' +

' },\n' +

' "fan_out_queries": ["top search query 1", "top search query 2", "top search query 3", "top search query 4", "top search query 5"]\n' +

'}';


try {

const requestData = {

model: MODEL,

messages: [

{

role: "user",

content: [

{

type: "text",

text: prompt

},

{

type: "image_url",

image_url: {

url: imageUrl,

detail: "high" // "low", "high", or "auto"

}

}

]

}

],

max_tokens: CONFIG.maxTokens,

temperature: 0.2,

response_format: { type: "json_object" }

};


const xhr = new XMLHttpRequest();

xhr.open('POST', 'https://api.openai.com/v1/chat/completions', false);

xhr.setRequestHeader('Content-Type', 'application/json');

xhr.setRequestHeader('Authorization', 'Bearer ' + OPENAI_API_KEY);

xhr.send(JSON.stringify(requestData));

if (xhr.status === 200) {

const response = JSON.parse(xhr.responseText);

if (response.choices && response.choices[0] && response.choices[0].message) {

const content = response.choices[0].message.content;

try {

return JSON.parse(content);

} catch (e) {

// Try to clean and parse

const cleaned = content.replace(/```json\n?/g, '').replace(/```\n?/g, '').trim();

return JSON.parse(cleaned);

}

}

} else {

const error = JSON.parse(xhr.responseText);

throw new Error(error.error?.message || 'API request failed');

}

} catch (e) {

return null;

}

}


// Main execution

try {

const startTime = Date.now();

const images = extractMainContentImages();

if (images.length === 0) {

return seoSpider.data('No main content images found on page. Checked ' + CONFIG.mainContentSelectors.length + ' content selectors.');

}

const results = [];

let processedCount = 0;

let totalObjects = 0;

const errors = [];

const allQueries = new Set();

// Process each image with OpenAI

for (let i = 0; i < images.length; i++) {

const image = images[i];

// Call OpenAI Vision API directly with URL

const analysis = analyzeImageWithOpenAI(image.url, image);

if (analysis) {

processedCount++;

if (analysis.objects) {

totalObjects += analysis.objects.length;

}

// Collect all queries

if (analysis.fan_out_queries) {

analysis.fan_out_queries.forEach(q => allQueries.add(q));

}

if (analysis.objects) {

analysis.objects.forEach(obj => {

if (obj.search_queries) {

obj.search_queries.forEach(q => allQueries.add(q));

}

});

}

results.push({

image: image,

data: analysis

});

} else {

errors.push('Failed to analyze: ' + image.url);

}

}

// Prepare output

let output = '=== OPENAI VISION QUERY FAN-OUT ANALYSIS ===\n\n';

output += 'URL: ' + window.location.href + '\n';

output += 'Model: ' + MODEL + '\n';

output += 'Processing Time: ' + (Date.now() - startTime) + 'ms\n\n';

output += '=== SUMMARY ===\n';

output += '• Images Found: ' + images.length + '\n';

output += '• Images Processed: ' + processedCount + '\n';

output += '• Total Objects Detected: ' + totalObjects + '\n';

output += '• Unique Queries Generated: ' + allQueries.size + '\n';

output += '• API Errors: ' + errors.length + '\n\n';

if (processedCount > 0) {

// Show detailed per-image analysis with queries

output += '=== PER-IMAGE ANALYSIS WITH QUERY FAN-OUT ===\n';

output += '━'.repeat(60) + '\n';

results.forEach((result, idx) => {

if (result.data) {

output += '\n📷 IMAGE ' + (idx + 1) + '\n';

output += '━'.repeat(60) + '\n';

// Image details

output += '📍 URL: ' + result.image.url.substring(result.image.url.lastIndexOf('/') + 1).substring(0, 60) + '\n';

output += '📏 Dimensions: ' + result.image.width + 'x' + result.image.height + 'px\n';

if (result.image.alt) {

output += '🏷️ Alt Text: "' + result.image.alt + '"\n';

}

// Scene Analysis

if (result.data.scene_analysis) {

output += '\n🎬 Scene Analysis:\n';

output += ' • Type: ' + result.data.scene_analysis.type + '\n';

output += ' • Purpose: ' + result.data.scene_analysis.purpose + '\n';

if (result.data.scene_analysis.composition) {

output += ' • Composition: ' + result.data.scene_analysis.composition + '\n';

}

}

// Detected objects

if (result.data.objects && result.data.objects.length > 0) {

output += '\n🔍 Detected Objects (' + result.data.objects.length + '):\n';

result.data.objects.forEach((obj, objIdx) => {

output += ' ' + (objIdx + 1) + '. ' + obj.label;

if (obj.category) output += ' [' + obj.category + ']';

output += '\n';

if (obj.attributes) {

const attrs = [];

Object.entries(obj.attributes).forEach(([key, val]) => {

if (val && val !== 'null' && val !== 'unknown') {

attrs.push(key + ': ' + val);

}

});

if (attrs.length > 0) {

output += ' Attributes: ' + attrs.join(', ') + '\n';

}

}

});

}

// Text detected

if (result.data.text_detected && result.data.text_detected.length > 0) {

output += '\n📝 Text/Brands Detected:\n';

result.data.text_detected.forEach(text => {

output += ' • ' + text + '\n';

});

}

// Collect all queries for this image

const imageQueries = new Set();

// Add fan-out queries

if (result.data.fan_out_queries) {

result.data.fan_out_queries.forEach(q => imageQueries.add(q));

}

// Add object-specific queries

if (result.data.objects) {

result.data.objects.forEach(obj => {

if (obj.search_queries) {

obj.search_queries.forEach(q => imageQueries.add(q));

}

});

}

// Display all queries for this image

if (imageQueries.size > 0) {

output += '\n🎯 Visual Query Fan-Out for This Image (' + imageQueries.size + ' queries):\n';

const imageQueriesArray = Array.from(imageQueries);

// Group queries by type if possible

const topQueries = result.data.fan_out_queries || [];

const objectQueries = [];

if (result.data.objects) {

result.data.objects.forEach(obj => {

if (obj.search_queries) {

obj.search_queries.forEach(q => {

if (!topQueries.includes(q)) {

objectQueries.push(q);

}

});

}

});

}

if (topQueries.length > 0) {

output += ' 📌 Primary Queries:\n';

topQueries.forEach((query, i) => {

output += ' ' + (i + 1) + '. "' + query + '"\n';

});

}

if (objectQueries.length > 0) {

output += ' 📦 Object-Specific Queries:\n';

objectQueries.slice(0, 10).forEach((query, i) => {

output += ' ' + (i + 1) + '. "' + query + '"\n';

});

}

}

output += '\n' + '─'.repeat(60) + '\n';

}

});

// Show aggregated top queries

output += '\n=== TOP QUERIES ACROSS ALL IMAGES ===\n';

const queriesArray = Array.from(allQueries);

queriesArray.slice(0, 15).forEach((query, idx) => {

output += (idx + 1) + '. "' + query + '"\n';

});

}

if (errors.length > 0) {

output += '\n=== ERRORS ===\n';

errors.forEach(err => {

output += '• ' + err + '\n';

});

output += '\nNote: Check API key and rate limits if errors occur.\n';

}

return seoSpider.data(output);

} catch (error) {

return seoSpider.error('Script Error: ' + error.toString() + '\n\nMake sure your OpenAI API key is valid.');

}


Sources:

© 2026 David Epding.            Erstellt mit Wix.com.

david epding logo

David Epding ist GEO & SEO, Data Analytics und Automation Manager mit über 10 Jahren Erfahrung in Technischem SEO mit breiter Expertise für LLMs und langjähriger Erfahrung in der Daten-Analyse.

bottom of page