UNPKG

sitemap-xml-parser

Version:

It parses xml based on sitemap.xml and gets all files described in sitemap. Supports gz format

128 lines (115 loc) 4.36 kB
'use strict'; const request = require('request'); const xml2js = require('xml2js'); const parser = new xml2js.Parser(); const bluebird = require('bluebird'); const promiseMap = bluebird.map; const delay = bluebird.delay; const Url = require('url'); const path = require('path'); const zlib = require("zlib"); class SitemapXMLParser { constructor(url, options) { this.siteMapUrl = url; this.delayTime = options.delay ? options.delay : 3000; this.limit = options.limit ? options.limit : 5; this.urlArray = []; } async fetch() { //トップページのXMLを取得 const indexBody = await this.getBodyFromURL(this.siteMapUrl); const indexXML = await this.executeParseXml(indexBody); //URL一覧を取得 await this.getURLFromXML(indexXML) //サイトマップの一覧 return this.urlArray; }; async getURLFromURL(url) { let body = await this.getBodyFromURL(url); let sitemapData = await this.executeParseXml(body); await this.getURLFromXML(sitemapData); return delay(this.delayTime); } /** * サイトマップ一覧からURLを取得する * サイトマップインデックスファイルの場合は、リンク先にアクセスしてURLを集める * @param {*} xml */ async getURLFromXML(xml) { let sitemapIndexData = []; if (xml.sitemapindex && xml.sitemapindex.sitemap ) { //サイトマップインデックスファイルの場合 for (let i = 0; i < Object.keys(xml.sitemapindex.sitemap).length; i++) { sitemapIndexData.push( { url: xml.sitemapindex.sitemap[i].loc[0], this: this //TODO promiseMapの引数が1つ?のため一緒の配列にthisを入れる 本来不要 //promiseMapへは参照渡しになっているので //promiseMap内でのthisの値を変更すればpromiseMap外でもthisの値は変更される } ); } //各サイトマップインデックスファィルにアクセスしてURL一覧を取得する //Limitに指定された数で同時に処理を行う await promiseMap( sitemapIndexData, async (data) => { let body = await data.this.getBodyFromURL(data.url); let sitemapData = await data.this.executeParseXml(body); await data.this.getURLFromXML(sitemapData); return delay(data.this.delayTime); }, { concurrency: this.limit } ) } if (xml.urlset && xml.urlset.url ) { //サイトマップの場合 取得した一覧を追加 for (let i = 0; i < Object.keys(xml.urlset.url).length; i++) { if (xml.urlset.url[i]) { this.urlArray.push(xml.urlset.url[i]); } } } } /** * URLからbodyを取得する * 拡張子がgzファィルの場合は解凍する * @param {*} url */ async getBodyFromURL(url) { return new Promise(resolve => { //拡張子がgzでないか確認する let urlParse = Url.parse(url); let ext = path.extname(urlParse.path); if (ext == '.gz') { request(url, { encoding: null }, function (error, response, body) { zlib.gunzip(body, function (error, result) { resolve(result.toString()); }); }); } else { request(url, function (error, response, body) { resolve(body.toString()); }); } }); } /** * 実際にXMLのパースを行う関数 * @param {*} value */ async executeParseXml(xml) { return new Promise(resolve => { parser.parseString(xml, (error, result) => { resolve(result); }); }) } } module.exports = SitemapXMLParser; module.exports.default = SitemapXMLParser;