⭐ 推荐:社区规则条款 V1.0

怎么通过日志追踪蜘蛛爬虫首次访问网站的入口页面?

liulian
liulian 归队站长新人站长
发布于 2026-10-14 22:13 ·2 浏览 ·3 回复
内容摘要

通过访问日志按 User-Agent 筛出蜘蛛,以 IP+User-Agent 切分会话并按时间排序,取每会话首条记录确定入口页面,全站首次取最早一条,并用反向 DNS 或官方 IP 段验证,不能依赖 Referer。

结论:追踪蜘蛛首次访问的入口页面,不要看 Referer,而是从访问日志里按 User-Agent 筛出蜘蛛,再用「IP+User-Agent」切分会话,按时间排序后取每个会话第一条记录;要找全站首次,就取该蜘蛛所有日志中时间最早的一条。最后用反向 DNS 或官方 IP 段验证,避免 UA 伪装。

为什么蜘蛛的入口页面不能靠 Referer 判断?

结论:蜘蛛请求不带 Referer,日志里的 $http_referer 字段对蜘蛛为 "-",所以入口页面只能按时间序列确定。

搜索引擎蜘蛛是主动发起抓取,HTTP 请求头没有来源页面。普通用户从百度结果页点击进入,Referer 是 https://www.baidu.com/...;蜘蛛抓取 /article/123 时,Referer 为空或站内上一页。Nginx combined 日志中 Referer 是倒数第二个引号字段,蜘蛛行显示为 "-"。拿 Referer 找入口,会把蜘蛛误判为直接访问。

日志要满足什么格式和保留周期?

结论:Nginx 使用 combined 格式即可满足追踪需求,日志必须包含 User-Agent 和 Referer,且保留周期要覆盖首次访问日期。

Nginx 默认 combined 格式为:
$remote_addr - $remote_user [$time_local] "$request" $status $body_bytes_sent "$http_referer" "$http_user_agent"

文件在 /var/log/nginx/access.log,轮转后是 access.log.1、access.log.2.gz。如果 logrotate 只保留 7 天,而蜘蛛首次访问在 8 天前,日志已经删除,无法追踪。把 /etc/logrotate.d/nginx 里的 rotate 7 改成 rotate 30,可保留 30 份轮转日志。Apache 对应 /var/log/apache2/access.log* 或 /var/log/httpd/access_log*,格式同样用 combined。

怎么用脚本找出每个蜘蛛会话的第一条记录?

结论:按「IP+User-Agent」分组,组内按时间排序,相邻请求间隔超过 1800 秒就切一个新会话,每个新会话的第一条就是入口页面。

1800 秒是常用的会话超时阈值,蜘蛛连续抓取时相邻请求间隔小于 1800 秒;超过则视为新一次访问。执行下面 Python 脚本,它会自动读取 .gz 轮转日志,输出 IP、UA、时间、URL:

import re, gzip, glob, datetime
r = re.compile(r'^(\S+).*\[([^\]]+)\] "\S+ (\S+)[^"]*" \d+ \S+ "[^"]*" "([^"]*)"')
b = re.compile(r'Googlebot|bingbot|Baiduspider|YandexBot', re.I)
s = {}
for f in glob.glob('/var/log/nginx/access.log*'):
    o = gzip.open if f.endswith('.gz') else open
    for line in o(f, 'rt', errors='ignore'):
        m = r.match(line)
        if not m or not b.search(m.group(4)): continue
        t = datetime.datetime.strptime(m.group(2), '%d/%b/%Y:%H:%M:%S %z')
        s.setdefault((m.group(1), m.group(4)), []).append((t, m.group(3)))
for k, v in s.items():
    v.sort()
    last = None
    for t, p in v:
        if last is None or (t - last).total_seconds() > 1800:
            print(k[0], k[1][:40], t.isoformat(), p)
        last = t

如果只找全站首次,不看每个会话,把输出条件改为只保留该蜘蛛全局最早一行。若入口页面只统计内容页,在脚本里加 if re.search(r'/(robots\.txt|sitemap.*\.xml|favicon\.ico)$', p): continue,因为蜘蛛第一次抓取经常是 /robots.txt 或 /sitemap.xml。

怎么验证 IP 真是搜索引擎蜘蛛?

结论:User-Agent 可以被伪造,必须做反向 DNS 正查+反查一致,或对照官方 IP 段,否则会把假蜘蛛算进入口页面。

以 Googlebot 为例:

ip=66.249.66.1
dig +short -x $ip
dig +short crawl-66-249-66-1.googlebot.com

反查得到 crawl-66-249-66-1.googlebot.com,再正查该域名得到 66.249.66.1,两个结果一致才算真 Googlebot。Googlebot 官方 IP 段 JSON 是 https://developers.google.com/search/apis/ipranges/googlebot.json,Bingbot 是 https://www.bing.com/toolbox/bingbot.json。Bingbot 反查域名以 search.msn.com 结尾;百度蜘蛛可在百度搜索资源平台用抓取诊断和 IP 验证。

常见坑有哪些?

结论:分布式蜘蛛、日志轮转、时区排序和 robots 过滤是四个最容易出错的点。

  • 分布式蜘蛛同一 UA 会用多个 IP。只按 UA 分组会把不同 IP 的抓取混在一起,入口页面错误;必须按 IP+UA 分组。
  • 轮转日志必须一起读,access.log* 要包含 .gz,脚本里用 gzip.open 就是为此。
  • 时间不能按字符串排序。10/Oct/2023 和 09/Nov/2023 按字符串排会乱,必须解析成带 %z 的 datetime 再排序。
  • /robots.txt、/sitemap.xml、/favicon.ico 常是蜘蛛第一次请求。要研究内容入口,先排除这些 URL。

按顺序做:确认日志保留 30 天以上,用 UA 筛出蜘蛛,用 IP+UA 以 1800 秒切会话,取每会话第一条,反向 DNS 验真,最后排除 robots/sitemap。这样得到的才是可验证的蜘蛛首次入口页面。

本文转载自 GJ站长论坛,原文地址:https://www.gj0.com/thread-2016.html
转载请注明出处,版权归原作者所有。
—/ 5暂无评分
还没有评分,来打第一分吧

全部回复 3

满月居于空
满月居于空 版主资深会员新人站长 👑年卡会员 1楼 2026-10-14 22:38

方法没问题,但「IP+UA」切会话在 Googlebot 上会过度切分,建议用 UA+时间窗,并且入口页要先排除 robots.txt。

先明确「入口页」的定义:绝大多数蜘蛛首次到站命中的其实是 /robots.txt 或 sitemap,真正的内容入口页是过滤掉 /robots.txt、/sitemap.*、/favicon.ico 之后的第一条记录。分析前不做这层过滤,结果永远都是 robots.txt。另外 Googlebot 会从大量不同 IP 段发起抓取,同一次连续访问常常横跨多个 IP,按 IP+UA 分组会把一次会话拆成好几段;如果只想找入口页而不是还原精确会话,直接按 UA 分组 + 1800s 间隔更实用。

验证环节,单靠 PTR 反查不够,要做 forward-confirmed reverse DNS:先 dig -x IP 拿到主机名,再正向 dig 主机名 确认能解析回同一 IP。

chinaz
chinaz 归队站长新人站长 #1659 2楼 2026-10-14 23:03
满月居于空:方法没问题,但「IP+UA」切会话在 Googlebot 上会过度切分,建议用 UA+时间窗,并且入口页要先排除 robots.txt。 先明确「入口页」的定…

同意,IP+UA 在 Googlebot 上确实会过切,这点我踩过坑。补几个落地细节:

1. UA 分组前先归一化。 Googlebot 至少有桌面、移动、Image/Video 几个变体,移动端 UA 形如 Mozilla/5.0 (Linux; Android ...) ... (compatible; Googlebot/2.1; ...),直接拿整串 UA 当 key,会把同一次抓取按变体切得比按 IP 还碎。建议从 UA 里正则抽出 Googlebot|bingbot|Baiduspider 这个 token 作为分组键,再配 1800s 时间窗,这样既避开多 IP 问题,也不会被变体拆散。

2. 过滤入口噪声时顺手看状态码。 排除 /robots.txt、/sitemap.*、/favicon.ico 之后,把首次命中是 404/5xx 的记录也剔掉。蜘蛛首次到站很容易先撞一个不存在的 URL,不过滤的话"入口页"会落在一个死链上,分析完全没意义。

**3. FCrDNS 只对 Googlebot 和 bingbot 有效。

满月居于空
满月居于空 版主资深会员新人站长 👑年卡会员 #1662 3楼 2026-10-14 23:13
chinaz:同意,IP+UA 在 Googlebot 上确实会过切,这点我踩过坑。补几个落地细节: **1. UA 分组前先归一化。** Googlebot 至少有桌面、…

你补的这三点基本就是落地时最容易翻车的地方,尤其 UA 归一化和状态码过滤,我完全赞成;FCrDNS 那条边界可以再补一下。

UA 归一化确实要抽 token 当分组键,但抽完不能直接信,还得拿官方 IP 段或 FCrDNS 二次确认,否则伪造 UA 会把假蜘蛛算进入口。Googlebot 有官方 JSON,bingbot 有 bingbot.json,Yandex 其实也支持 FCrDNS,所以“只对 Googlebot 和 bingbot 有效”稍保守了;Baiduspider 更稳的是直接比对百度公布的 IP 段,360、Sogou 同理。

状态码过滤我同意剔 404/5xx,但别把 301/302 全杀掉,http 跳 https 或 / 跳 /index.php 经常是真实入口,建议保留 3xx 并把 Location 一起记下来。robots.txt、sitemap 可以单独放一列,不算入口页,但能帮你判断蜘蛛第一次发现站点的时间。

如果只想找“全站首次入口”,其实不用切会话,按归一化 UA token 取最早一条最稳;要找每次访问的入口再上 1800s 窗口。低频蜘蛛可以把阈值调大点,避免被切碎。最后建议把脚本输出落库,加上 spider_name、entry_url、status、verify 字段,以后回溯不用重扫日志。