这篇文章使用fast-xml-parser
在node.js中演示了读写xml。我们将以docusauruses xml站点地图为例。
Docusaurus Sitemap
我是通过想在我的Docusaurus博客上编辑站点地图来撰写这篇文章的。我想从站点地图中删除/page/
和/tag/
路由。它们有效地充当重复的内容,我不希望它们被搜索引擎索引。 (还需要更多一点才能将它们从搜索引擎中删除 - 请参阅帖子末尾的部分。)
我能够在我的Docusaurus网站的build
文件夹中找到站点地图。它称为sitemap.xml
,它位于build
文件夹的根部。看起来像这样:
<?xml version="1.0" encoding="UTF-8"?><urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" xmlns:news="http://www.google.com/schemas/sitemap-news/0.9" xmlns:xhtml="http://www.w3.org/1999/xhtml" xmlns:image="http://www.google.com/schemas/sitemap-image/1.1" xmlns:video="http://www.google.com/schemas/sitemap-video/1.1">
<url>
<loc>https://blog.johnnyreilly.com/2012/01/07/standing-on-shoulders-of-giants</loc>
<changefreq>weekly</changefreq>
<priority>0.5</priority>
</url>
<url>
<loc>https://blog.johnnyreilly.com/2022/09/20/react-usesearchparamsstate</loc>
<changefreq>weekly</changefreq>
<priority>0.5</priority>
</url>
<url>
<loc>https://blog.johnnyreilly.com/page/10</loc>
<changefreq>weekly</changefreq>
<priority>0.5</priority>
</url>
<url>
<loc>https://blog.johnnyreilly.com/tags/ajax</loc>
<changefreq>weekly</changefreq>
<priority>0.5</priority>
</url>
<!-- ... -->
</urlset>
fast-xml-parser
在尝试了几个不同的XML解析器后,我在koude0上定居。它很快,很简单,维护得很好。它还可以很好地处理XML名称空间和属性。 (这在XML解析器中似乎很少见。)
让我们与我们的Docusaurus网站一起进行示例项目:
mkdir trim-xml
cd trim-xml
npx typescript --init
yarn init
yarn add @types/node fast-xml-parser ts-node typescript
和在package.json
文件中添加一个start
脚本:
{
"scripts": {
"start": "ts-node index.ts"
}
}
最后,创建一个空的index.ts
文件。
阅读XML
我们的Docusaurus Sitemap位于我们Docusaurus网站的build
文件夹中。让我们阅读并将其解析为一个JavaScript对象:
import { XMLParser, XMLBuilder } from 'fast-xml-parser';
import fs from 'fs';
import path from 'path';
interface Sitemap {
urlset: {
url: { loc: string; changefreq: string; priority: number }[];
};
}
async function trimXML() {
const sitemapPath = path.resolve(
'..',
'blog-website',
'build',
'sitemap.xml'
);
console.log(`Loading ${sitemapPath}`);
const sitemapXml = await fs.promises.readFile(sitemapPath, 'utf8');
const parser = new XMLParser({
ignoreAttributes: false,
});
let sitemap: Sitemap = parser.parse(sitemapXml);
console.log(sitemap);
}
trimXML();
我们正在使用XMLParser
类将XML解析为JavaScript对象。我们还使用ignoreAttributes
选项来确保属性包含在解析的对象中。当我们运行此功能时,我们将获得以下输出:
Loading /home/john/code/github/blog.johnnyreilly.com/blog-website/build/sitemap.xml
{
'?xml': { '@_version': '1.0', '@_encoding': 'UTF-8' },
urlset: {
url: [
[Object], [Object], [Object], [Object], [Object], [Object],
[Object], [Object], [Object], [Object], [Object], [Object],
[Object], [Object], [Object], [Object], [Object], [Object],
[Object], [Object], [Object], [Object], [Object], [Object],
[Object], [Object], [Object], [Object], [Object], [Object],
[Object], [Object], [Object], [Object], [Object], [Object],
[Object], [Object], [Object], [Object], [Object], [Object],
[Object], [Object], [Object], [Object], [Object], [Object],
[Object], [Object], [Object], [Object], [Object], [Object],
[Object], [Object], [Object], [Object], [Object], [Object],
[Object], [Object], [Object], [Object], [Object], [Object],
[Object], [Object], [Object], [Object], [Object], [Object],
[Object], [Object], [Object], [Object], [Object], [Object],
[Object], [Object], [Object], [Object], [Object], [Object],
[Object], [Object], [Object], [Object], [Object], [Object],
[Object], [Object], [Object], [Object], [Object], [Object],
[Object], [Object], [Object], [Object],
... 1481 more items
],
'@_xmlns': 'http://www.sitemaps.org/schemas/sitemap/0.9',
'@_xmlns:news': 'http://www.google.com/schemas/sitemap-news/0.9',
'@_xmlns:xhtml': 'http://www.w3.org/1999/xhtml',
'@_xmlns:image': 'http://www.google.com/schemas/sitemap-image/1.1',
'@_xmlns:video': 'http://www.google.com/schemas/sitemap-video/1.1'
}
}
我们可以看到,fast-xml-parser
库将XML解析为JavaScript对象。我们可以看到urlset
元素具有一系列的url
元素。每个url
元素都有一个loc
,changefreq
和priority
元素。我们还可以看到,urlset
元素具有许多属性。这匹配了我们之前看到的XML和我们定义的接口。
过滤和写XML
现在,我们将XML解析为JavaScript对象,我们可以像其他任何JavaScript对象一样过滤它。我们在指尖具有所有JavaScript的力量!
正如我之前提到的,我想删除代表重复内容的所有URL。这包括“分页” URL。这些是用于在内容页面之间导航的URL。例如,url https://blog.johnnyreilly.com/page/10
是分页URL。我想从站点地图中删除这些URL。我也想摆脱“标签” URL。这些URL用于在具有特定标签的帖子之间导航。例如,url https://blog.johnnyreilly.com/tags/ajax
是标签URL。我也想从站点地图中删除这些URL。
这是简单的本身,现在我们在JavaScript土地上。我们可以在url
阵列上使用filter
方法来删除我们不需要的URL:
const rootUrl = 'https://blog.johnnyreilly.com';
const filteredUrls = sitemap.urlset.url.filter(
(url) =>
url.loc !== `${rootUrl}/tags` &&
!url.loc.startsWith(rootUrl + '/tags/') &&
!url.loc.startsWith(rootUrl + '/page/')
);
然后,我们可以使用过滤的URL更新url
数组:
sitemap.urlset.url = filteredUrls;
最后,我们可以将XML写回文件:
const builder = new XMLBuilder({
ignoreAttributes: false,
});
const xml = builder.buildObject(sitemap);
const outputPath = path.resolve('sitemap.xml');
await fs.promises.writeFile(outputPath, xml);
再次注意,我们正在使用ignoreAttributes
选项来确保属性包含在XML中。
让我们把它们全部放在一个文件中:
import { XMLParser, XMLBuilder } from 'fast-xml-parser';
import fs from 'fs';
import path from 'path';
interface Sitemap {
urlset: {
url: { loc: string; changefreq: string; priority: number }[];
};
}
async function trimXML() {
const sitemapPath = path.resolve(
'..',
'blog-website',
'build',
'sitemap.xml'
);
console.log(`Loading ${sitemapPath}`);
const sitemapXml = await fs.promises.readFile(sitemapPath, 'utf8');
const parser = new XMLParser({
ignoreAttributes: false,
});
let sitemap: Sitemap = parser.parse(sitemapXml);
const rootUrl = 'https://blog.johnnyreilly.com';
const filteredUrls = sitemap.urlset.url.filter(
(url) =>
url.loc !== `${rootUrl}/tags` &&
!url.loc.startsWith(rootUrl + '/tags/') &&
!url.loc.startsWith(rootUrl + '/page/')
);
console.log(
`Reducing ${sitemap.urlset.url.length} urls to ${filteredUrls.length} urls`
);
sitemap.urlset.url = filteredUrls;
const builder = new XMLBuilder({ format: false, ignoreAttributes: false });
const shorterSitemapXml = builder.build(sitemap);
console.log(`Saving ${sitemapPath}`);
await fs.promises.writeFile(sitemapPath, shorterSitemapXml);
}
trimXML();
我们已经完成了。我们可以运行脚本并查看结果:
Loading /github/workspace/blog-website/build/sitemap.xml
Reducing 1598 urls to 281 urls
Saving /github/workspace/blog-website/build/sitemap.xml
结论
在这篇文章中,我们看到了如何使用fast-xml-parser
库将XML解析为JavaScript对象,在该对象上操作,然后将其写回XML。
如果您要在博客上直接查看我的使用方式,那么可能值得查看this PR。
PS noindex
这与XML处理无关,但我不想错过这一点。 Merely editing the sitemap isn't enough to remove them from search engines。我们还将通过调整koude31 file of our Static Web App:
来为这些路线提供noindex
响应标头
{
// ...
"routes": [
// ...
{
"route": "/tags/*",
"headers": {
"X-Robots-Tag": "noindex"
}
},
{
"route": "/page/*",
"headers": {
"X-Robots-Tag": "noindex"
}
}
]
// ...
}