Personal statistics

Posted under General

I read CoreMack's comment #2515003 earlier and it made me curious if there was an easy way to similarly break down my own posts by artist, copyright, character, gentag, etc; essentially something similar to the post reports. I've posted a lot over the last half year, and I wonder what some of the numbers are. I know I could do it manually by searching, but I am interested in a more automated method.

I'm sure there are better ways to do this, especially the people who actually know the inner workings of Danbooru or database wizards, but I've whipped up this ai-assisted Python script to scrape your data. I've used something similar to collect the data for comment #2547977 on post #10000000.

In short, this script asks for an username. It then browses through https://danbooru.donmai.us/posts.json?limit=100&page={page}&tags=user%3A{user} and collects all post IDs.
It then goes through each ID and counts all tags it finds grouped per category.

Python code
import requests
import time
import os
from collections import Counter

def fetch_ids(username, debug=False):
    """Fetch IDs from Danbooru for a given username."""
    base_url = "https://danbooru.donmai.us/posts.json?limit=100&page={page}&tags=user%3A{user}"
    page = 1
    all_ids = []

    while True:
        url = base_url.format(page=page, user=username)
        print(f"Fetching page {page} for user '{username}'...")

        response = requests.get(url)
        response.raise_for_status()
        data = response.json()

        if not data:
            print("No more data. Ending ID collection.")
            break

        ids = [str(item["id"]) for item in data if "id" in item]
        all_ids.extend(ids)

        print(f"Found {len(ids)} IDs on page {page}")

        if debug:  # stop after 1 page in debug mode
            break

        time.sleep(0.2)  # polite delay
        page += 1

    # Save IDs to file
    id_filename = f"{username}_ids.txt"
    with open(id_filename, "w") as f:
        f.write("\n".join(all_ids))
    print(f"Saved {len(all_ids)} IDs to {id_filename}")

    return all_ids

def process_ids(ids, username):
    """Fetch tags for each ID and count occurrences."""
    post_url = "https://danbooru.donmai.us/posts/{id}.json"

    general_counter = Counter()
    copyright_counter = Counter()
    character_counter = Counter()
    artist_counter = Counter()

    for idx, post_id in enumerate(ids, start=1):
        url = post_url.format(id=post_id)
        print(f"[{idx}/{len(ids)}] Fetching post {post_id}...")

        response = requests.get(url)
        response.raise_for_status()
        post_data = response.json()

        # General tags
        if "tag_string_general" in post_data and post_data["tag_string_general"]:
            general_counter.update(post_data["tag_string_general"].split())

        # Copyright tags
        if "tag_string_copyright" in post_data and post_data["tag_string_copyright"]:
            copyright_counter.update(post_data["tag_string_copyright"].split())

        # Character tags
        if "tag_string_character" in post_data and post_data["tag_string_character"]:
            character_counter.update(post_data["tag_string_character"].split())

        # Artist tags
        if "tag_string_artist" in post_data and post_data["tag_string_artist"]:
            artist_counter.update(post_data["tag_string_artist"].split())

        time.sleep(0.2)  # polite delay

    # Save results to separate files
    save_counter(general_counter, f"{username}_general_tags.txt", "General Tags")
    save_counter(copyright_counter, f"{username}_copyright_tags.txt", "Copyright Tags")
    save_counter(character_counter, f"{username}_character_tags.txt", "Character Tags")
    save_counter(artist_counter, f"{username}_artist_tags.txt", "Artist Tags")

def save_counter(counter, filename, label):
    """Save counter results to a file and print them."""
    print(f"\n=== {label} ===")
    with open(filename, "w") as f:
        for tag, count in counter.most_common():
            print(f"{tag}: {count}")
            f.write(f"{tag}: {count}\n")
    print(f"Results saved to {filename}")

def main():
    # Prompt user for username
    username = input("Enter the Danbooru username: ")

    # Ask if debug mode
    debug_input = input("Enable debug mode (only 1 page)? [Y/N]: ").strip().lower()
    debug = debug_input == "y"

    # Ask if loading IDs from file
    load_input = input("Load IDs from existing file instead of fetching? [Y/N]: ").strip().lower()
    if load_input == "y":
        id_filename = f"{username}_ids.txt"
        if os.path.exists(id_filename):
            with open(id_filename, "r") as f:
                ids = [line.strip() for line in f if line.strip()]
            print(f"Loaded {len(ids)} IDs from {id_filename}")
        else:
            print(f"No file {id_filename} found. Fetching IDs instead...")
            ids = fetch_ids(username, debug)
    else:
        ids = fetch_ids(username, debug)

    # Process IDs
    process_ids(ids, username)

if __name__ == "__main__":
    main()

Edit: Just realised I forgot the artists. And updated the table to be more compact and changed it into a Top 20.

#ArtistsCopyrightsCharacterGeneral
1dairi435original3874yuzuha_riko2261girl5629
2fangpeii101stellive721gotoh_hitori152solo4965
3midori_(mira567)80touhou468tenko_shibuki142long_hair4681
4jimini_shijimi80blue_archive355hanako_nana123looking_at_viewer3769
5lemontyoisy_r1879bocchi_the_rock!242ijichi_nijika117shirt3559
6zaq_(pgvc4472)75hololive198frieren97blush3297
7makomako106171touhou_(pc-98)195kita_ikuyo97breasts3095
8nyankosisyou65idolmaster156arahashi_tabi81standing2639
9ito_yoshi60sousou_no_frieren149shirayuki_hina74closed_mouth2613
10lensia59indie_virtual_youtuber91hatsune_miku73long_sleeves2536
11nengoro57vocaloid82yamada_ryo68open_mouth2410
12napo_(napo_illust)55phantasmagoria_of_dim.dream82asakura_rikako63smile2363
13urushiushiru52idolmaster_cinderella_girls61fern_(sousou_no_frieren)61simple_background2157
14artdroiiid_ii50project_sekai59kitashirakawa_chiyuri61skirt2132
15moonsorrow50gakuen_idolmaster58akane_lize59white_shirt1964
16lin_oekaki47lycoris_recoil57yagokoro_eirin59bow1919
17kinoko_no_sato44honkai_(series)51neneko_mashiro58holding1864
18wakusei_habuti44fate_(series)46white-haired_man_(jimini_shijimi)54outdoors1763
19paruperu43honkai:_star_rail45murasa_minamitsu54large_breasts1730
20chaeeunhan12442arknights45ayatsuno_yuni52navel1716

Updated by GabrielWB

1