#!/usr/bin/env python3 """How much of your traffic (and bandwidth bill) is AI crawlers? Reads web server access logs in Combined Log Format (nginx, Apache default) and reports requests and bytes per AI crawler. Plain or .gz files, or stdin. python3 botcheck.py /var/log/nginx/access.log* zcat access.log.*.gz | python3 botcheck.py - Nothing leaves your machine. Stdlib only. """ import argparse import gzip import json import re import sys from datetime import datetime SITE_URL = "https://crawlbill.anas.link" # (user-agent substring, name, operator, kind). Matched case-insensitively, first hit wins. BOTS = [ ("GPTBot", "GPTBot", "OpenAI", "ai"), ("OAI-SearchBot", "OAI-SearchBot", "OpenAI", "ai"), ("ChatGPT-User", "ChatGPT-User", "OpenAI", "ai"), ("ClaudeBot", "ClaudeBot", "Anthropic", "ai"), ("Claude-User", "Claude-User", "Anthropic", "ai"), ("Claude-SearchBot", "Claude-SearchBot", "Anthropic", "ai"), ("anthropic-ai", "anthropic-ai", "Anthropic", "ai"), ("PerplexityBot", "PerplexityBot", "Perplexity", "ai"), ("Perplexity-User", "Perplexity-User", "Perplexity", "ai"), ("CCBot", "CCBot", "Common Crawl", "ai"), ("Bytespider", "Bytespider", "ByteDance", "ai"), ("Amazonbot", "Amazonbot", "Amazon", "ai"), ("meta-externalagent", "meta-externalagent", "Meta", "ai"), ("meta-externalfetcher", "meta-externalfetcher", "Meta", "ai"), ("FacebookBot", "FacebookBot", "Meta", "ai"), ("Google-CloudVertexBot", "Google-CloudVertexBot", "Google", "ai"), ("cohere-ai", "cohere-ai", "Cohere", "ai"), ("Diffbot", "Diffbot", "Diffbot", "ai"), ("YouBot", "YouBot", "You.com", "ai"), ("Timpibot", "Timpibot", "Timpi", "ai"), ("ImagesiftBot", "ImagesiftBot", "ImageSift", "ai"), ("omgili", "omgili", "Webz.io", "ai"), ("Googlebot", "Googlebot", "Google", "search"), ("bingbot", "bingbot", "Microsoft", "search"), ("Applebot", "Applebot", "Apple", "search"), ("DuckDuckBot", "DuckDuckBot", "DuckDuckGo", "search"), ("YandexBot", "YandexBot", "Yandex", "search"), ("Baiduspider", "Baiduspider", "Baidu", "search"), ("PetalBot", "PetalBot", "Huawei", "search"), ] _BOTS_LOWER = [(sub.lower(), name, op, kind) for sub, name, op, kind in BOTS] LINE_RE = re.compile( r'^\S+ \S+ \S+ \[(?P