Skip to content
搜索引擎爬虫访问网站时,通常会首先请求 /robots.txt。这个纯文本文件声明了当前协议、主机、端口下哪些路径允许抓取、哪些路径应当避开。它不具备强制力——恶意爬虫可以完全忽略,但所有主流搜索引擎都会宣称支持自己认可的机器人排除协议(REP)规则。
从零编写 robots.txt
文件必须放在网站根目录,例如 https://example.com/robots.txt。每个子域需要各自放置一份。使用纯文本编辑器创建,保存为 UTF-8 编码(无 BOM),Google 目前将文件大小限制为 500 KiB,超出部分会被忽略。
基本的规则结构由字段、冒号和值组成:
User-agent: <bot名称>
Disallow: <路径>
Allow: <路径>字段名不区分大小写,但值区分大小写。行首尾空格会被丢弃,# 之后的内容视为注释。如果不写任何规则,所有路径默认允许抓取。
如果希望禁止所有爬虫访问 /admin 目录:
User-agent: *
Disallow: /admin/User-agent: * 表示规则适用于所有爬虫。Disallow: /admin/ 则禁止匹配该前缀的路径。
针对特定爬虫的写法:
User-agent: Googlebot
Disallow: /private/
Disallow: /tmp/
User-agent: *
Disallow:第一个规则组只对 Googlebot 生效,禁止 /private/ 和 /tmp/。第二个规则组没有 Allow 且 Disallow 值为空,表示允许所有爬虫抓取任何内容——这是显式全开放的写法。
语法与字段
User-agent、Disallow 与 Allow
- User-agent:声明规则组适用哪个爬虫,可以写具体的名称(
Googlebot、Bingbot)或*表示所有。多个User-agent行可以指向同一组规则,匹配时 Google 会按优先级选择。 - Disallow:值必须以
/开头,代表从网站根目录起的相对路径。匹配方式是前缀匹配:Disallow: /images会同时阻挡/images、/images/photo.jpg以及/images2024/。若要精确匹配目录,建议尾部带斜杠,如Disallow: /images/。 - Allow:用于在宽泛的禁止规则中打开白名单。例如:
User-agent: *
Disallow: /downloads/
Allow: /downloads/public/此时 /downloads/public/manual.pdf 允许抓取,其余 /downloads/ 下的路径则被禁止。Allow 无法独立发挥作用,它必须与一条更广的 Disallow 搭配。当多条规则重叠时,爬虫会选择最具体(路径更深、字符数更多)的那一条,而不是按照书写顺序。Google 在解析时会忽略无效行,文件开头如果出现 Unicode BOM 也会被跳过。
Crawl-delay 与 Sitemap 指令
Crawl-delay 用于设置两次请求之间的最短间隔(秒)。Google 不识别该字段,如需调节抓取速率,需在 Search Console 中操作。
Sitemap 字段不受 User-agent 约束,可以出现在文件的任何位置,值为站点地图或站点地图索引的完整 HTTPS URL,可以写多条:
Sitemap: https://example.com/sitemap.xml
Sitemap: https://example.com/sitemap-pages.xml典型抓取控制场景
禁止整个网站
User-agent: *
Disallow: /只禁止特定目录
User-agent: *
Disallow: /members/
Disallow: /checkout/禁止特定后缀的文件
借助通配符 *(匹配任意字符序列)和 $(行尾匹配):
User-agent: *
Disallow: /*.xls$
Disallow: /*.docx$对不同爬虫分别控制
User-agent: Googlebot
Disallow: /staging/
User-agent: Bingbot
Disallow: /archives/规则组之间没有继承关系,爬虫只会匹配与自身最相关的那一组,不会参考其他组的规则。
校验与测试
文件编写完成后需要验证,不应直接依赖它来阻止索引。
Google Search Console 提供了 robots.txt 测试工具:可以粘贴文件内容,指定爬虫名称和路径,得到允许或禁止的结果,同时会标记无效行。命令行也可以用后面给出的 Node.js 解析器快速检查。
注意服务器返回的状态码会直接影响 Google 的行为:
- 200:正常处理。
- 404:视为没有
robots.txt,所有内容允许抓取。 - 5xx 等服务器错误:Google 会暂停抓取,在此期间当作禁止抓取。
- 301 重定向:会跟随重定向后的地址获取规则。
常见失误包括:
- 文件错放在子目录(如
/blog/robots.txt),不生效。 Disallow值未以/开头,被当作无效行忽略。- 值中包含不必要的 URL 编码(如
%20),会被按字面匹配,不会解码。 - 文件超过 500 KiB,超出部分直接丢弃,可能遗漏关键规则。
- 服务器错误页面返回 HTML,Google 会尝试从 HTML 中提取类似
Disallow的文本,导致意外行为。
Sitemap
robots.txt 负责阻挡不必要或敏感的路径,Sitemap 负责把需要索引的重要页面显式列出来交给爬虫。两者是互补关系。
XML 格式
一个最简的 Sitemap 文件:
xml
<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<url>
<loc>https://example.com/</loc>
<lastmod>2025-03-15</lastmod>
<changefreq>weekly</changefreq>
<priority>1.0</priority>
</url>
<url>
<loc>https://example.com/blog/v8-optimization</loc>
<lastmod>2025-03-10</lastmod>
</url>
</urlset>标签说明:
<loc>(必填):页面的完整 URL,不包含换行符,必要时使用实体编码。<lastmod>(可选):YYYY-MM-DD格式的最后修改时间,搜索引擎据此判断是否需要重新抓取。<changefreq>(可选):大致更新频率,仅作为信号,爬虫不一定严格执行。可选值有always、hourly、daily、weekly、monthly、yearly、never。<priority>(可选):相对于本站其他页面的优先级,范围0.0到1.0,默认0.5,仅在本站点内部有意义。
单个 Sitemap 文件最多包含 50,000 个 URL,未压缩体积不超过 50 MiB。超过时可使用 Sitemap 索引文件 组合多个子 Sitemap。
动态生成 Sitemap
静态网站可以在构建阶段生成,内容动态变化的站点更适合通过脚本从数据库或路由配置中产出。
以下 Node.js 脚本接收页面列表,生成 XML:
typescript
const fs = require('fs');
interface Page {
loc: string;
lastmod: string;
changefreq?: string;
priority?: number;
}
function generateSitemapXML(pages: Page[]): string {
const urls = pages
.map(
(p) => ` <url>
<loc>${escapeXml(p.loc)}</loc>
<lastmod>${p.lastmod}</lastmod>
${p.changefreq ? `<changefreq>${p.changefreq}</changefreq>` : ''}
${p.priority !== undefined ? `<priority>${p.priority.toFixed(1)}</priority>` : ''}
</url>`
)
.join('\n');
return `<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
${urls}
</urlset>`;
}
function escapeXml(unsafe: string): string {
return unsafe.replace(/[<>&'"]/g, (c) => {
switch (c) {
case '<': return '<';
case '>': return '>';
case '&': return '&';
case "'": return ''';
case '"': return '"';
default: return c;
}
});
}
// 示例
const pages: Page[] = [
{ loc: 'https://example.com/', lastmod: '2025-03-15', changefreq: 'weekly', priority: 1.0 },
{ loc: 'https://example.com/about', lastmod: '2025-02-20', priority: 0.8 },
];
fs.writeFileSync('public/sitemap.xml', generateSitemapXML(pages));脚本可以直接接入构建流程,也可以定期执行。对于大型站点,应生成索引文件并考虑增量更新(只重新输出最近变化的 URL)。如果源数据位于数据库,可以按分页或时间范围查询后生成 XML。
向搜索引擎提交 Sitemap
最简单的方法是在 robots.txt 中声明 Sitemap 字段,爬虫读取规则时会自动发现:
Sitemap: https://example.com/sitemap.xml
Sitemap: https://example.com/sitemap-index.xml此外,可通过各搜索引擎的站长工具手动提交:
- Google Search Console:在“站点地图”菜单中输入 Sitemap 网址,提交后会显示发现的网址数及错误。
- Bing Webmaster Tools:在“Sitemaps”模块中提交并查看报告。
也可以向搜索引擎的提交接口发送 HTTP 请求:
GET https://www.google.com/ping?sitemap=https://example.com/sitemap.xml这种方式会立即返回状态,但不提供详细抓取报告,适合批量提交或自动化管线。
编写 robots.txt 解析器
现成的 robots-parser 库已很成熟,但自己实现一个精简版有助于理解匹配逻辑,也可以直接用于本地校验。
以下解析器支持 Disallow、Allow、User-agent(含通配)以及 Sitemap,会忽略无效行、注释和大小写差异:
typescript
interface Rule {
userAgents: string[];
disallow: string[];
allow: string[];
}
interface ParseResult {
rules: Rule[];
sitemaps: string[];
}
function parseRobotsTxt(content: string): ParseResult {
const lines = content.split(/\r?\n/);
const rules: Rule[] = [];
const sitemaps: string[] = [];
let currentRule: Rule | null = null;
for (let rawLine of lines) {
const line = rawLine.trim().replace(/#.*/, '').trim();
if (!line) continue;
const colonIndex = line.indexOf(':');
if (colonIndex === -1) continue;
const field = line.substring(0, colonIndex).trim().toLowerCase();
const value = line.substring(colonIndex + 1).trim();
if (field === 'user-agent') {
if (currentRule) rules.push(currentRule);
currentRule = { userAgents: [value], disallow: [], allow: [] };
} else if (field === 'sitemap') {
sitemaps.push(value);
} else if (currentRule) {
if (field === 'disallow') {
currentRule.disallow.push(value);
} else if (field === 'allow') {
currentRule.allow.push(value);
}
}
}
if (currentRule) rules.push(currentRule);
return { rules, sitemaps };
}检查 URL 是否允许抓取
匹配策略:先收集所有匹配该 User-Agent(或通配 *)的规则组,优先选择有精确 User-Agent 的规则组,若无则回退到通配组。然后在该组内进行前缀匹配,Allow 会覆盖 Disallow。这里的实现仅做前缀匹配,未处理 * 和 $ 通配符,但已经能覆盖多数场景。
typescript
function isAllowed(
parsed: ParseResult,
userAgent: string,
path: string
): boolean {
const matchedRules = parsed.rules.filter(
(r) =>
r.userAgents.includes(userAgent) || r.userAgents.includes('*')
);
if (matchedRules.length === 0) return true;
let selectedRule: Rule | undefined;
for (const rule of matchedRules) {
if (rule.userAgents.includes(userAgent)) {
selectedRule = rule;
break;
}
}
if (!selectedRule) {
selectedRule = matchedRules[0];
}
let allowed = true;
for (const dis of selectedRule.disallow) {
if (path.startsWith(dis)) {
allowed = false;
break;
}
}
for (const alw of selectedRule.allow) {
if (path.startsWith(alw)) {
allowed = true;
break;
}
}
return allowed;
}测试示例:
typescript
const robotsTxt = `
User-agent: *
Disallow: /private/
Allow: /private/public/
User-agent: Googlebot
Disallow: /staging/
`;
const parsed = parseRobotsTxt(robotsTxt);
console.log(isAllowed(parsed, 'Googlebot', '/staging/deploy.html')); // false
console.log(isAllowed(parsed, 'Googlebot', '/blog/post.html')); // true
console.log(isAllowed(parsed, 'Bingbot', '/private/data.json')); // false
console.log(isAllowed(parsed, 'Bingbot', '/private/public/readme')); // truerobots.txt 与 Sitemap 的协作
完整的抓取控制流程可以概括为:
- 编写
robots.txt,禁止后台管理、内部搜索页、API 响应、临时页面等不想被抓取的路径。 - 在
robots.txt中通过Sitemap字段声明 Sitemap 位置。 - 生成 Sitemap,确保其中列出的所有 URL 都在
robots.txt中未被禁止。如果 Sitemap 包含被Disallow的 URL,爬虫不会抓取,等同于白列。 - 向 Search Console 或 Bing Webmaster Tools 提交 Sitemap,持续监控抓取错误。
几点提醒:
robots.txt只控制抓取,不能替代安全措施。敏感页面应配合登录认证或noindex元标记,因为即使页面不被抓取,其网址仍然可能出现在搜索结果中(只是没有摘要)。- 动态 Sitemap 应设置合理的
lastmod,这直接影响搜索引擎重新抓取的频率。changefreq仅作为辅助信号,爬虫最终自行决策。 - 若有多个子域,每个子域都需要各自的
robots.txt,Sitemap 也应分别提交或通过索引文件统一管理。
