← Back to Library Pop-Up Tools
Suspicious Books Screener (suspicious_books_screener.py)
This tool adds a small suspicious_ai_like_score (0–3) and a short note to help you spot titles that may be
mass-produced or very low-effort, based on generic titles and templated descriptions. It cannot prove a book is AI-generated.
How It Works (In Plain Language)
Title / Author / Description / …
Looks for generic phrases and very long, keyword-heavy titles.
Looks for template phrases like "In this book you will learn…".
Optionally notes if the same author appears on many titles.
score 0–3 + notes
suspicious_ai_like_score and suspicious_notes
- Looks at key text fields such as title, author, and description/summary in your CSV.
- Checks the title for red-flag phrases like "Ultimate Guide", "Comprehensive Guide", "for Beginners and Experts" and very long, keyword-stuffed titles.
- Checks the description for template-like phrases such as "In this book you will learn…", "This comprehensive guide will…" and lots of repeated "you will…" lines.
- Optionally looks at author volume and adds a small extra score if the same author appears on many titles in the same file.
- Combines these into a 0–3 score and a short note explaining why it flagged the record (for example "generic/long title; repetitive description").
- Writes a new CSV with two extra columns:
suspicious_ai_like_scoreandsuspicious_notes, so you can sort and review in your own way.
How to Use
- Try it with the included
sample_books.csvfirst. - Open Terminal and run:
cd ~/Desktop/library_pop_up_tools - Then run:
python suspicious_books_screener.py sample_books.csv sample_books_with_flags.csv - Open
sample_books_with_flags.csvand sort bysuspicious_ai_like_score. - For your own export, replace
sample_books.csvwith your own CSV file name.
Screened 4 books.
Score 0: 2 titles
Score 3: 2 titles
Flagged file written to: sample_books_with_flags.csv
Optional: Adjust What It Watches For
You can use this tool exactly as-is and never touch the code. The list below is just a set of "phrases to watch for": like settings. Only change them if you want to look for something specific.
Key phrases it looks for (simplified view):
GENERIC_PHRASES_TITLE = [
"ultimate guide",
"made easy",
"using chatgpt",
"passive income",
# add your own...
]
GENERIC_PHRASES_DESC = [
"in this book you will learn",
"no prior knowledge required",
"financial freedom",
"chatgpt prompts",
# add your own...
]
To add a new phrase later, you can open suspicious_books_screener.py, find these two lists near the top,
add a new line like "instant results",, save, and run the tool again.
Full Python Source (Optional)
Click to show the full script
#!/usr/bin/env python3
"""
suspicious_books_screener.py
Pop-up tool to flag books that look "mass-produced" or low-effort,
based on simple, transparent rules. It does NOT prove that a book is
AI-generated. It only surfaces titles that might deserve closer review.
Usage:
python suspicious_books_screener.py books.csv books_with_flags.csv
"""
import csv
import sys
from collections import Counter
from pathlib import Path
GENERIC_PHRASES_TITLE = [
"ultimate guide",
"comprehensive guide",
"for beginners",
"for beginners and experts",
"step by step",
"step-by-step",
"the complete guide",
"the essential guide",
]
GENERIC_PHRASES_DESC = [
"in this book you will learn",
"in this book, you will learn",
"this book will teach you",
"this comprehensive guide will",
"by the end of this book",
"whether you are a beginner or an expert",
"this book covers everything you need to know",
]
def score_title(title: str) -> int:
if not title:
return 0
t = title.lower()
score = 0
if len(title) > 120:
score += 1
matches = sum(1 for phrase in GENERIC_PHRASES_TITLE if phrase in t)
if matches >= 1:
score += 1
if "," in title and ":" in title and len(title.split()) > 15:
score += 1
return score
def score_description(desc: str) -> int:
if not desc:
return 0
d = desc.lower()
score = 0
matches = sum(1 for phrase in GENERIC_PHRASES_DESC if phrase in d)
if matches >= 2:
score += 2
elif matches == 1:
score += 1
if desc.count("you will") >= 3:
score += 1
if len(desc) > 0 and len(set(desc.split())) < len(desc.split()) * 0.5:
score += 1
return score
def count_authors(rows: list[dict], author_key: str) -> Counter:
counter: Counter = Counter()
for row in rows:
author = row.get(author_key, "").strip()
if author:
counter[author] += 1
return counter
def detect_author_volume_flags(rows: list[dict], author_key: str) -> dict:
result: dict[int, int] = {}
if not author_key:
return result
counts = count_authors(rows, author_key)
high_volume_authors = {a for a, c in counts.items() if c >= 10}
if not high_volume_authors:
return result
for idx, row in enumerate(rows):
author = row.get(author_key, "").strip()
if author in high_volume_authors:
result[idx] = 1
return result
def choose_key(row: dict, candidates: list[str]) -> str:
for name in candidates:
for key in row.keys():
if key.lower() == name.lower():
return key
return ""
def screen_books(input_path: Path, output_path: Path) -> None:
with input_path.open(newline="", encoding="utf-8-sig") as infile:
reader = csv.DictReader(infile)
rows = list(reader)
if not rows:
print("No rows found in input file.")
return
sample_row = rows[0]
title_key = choose_key(sample_row, ["title", "book_title"])
author_key = choose_key(sample_row, ["author", "author_name"])
desc_key = choose_key(sample_row, ["description", "desc", "summary", "blurb"])
author_volume_scores = detect_author_volume_flags(rows, author_key)
fieldnames = list(sample_row.keys())
for extra in ("suspicious_ai_like_score", "suspicious_notes"):
if extra not in fieldnames:
fieldnames.append(extra)
suspicious_counts = Counter()
with output_path.open("w", newline="", encoding="utf-8") as outfile:
writer = csv.DictWriter(outfile, fieldnames=fieldnames)
writer.writeheader()
for idx, row in enumerate(rows):
for key in row:
if row[key] is None:
row[key] = ""
title = row.get(title_key, "")
desc = row.get(desc_key, "")
score = 0
notes: list[str] = []
t_score = score_title(title)
if t_score:
score += t_score
notes.append("generic/long title")
d_score = score_description(desc)
if d_score:
score += d_score
notes.append("repetitive / template-like description")
extra = author_volume_scores.get(idx, 0)
if extra:
score += extra
notes.append("author appears on many titles")
if score > 3:
score = 3
row["suspicious_ai_like_score"] = str(score)
row["suspicious_notes"] = "; ".join(sorted(set(notes))) if score > 0 else ""
suspicious_counts[score] += 1
writer.writerow(row)
print(f\'Screened {len(rows)} books.")
for s in sorted(suspicious_counts.keys()):
print(f\'Score {s}: {suspicious_counts[s]} titles")
print("Higher scores simply mean: worth a closer human look.")
def main(argv: list[str]) -> int:
if len(argv) != 3:
print("Usage: python suspicious_books_screener.py books.csv books_with_flags.csv")
return 1
input_path = Path(argv[1]).expanduser()
output_path = Path(argv[2]).expanduser()
if not input_path.exists():
print(f\'Input file not found: {input_path}")
return 1
screen_books(input_path, output_path)
print(f\'Flagged file written to: {output_path}")
return 0
if __name__ == "__main__":
raise SystemExit(main(sys.argv))