NodeJSでsitemapをパースしてURLを抽出する。

NodeJSでsitemapをパースしてURLを抽出する。 JavaScript




概要

NodeJSでsitemap.xmlからURLを抽出する方法のメモ。
ちょっとググるとsitemap-stream-parserなんてものがありましたが、正直イケてませんでした。

PythonならBeautifulSoupという強力なライブラリがありますが、
せっかくなのでNodeJS自作しました。

環境 + 利用したもの

macOS
NodeJS v10.10.0
sync-request ※同期処理でリクエストを投げる
xml2json *XMLをJSONに変換するモジュール

実装

実装で気をつけたポイント

sitemap.xmlはURLがそのまま記述されているパターンのものと、
子のxmlのパスが記述されているパターンがあります。

ちょっとググるとこの点を考慮していないものも見受けられますが、
紹介する実装はこの点を考慮しました。

シンプルに配列を返すパターン

const request = require('sync-request');
const parser = require('xml2json');

const url = "https://tech-blog.s-yoshiki.com/sitemap.xml"
console.log(JSON.stringify(getSitemap(url)))

function getSitemap(sitemap_url) {
    var result = []
    var response = request(
        'GET',
        sitemap_url
    );

    if (response.statusCode !== 200) {
        console.log("Status Code (function) : " + response.statusCode);
        return;
    }

    var data = JSON.parse(
        parser.toJson(
            response.getBody('utf8')
        )
    )
    if (data["urlset"]) {
        if (data["urlset"]["url"].length > 0) {
            data["urlset"]["url"].forEach((v) => {
                result.push(v)
            })
        } else if (data["urlset"]["url"]) {
            result.push(data["urlset"]["url"])
        }
    }

    if (!data["sitemapindex"] || !data["sitemapindex"]["sitemap"]) {
        return result
    }

    if (data["sitemapindex"]["sitemap"].length > 0) {
        data["sitemapindex"]["sitemap"].forEach((v) => {
            Array.prototype.push.apply(result, getSitemap(v.loc));
        })
    } else if (data["sitemapindex"]["sitemap"]["loc"]) {
        result.push(getSitemap(v.loc))
    }
    return result
}

非同期実行にする場合

getSitemap(url, (v)=> {
    console.log(v)
}, (err) => {
    console.log(err)
})

function getSitemap(sitemap_url, success_callback, err_callback) {
    var response = request(
        'GET',
        sitemap_url
    );

    if (response.statusCode !== 200) {
        err_callback(response)
        return;
    }

    var data = JSON.parse(
        parser.toJson(
            response.getBody('utf8')
        )
    )
    if (data["urlset"]) {
        if (data["urlset"]["url"].length > 0) {
            data["urlset"]["url"].forEach((v) => {
                success_callback(v)
            })
        } else if (data["urlset"]["url"]) {
            success_callback(data["urlset"]["url"])
        }
    }

    if (!data["sitemapindex"] || !data["sitemapindex"]["sitemap"]) {
        return;
    }

    if (data["sitemapindex"]["sitemap"].length > 0) {
        data["sitemapindex"]["sitemap"].forEach((v) => {
            getSitemap(v.loc, success_callback, err_callback)
        })
    } else if (data["sitemapindex"]["sitemap"]["loc"]) {
        getSitemap(v.loc, success_callback, err_callback)
    }
}

出力

出力例として、下記のような出力がされます。

{ loc: 'https://tech-blog.s-yoshiki.com/2018/01/13/',
  lastmod: '2018-05-03T05:45:57+00:00',
  changefreq: 'monthly',
  priority: '1.0' }
{ loc: 'https://tech-blog.s-yoshiki.com/2018/01/10/',
  lastmod: '2018-09-09T07:59:35+00:00',
  changefreq: 'monthly',
  priority: '1.0' }
{ loc: 'https://tech-blog.s-yoshiki.com/2018/01/5/',
  lastmod: '2018-01-22T12:15:57+00:00',
  changefreq: 'monthly',
  priority: '1.0' }
{ loc: 'https://tech-blog.s-yoshiki.com/blog_history/',
  lastmod: '2018-09-24T15:38:10+00:00',
  changefreq: 'weekly',
  priority: '0.3' }
{ loc: 'https://tech-blog.s-yoshiki.com/contents/',
  lastmod: '2018-06-16T14:48:38+00:00',
  changefreq: 'weekly',
  priority: '0.3' }
{ loc: 'https://tech-blog.s-yoshiki.com/about-me/',
  lastmod: '2018-07-28T13:28:55+00:00',
  changefreq: 'weekly',
  priority: '0.3' }