Skip to main content

Convert any HTML tutorial page into MCQ questions using AI

Project description

html2mcq

Convert any HTML tutorial page, YouTube video, or PDF into MCQ questions using AI.

PyPI version Python 3.9+ License: MIT Tests


Install

pip install html2mcq

That's it. Everything is included — HTML extraction, YouTube transcripts, PDF support, all AI providers.

For scanned PDFs (OCR + complex layouts), optionally install Docling:

pip install html2mcq docling

Quick Start

from html2mcq import MCQGenerator

gen = MCQGenerator(
    api_key="sk-or-v1-...",
    provider="openrouter",
    model="meta-llama/llama-3.3-70b-instruct:free",
)

# From an HTML tutorial page
mcq = gen.from_url("https://docs.python.org/3/tutorial/", n=15)

# From a YouTube video (transcript fetched automatically)
mcq = gen.from_video_url("https://www.youtube.com/watch?v=VXU4LSAQDSc", n=10)

# From a PDF URL
mcq = gen.from_pdf_url("https://example.com/tutorial.pdf", n=10)

# From a local PDF file
mcq = gen.from_pdf_path("/path/to/notes.pdf", n=10)

# From raw HTML
mcq = gen.from_html(html_string, n=10)

print(mcq.to_json())
print(mcq.to_pretty_str())

What it supports

Input How
HTML tutorial page URL from_url(url) — extracts text, code, tables, images, video links, PDF links
YouTube video URL from_video_url(url) or from_url(url) — transcript fetched automatically
PDF via URL from_pdf_url(url) — downloaded and extracted
Local PDF file from_pdf_path(path)
Raw HTML string from_html(html)
Pre-built blocks from_blocks(blocks)

Output Schema

{
  "total_exam_time": 20,
  "questions": [
    {
      "question_html": "Which of these are non-mutating array methods?",
      "options": ["push()", "map()", "filter()", "pop()"],
      "answers": [1, 2],
      "multi": true,
      "marks": 1,
      "negative_marks": 0,
      "difficulty": "medium",
      "explaination": "map() and filter() return new arrays without modifying the original."
    }
  ]
}
  • answers is always an array — supports multiple correct answers
  • multi: truenegative_marks: 0.0, multi: falsenegative_marks: 0.25
  • marks always 1, difficulty roughly 1/3 each easy/medium/hard

AI Providers

# Anthropic Claude
gen = MCQGenerator(api_key="sk-ant-...", provider="anthropic")

# OpenAI
gen = MCQGenerator(api_key="sk-...", provider="openai", model="gpt-4o")

# OpenRouter — 100+ models, free Llama available
gen = MCQGenerator(api_key="sk-or-...", provider="openrouter",
                   model="meta-llama/llama-3.3-70b-instruct:free")

# API key from environment variable
# Set ANTHROPIC_API_KEY / OPENAI_API_KEY / OPENROUTER_API_KEY
gen = MCQGenerator(provider="anthropic")

Custom Instructions

Override the AI's default behaviour without breaking the fixed rules (JSON schema, marks, options count).

# Apply to every call this generator makes
gen = MCQGenerator(
    provider="openrouter",
    custom_instructions=(
        "Make answers very close and confusing. "
        "Only people with sharp attention should get 100% marks."
    )
)

# Or per individual call
mcq = gen.from_url(
    "https://example.com/tutorial",
    n=10,
    custom_instructions="All questions must be code-based. No theory questions."
)

How the prompt is structured:

SYSTEM PROMPT  (fixed — always sent, never modified)
  Rules: 4 options, JSON schema, marks/negative_marks, no hallucination...

USER PROMPT
  [extracted content: text / code / tables / transcripts / PDFs]

  Generate exactly N questions. Mix difficulties...

  --- CUSTOM INSTRUCTIONS (highest priority) ---
  Your custom text here
  --- END CUSTOM INSTRUCTIONS ---

  Return ONLY the JSON array.

PDF Backends

By default, PyMuPDF handles PDFs instantly. For scanned or complex PDFs, Docling is used as an automatic fallback.

# Default — PyMuPDF, fast, works for most digital PDFs
gen = MCQGenerator(provider="anthropic")

# With Docling Serve on your own server (GPU recommended)
# Run once: docker run --gpus all -p 5001:5001 quay.io/docling/docling-serve
gen = MCQGenerator(
    provider="anthropic",
    docling_api_url="http://your-server:5001"
)

Auto-fallback:

PyMuPDF extracts text
  ↓ fewer than 100 chars? (scanned PDF)
  → retry with Docling Serve  (if docling_api_url set)
  → or retry with Docling Local  (if pip install html2mcq docling)

Desktop GUI

python html2mcq_gui.py

5 input tabs: Web URL · YouTube · PDF URL · Local PDF · Raw HTML

Options: N questions, difficulty mix, focus topics, custom instructions, PDF backend selector.

Output: syntax-highlighted pretty view or JSON, copy to clipboard, save as JSON.


CLI

# Basic
html2mcq https://example.com/tutorial --n 20

# All options
html2mcq https://example.com/tutorial \
    --n 20 \
    --provider openrouter \
    --model meta-llama/llama-3.3-70b-instruct:free \
    --difficulty "40% easy, 40% medium, 20% hard" \
    --topics "variables" "functions" \
    --instructions "Make answers very close and confusing" \
    --output quiz.json \
    --format json

# From a local HTML file
html2mcq --html page.html --n 5

Live Tests

export OPENROUTER_API_KEY="sk-or-v1-..."
python test_live.py                  # all input types
python test_live.py --test html      # HTML only
python test_live.py --test video     # YouTube only
python test_live.py --test pdf_file  # local PDF only
python test_live.py --n 5

API Reference

MCQGenerator

MCQGenerator(
    api_key=None,               # or set env var ANTHROPIC/OPENAI/OPENROUTER_API_KEY
    provider="anthropic",       # "anthropic" | "openai" | "openrouter"
    model="",                   # default per provider if not set
    batch_size=10,              # questions per API call; large N is auto-batched
    max_tokens=4096,
    pdf_backend="pymupdf",      # "pymupdf" | "docling_local" | "docling_serve"
    docling_api_url="",         # Docling Serve URL e.g. "http://your-server:5001"
    docling_ocr=True,
    transcript_languages=["en"],
    custom_instructions="",     # global custom instructions for every call
)
Method Description
from_url(url, n, ...) HTML page; auto-detects YouTube links
from_html(html, n, base_url, ...) Raw HTML string
from_video_url(url, n, video_title, ...) YouTube video via transcript
from_pdf_url(url, n, pdf_title, ...) PDF via URL
from_pdf_path(path, n, pdf_title, ...) Local PDF file
from_blocks(blocks, n, ...) Pre-extracted ContentBlock list

All methods accept custom_instructions, difficulty_mix, and focus_topics.

MCQSet

Property / Method Description
.questions List[MCQQuestion]
.total_exam_time Minutes — auto-calculated as n × 2
.to_json() Exam-ready JSON (total_exam_time + questions only)
.to_pretty_str() Human-readable output
.filter_by_difficulty(d) Returns filtered MCQSet for "easy"/"medium"/"hard"

Project Structure

html2mcq/
├── html2mcq/
│   ├── __init__.py        # Public exports
│   ├── extractor.py       # HTML parser
│   ├── video.py           # YouTube transcript extractor
│   ├── pdf.py             # PDF extractor (PyMuPDF + Docling)
│   ├── generator.py       # MCQGenerator — main API
│   ├── models.py          # ContentBlock, MCQQuestion, MCQSet
│   ├── prompts.py         # Fixed system prompt + dynamic user prompt
│   └── cli.py             # CLI entry point
├── tests/
│   └── test_html2mcq.py   # 68 unit tests (fully mocked, no API key needed)
├── html2mcq_gui.py        # Tkinter desktop GUI
├── test_live.py           # Live integration tests
├── examples/
│   └── basic_usage.py
├── pyproject.toml
├── README.md
└── CHANGELOG.md

License

MIT © 2025 html2mcq contributors

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

html2mcq-1.3.0.tar.gz (36.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

html2mcq-1.3.0-py3-none-any.whl (28.7 kB view details)

Uploaded Python 3

File details

Details for the file html2mcq-1.3.0.tar.gz.

File metadata

  • Download URL: html2mcq-1.3.0.tar.gz
  • Upload date:
  • Size: 36.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.11.6

File hashes

Hashes for html2mcq-1.3.0.tar.gz
Algorithm Hash digest
SHA256 7c3fdb3cf782b11ab278bc247b122e38f719dde4b0fe0ec8b8152fb82e5d169f
MD5 8bbf7040c23de71664518995b5d991a1
BLAKE2b-256 e4e6e1bc564e9d469d921661af7c017913d23c9c9e1e038cfe1cd63ba1e04df3

See more details on using hashes here.

File details

Details for the file html2mcq-1.3.0-py3-none-any.whl.

File metadata

  • Download URL: html2mcq-1.3.0-py3-none-any.whl
  • Upload date:
  • Size: 28.7 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.11.6

File hashes

Hashes for html2mcq-1.3.0-py3-none-any.whl
Algorithm Hash digest
SHA256 8e3cd6a0064b0d6935c64dec0879c4c31302aab16b93b4b034dedc33770ef5b8
MD5 1e7404fd7bc0dd8d8a241acc6bf89a94
BLAKE2b-256 19f1286e7e98f377e9c5c2e4a20f84c59b9ef92e2b303678962db2a38c9dc095

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page