Code Snippets Repository

Home

Code for single url on page seo checker in NODE JS output in HTML file

Wednesday, 9 August 2023 -

 'use strict';


const axios = require('axios');

const cheerio = require('cheerio');

const fs = require('fs');

const puppeteer = require('puppeteer');

const { parse } = require('schema-inspector'); // Add schema-inspector package



async function checkMobileFriendly(url) {

  const browser = await puppeteer.launch({ headless: 'new' });

  const page = await browser.newPage();

  await page.goto(url);


  const viewportMeta = await page.evaluate(() =>

    Array.from(document.querySelectorAll('meta[name="viewport"]'), element => element.content)

  );







  // More detailed mobile usability checks

  const mobileIssues = await page.evaluate(() => {

    const issues = [];

    

    // Check for small font size

    const smallFontElements = Array.from(document.querySelectorAll('body *')).filter(element => {

      const style = window.getComputedStyle(element);

      const fontSize = parseFloat(style.fontSize);

      return fontSize < 12;

    });

    if (smallFontElements.length > 0) {

      issues.push(`Found ${smallFontElements.length} elements with small font size.`);

    }


    // Check for clickable elements too close together

    const clickableElements = Array.from(document.querySelectorAll('a, button, [role="button"], input[type="button"], input[type="submit"], [tabindex]'));

    const closeElements = clickableElements.filter((element, index) => {

      const rect1 = element.getBoundingClientRect();

      return clickableElements.some((otherElement, otherIndex) => {

        if (index === otherIndex) return false;

        const rect2 = otherElement.getBoundingClientRect();

        return Math.abs(rect1.left - rect2.left) < 48 && Math.abs(rect1.top - rect2.top) < 48;

      });

    });

    if (closeElements.length > 0) {

      issues.push(`Found ${closeElements.length} sets of clickable elements too close to each other.`);

    }


    // Check for elements not fitting within the viewport

    const outsideViewportElements = Array.from(document.querySelectorAll('body *')).filter(element => {

      const rect = element.getBoundingClientRect();

      return rect.right > window.innerWidth || rect.bottom > window.innerHeight;

    });

    if (outsideViewportElements.length > 0) {

      issues.push(`Found ${outsideViewportElements.length} elements that don't fit within the viewport.`);

    }


    return issues;

  });


  await browser.close();


  return {

    isMobileFriendly: viewportMeta.includes('width=device-width, initial-scale=1') && mobileIssues.length === 0,

    issues: mobileIssues,

  };

}



// URL Structure Optimization

function optimizeURLStructure(url) {

  const urlObject = new URL(url);

  let optimizedURL = urlObject.origin + urlObject.pathname;


  // Replace underscores (_) with hyphens (-) in the URL

  optimizedURL = optimizedURL.replace(/_/g, '-');


  return optimizedURL;

}


async function analyzePage(url, page) {

  // Fetch the page

  const response = await axios.get(url);


  // Load the page into cheerio

  const $ = cheerio.load(response.data);





  // Check for meta tags

  const title = $('head > title').text() || '';

  const metaDescription = $('head > meta[name=description]').attr('content') || '';

  const metaKeywords = $('head > meta[name=keywords]').attr('content') || '';


  // Colorize the meta title and description based on their length

  const titleColor = title.length > 60 ? 'red' : 'green';

  const descriptionColor = metaDescription.length > 160 ? 'red' : 'green';


  // Check for correct HTML tags usage

  let headersData = {};

  for (let i = 1; i <= 6; i++) {

    let headers = $(`h${i}`).map((_, el) => $(el).text()).get();

    let headerColor = i === 1 && headers.length > 1 ? 'red' : 'green';

    headersData[`h${i}`] = {

      count: headers.length,

      color: headerColor,

      headers: headers

    };

  }


  let headersContent;

  if (headersData['h1'].count === 1) {

    headersContent = '<span style="color: green">Good</span>';

  } else {

    headersContent = Object.keys(headersData).map(header => `<span style="color: ${headersData[header].color}">${header}: ${headersData[header].count}, ${headersData[header].headers.join(', ')}</span>`).join('<br/>');

  }



  


  // Server Response Code

  const serverResponseCode = response.status;

  const serverResponseText = response.statusText;

  let serverResponseColor;

  if (serverResponseCode >= 200 && serverResponseCode < 300) {

    serverResponseColor = 'green';

  } else if (serverResponseCode >= 300 && serverResponseCode < 400) {

    serverResponseColor = 'yellow';

  } else {

    serverResponseColor = 'red';

  }




  // Check for alt text on images

  const imagesWithoutAlt = $('img:not([alt])');

  const imagesWithoutAltCount = imagesWithoutAlt.length;

  const imagesWithoutAltColor = imagesWithoutAltCount > 0 ? 'red' : 'green';

  let imagesWithoutAltSrcList = '';

  imagesWithoutAlt.each((i, img) => {

    imagesWithoutAltSrcList += `<li>${$(img).attr('src')}</li>`;

  });


  // Check for canonical URL

  const canonicalUrls = $('head > link[rel=canonical]').map((i, link) => $(link).attr('href')).get();

  const uniqueCanonicalUrls = [...new Set(canonicalUrls)];

  const canonicalUrlCount = uniqueCanonicalUrls.length;

  const duplicateCanonicalUrls = canonicalUrls.length > canonicalUrlCount;

  const canonicalColor = duplicateCanonicalUrls ? 'red' : 'green';

  let canonicalUrlsList = uniqueCanonicalUrls.map(url => `<li><span style="color: ${canonicalColor}">${url}</span></li>`).join('');


  // Calculate Text to HTML ratio

  const textContent = $('body').text();

  const htmlContentLength = response.data.length;

  const textContentLength = textContent.length;

  const textToHtmlRatio = ((textContentLength / htmlContentLength) * 100).toFixed(2);


  // Check SEO-friendly URL

  function checkURL(url) {

    const urlObject = new URL(url);

    const urlPath = urlObject.pathname;


    if (urlPath.length > 100) return { friendly: false, reason: 'URL is too long (more than 100 characters)' };

    if (urlObject.search) return { friendly: false, reason: 'URL contains parameters' };

    if (urlPath !== urlPath.toLowerCase()) return { friendly: false, reason: 'URL is not lowercase' };

    if (!/^[\w\-/]+$/.test(urlPath)) return { friendly: false, reason: 'URL contains special characters or spaces' };

    if (/\.[0-9a-z]+$/i.test(urlPath)) return { friendly: false, reason: 'URL ends with a file extension' };

    if (/_/.test(urlPath)) return { friendly: false, reason: 'URL uses underscores instead of hyphens' };

    if (urlPath.split('/').length > 3) return { friendly: false, reason: 'URL contains more than 2 subdirectories' };


    return {

      friendly: true,

      reason: 'URL is short, contains no parameters or special characters, is lowercase, does not end with a file extension, uses hyphens as word separators, and contains 2 or fewer subdirectories'

    };

  }


  // Check Social Media Meta Tags

  function checkSocialMediaMetaTags($) {

    const ogTags = $('head > meta[property^="og:"]');

    const twitterTags = $('head > meta[name^="twitter:"]');

    return ogTags.length > 0 || twitterTags.length > 0;

  }


  // Check Schema Markup

  function checkSchemaMarkup($) {

    const schemaTags = $('script[type="application/ld+json"]');

    const schemaCount = schemaTags.length;

    const hasSchemaMarkup = schemaCount > 0;

    const schemaColor = hasSchemaMarkup ? 'green' : 'red';

    const schemaMarkupText = hasSchemaMarkup ? 'Present' : 'Not Present';


    let schemaMarkupContent = '';

    if (hasSchemaMarkup) {

      schemaTags.each((i, tag) => {

        schemaMarkupContent += `<pre>${$(tag).html()}</pre>`;

      });

    }


    return {

      count: schemaCount,

      color: schemaColor,

      markupText: schemaMarkupText,

      markupContent: schemaMarkupContent

    };

  }




  // Inside analyzePage function:

  const seoFriendlyURLCheck = checkURL(url);

  const seoFriendlyURLColor = seoFriendlyURLCheck.friendly ? 'green' : 'red';

  const seoFriendlyURL = `<span style="color: ${seoFriendlyURLColor}">${seoFriendlyURLCheck.reason}</span>`;


  // Mobile Friendly Check

  const isMobileFriendly = await checkMobileFriendly(url);

  const mobileFriendlyColor = isMobileFriendly ? 'green' : 'red';


  // Check Social Media Meta Tags

  const hasSocialMediaMetaTags = checkSocialMediaMetaTags($);

  const socialMediaTagsColor = hasSocialMediaMetaTags ? 'green' : 'red';

  const socialMediaTagsText = hasSocialMediaMetaTags ? 'Used' : 'Not Used';


  // Check Schema Markup

  const schemaMarkup = checkSchemaMarkup($);

  const schemaMarkupColor = schemaMarkup.color;

  const schemaMarkupText = schemaMarkup.markupText;

  const schemaMarkupContent = schemaMarkup.markupContent;



  // Create HTML content

  let htmlContent = `

    <h1>Analysis of ${url}</h1>

    <p>Title: <span style="color: ${titleColor}">${title} (${title.length} characters)</span></p>

    <p>Meta description: <span style="color: ${descriptionColor}">${metaDescription} (${metaDescription.length} characters)</span></p>

    <p>Headers: ${headersContent}</p>

    <p>Images without alt text: <span style="color: ${imagesWithoutAltColor}">${imagesWithoutAltCount}</span></p>

    <ul>${imagesWithoutAltSrcList}</ul>

    <p>Canonical URL(s): <ul>${canonicalUrlsList}</ul></p>

    <p>Text to HTML Ratio: ${textToHtmlRatio}%</p>

    <p>SEO-friendly URL: ${seoFriendlyURL}</p>

    <p>Mobile-Friendly: <span style="color: ${mobileFriendlyColor}">${isMobileFriendly ? 'Yes' : 'No'}</span></p>

<p>Server Response: <span style="color: ${serverResponseColor}">${serverResponseCode} ${serverResponseText}</span></p>

    <p>Social Media Meta Tags: <span style="color: ${socialMediaTagsColor}">${socialMediaTagsText}</span></p>

    <p>Schema Markup: <span style="color: ${schemaMarkupColor}">${schemaMarkupText}</span></p>

    ${schemaMarkupContent}


  `;


  // Security Analysis

  const secureContent = response.data.includes('https:');

  const insecureElements = [];


  $('script[src], img[src], link[href]').each((i, element) => {

    const src = $(element).attr('src');

    const href = $(element).attr('href');


    if (src && !src.startsWith('https:')) {

      insecureElements.push(src);

    }


    if (href && !href.startsWith('https:')) {

      insecureElements.push(href);

    }

  });


  let insecureElementsContent;

  if (insecureElements.length > 0) {

    const insecureElementsList = insecureElements.map(element => `<li>${element}</li>`).join('');

    insecureElementsContent = `<p style="color: red">Insecure Elements:</p><ul>${insecureElementsList}</ul>`;

  } else {

    insecureElementsContent = '<p style="color: green">No insecure elements found.</p>';

  }


  htmlContent += insecureElementsContent;




  return htmlContent;

}


// Function to analyze responsiveness across different devices and screen sizes

async function analyzeResponsiveness(url) {

  const browser = await puppeteer.launch({ headless: 'new' });

  const page = await browser.newPage();


  // Define the devices and screen sizes you want to test

  const devices = [

    {

      name: 'Desktop',

      userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.212 Safari/537.36',

      viewport: {

        width: 1920,

        height: 1080,

        deviceScaleFactor: 1,

        isMobile: false,

        hasTouch: false,

        isLandscape: true,

      }

    },

    {

      name: 'Tablet',

      userAgent: 'Mozilla/5.0 (iPad; CPU OS 14_5 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) CriOS/90.0.4430.212 Mobile/15E148 Safari/604.1',

      viewport: {

        width: 768,

        height: 1024,

        deviceScaleFactor: 2,

        isMobile: true,

        hasTouch: true,

        isLandscape: false,

      }

    },

    {

      name: 'Mobile',

      userAgent: 'Mozilla/5.0 (iPhone; CPU iPhone OS 14_5 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.1 Mobile/15E148 Safari/604.1',

      viewport: {

        width: 375,

        height: 812,

        deviceScaleFactor: 3,

        isMobile: true,

        hasTouch: true,

        isLandscape: false,

      }

    }

  ];


  const results = {};


  for (const device of devices) {

    await page.setUserAgent(device.userAgent);

    await page.setViewport(device.viewport);

    await page.goto(url);


    // Wait for images to be fully loaded

    await page.waitForFunction(() => {

      const images = Array.from(document.images);

      return images.every(img => img.complete && img.naturalHeight !== 0);

    });


    // Perform responsiveness analysis for the device

    const deviceName = device.name;

    const deviceScreenshotPath = `responsiveness_${deviceName.toLowerCase()}.png`;

    await page.screenshot({ path: deviceScreenshotPath });


    results[deviceName] = deviceScreenshotPath;

  }


  await browser.close();


  return results;

}


// Wrap code in IIFE to handle async

(async () => {

  try {

    // Replace with the URL you want to analyze

    const url = 'https://www.clicky.pk/moonwalk-men-black-best-synthetic-rubber-sports-shoes-for-athletic-performance-black?id=6492d9459a567e108a95dd83';


    const browser = await puppeteer.launch({ headless: 'new' });

    const page = await browser.newPage();


    const htmlContent = await analyzePage(url, page);


    // Add responsiveness analysis to HTML content

    const responsivenessResults = await analyzeResponsiveness(url);

    const responsivenessContent = Object.keys(responsivenessResults)

      .map(deviceName => `<h2>${deviceName}:</h2><img src="${responsivenessResults[deviceName]}" alt="${deviceName}" />`)

      .join('');


    // Combine all the analysis results

    const combinedContent = htmlContent + responsivenessContent;


    // Write to HTML file

    fs.writeFile('output.html', combinedContent, (error) => {

      if (error) {

        console.error('Error writing to file:', error);

      } else {

        console.log('Analysis results written to output.html');

      }

    });


    await browser.close();

  } catch (error) {

    console.error('Error analyzing page:', error.message);

  }

})();