UNPKG

seo-checker

Version:

A library for checking basic SEO signals of a website

146 lines (132 loc) 5.72 kB
/** * SEO Checker * Copyright (c) 2014 - 2015 Clever Labs / MIT Licensed * A library to do some basic SEO checks. */ // Set up requires var cheerio = require('cheerio'), request = require('request'), Crawler = require('simplecrawler'); module.exports = { /** * Load HTML for a single URL * * Use this to fetch the contents of a single URL then * pass the result to the `meta` function or any other * code that can parse or transform the response body of * an HTTP request. * * `url` [String] - URL of page to read * `callback` [Function] - Function to call on completion * * Returns the response body of an HTTP request as a string */ load: function(url, callback) { // Check if user input protocol if (url.indexOf('http://') < 0 && url.indexOf('https://') < 0) { // TODO: Turn this into its own function url = 'http://' + url; } // Make request and fire callback request.get(url.toLowerCase(), function(error, response, body) { if (!error && response.statusCode === 200) { return callback(body); } return callback(false); }); }, /** * Parse meta data from an HTTP response body * * `body` [String] - The HTML of a web page to parse * * Returns an object containing data related to the SEO * signals of the page that was parsed. Pass the result to * another function to determine an "SEO score". */ meta: function(body) { var $ = cheerio.load(body), page = {}; // Meta signals page.title = $('title').text() || null; page.description = $('meta[name=description]').attr('content') || null; page.author = $('meta[name=author]').attr('content') || null; page.keywords = $('meta[name=keywords]').attr('content') || null; // Heading signals var h1s = 0; $('h1').each(function() { h1s++; }); page.heading1 = $('body h1:first-child').text().trim().replace('\n', ''); page.totalHeadings = h1s; // Accessibility signals var totalImgs = 0, accessibleImgs = 0; $('img').each(function(index) { totalImgs++; if ($(this).attr('alt') || $(this).attr('title')) { accessibleImgs++; } }); page.imgAccessibility = (accessibleImgs / totalImgs) * 100; return page; }, /** * Generate SEO data for multiple pages of a site at once * * `url` [String] - The URL to begin the crawl * `options` [Object] - Options to pass to the crawler. Uses a subset of the `simplecrawler` lib's options: * - `maxPages` [Number] - The max number of pages to crawl (defaults to 10) * - `interval` [Number] - Delay between each request for a new page * - `maxDepth` [Number] - Depth of crawl. See simplecrawler docs for an explanation * - `maxConcurrency` [Number] - Number of processes to spawn at a time * - `timeout` [Number] - Time to wait for a server response before moving on * - `downloadUnsupported` [Boolean] - Determines whether crawler downloads files it cannot parse * - `userAgent` [String] - The UA string to send with requests * - `htmlOnly` [Boolean] - Tells crawler not to crawl any non-HTML text/html pages. This is a required option and has no default * * Returns an array of objects containing SEO data and URL. Example return value: * * [{ * url: 'http://example.com/page1.html', * results: { <results object identical to signature of this.meta()'s return value> } * }] */ crawl: function(url, options, callback) { var crawler = Crawler.crawl(url.toLowerCase()), opts = options || {}, maxPages = opts.maxPages || 10, parsedPages = [], // Store parsed pages in this array seoParser = this.meta, // Reference to `meta` method to call during crawl crawlResults = []; // Store results in this array and then return it to caller // Crawler settings crawler.interval = opts.interval || 250; // Time between spooling up new requests crawler.maxDepth = opts.depth || 2; // Maximum deptch of crawl crawler.maxConcurrency = opts.concurrency || 2; // Number of processes to spawn at a time crawler.timeout = opts.timeout || 1000; // Milliseconds to wait for server to send headers crawler.downloadUnsupported = opts.unsupported || false; // Save resources by only downloading files Simple Crawler can parse // The user agent string to provide - Be cool and don't trick people crawler.userAgent = opts.useragent || 'SEO Checker v1 (https://github.com/Clever-Labs/seo-checker)'; // Only fetch HTML! You should always set this option unless you have a good reason not to if (opts.htmlOnly === true) { // Being explicit about truthy values here var htmlCondition = crawler.addFetchCondition(function(parsedURL) { return !parsedURL.path.match(/\.jpg|jpeg|png|gif|js|txt|css|pdf$/i); }); } crawler.on('fetchcomplete', function(queueItem, responseBuffer, response) { if (queueItem.stateData.code === 200) { crawlResults.push({ url: queueItem.url, body: responseBuffer.toString() }); } if (crawlResults.length >= maxPages) { this.stop(); // Stop the crawler crawlResults.forEach(function(page, index, results) { parsedPages.push({url: page.url, results: seoParser(page.body)}); }); if (!callback) { return parsedPages; } else { callback(parsedPages); } } }); } };