Of course! Creating an Image Captcha in Python is a classic and useful task. I'll guide you through it step-by-step, from a simple, manual approach to using a powerful, specialized library.

What is a CAPTCHA?
A CAPTCHA (Completely Automated Public Turing test to tell Computers and Humans Apart) is a type of challenge-response test used in computing to determine whether the user is human. We'll focus on the image-based type.
Method 1: The Manual Approach with Pillow
This method is great for understanding the underlying principles. We'll use the Pillow library (a fork of PIL) to generate images, add text, and draw noise.
Step 1: Install Pillow
pip install Pillow
Step 2: The Python Code
This script will generate a simple image with a 4-character code, some random lines, and dots to make it harder for bots to read.
import random
import string
from PIL import Image, ImageDraw, ImageFont
def generate_captcha_text(length=6):
"""Generates a random string of uppercase letters and digits."""
characters = string.ascii_uppercase + string.digits
return ''.join(random.choice(characters) for _ in range(length))
def create_captcha_image(text, width=200, height=80, font_size=36):
"""Creates a CAPTCHA image with the given text."""
# Create a new image with a white background
image = Image.new('RGB', (width, height), color='white')
draw = ImageDraw.Draw(image)
# Try to load a font, fall back to default if not found
try:
# You might need to provide a path to a .ttf file
# For example, on Linux: '/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf'
font = ImageFont.truetype("arial.ttf", font_size)
except IOError:
font = ImageFont.load_default()
# Calculate text position to center it
text_width, text_height = draw.textsize(text, font=font)
position = ((width - text_width) / 2, (height - text_height) / 2)
# Draw the text
draw.text(position, text, font=font, fill='black')
# Add some random lines to the image
for _ in range(5):
x1 = random.randint(0, width)
y1 = random.randint(0, height)
x2 = random.randint(0, width)
y2 = random.randint(0, height)
draw.line([(x1, y1), (x2, y2)], fill='gray', width=1)
# Add some random noise (dots)
for _ in range(100):
x = random.randint(0, width)
y = random.randint(0, height)
draw.point((x, y), fill='gray')
return image
# --- Main execution ---
if __name__ == "__main__":
captcha_text = generate_captcha_text()
print(f"Generated CAPTCHA Text: {captcha_text}") # For demonstration only
captcha_image = create_captcha_image(captcha_text)
# Save the image to a file
captcha_image.save("captcha.png")
print("CAPTCHA image saved as captcha.png")
# To display the image (requires a GUI environment)
captcha_image.show()
How to Use It:

- Run the script. It will print the generated text to the console (for your reference) and save an image named
captcha.png. - You would then display
captcha.pngto the user on a website or in an application. - When the user enters the text, you compare their input (case-insensitively) with the
captcha_textyou stored in your server's session or database.
Method 2: The Professional Approach with captcha Library
Manually creating a robust, secure, and visually appealing CAPTCHA is difficult. The best practice is to use a dedicated library. The captcha library is excellent for this.
Step 1: Install the captcha library
pip install captcha
Step 2: The Python Code
This library handles all the complexities: font generation, distortion, noise, and color variations.
from captcha.image import ImageCaptcha
import random
import string
def generate_captcha_text(length=6):
"""Generates a random string of uppercase letters and digits."""
characters = string.ascii_uppercase + string.digits
return ''.join(random.choice(characters) for _ in range(length))
def create_captcha_image(text, width=280, height=90):
"""Creates a CAPTCHA image using the captcha library."""
image = ImageCaptcha(width=width, height=height)
# Generate the image data
data = image.generate(text)
# Save the image to a file
image.write(text, "captcha_library.png")
return "captcha_library.png"
# --- Main execution ---
if __name__ == "__main__":
captcha_text = generate_captcha_text()
print(f"Generated CAPTCHA Text: {captcha_text}") # For demonstration only
image_path = create_captcha_image(captcha_text)
print(f"CAPTCHA image saved as {image_path}")
# To display the image (requires a GUI environment)
from PIL import Image
Image.open(image_path).show()
Why is this better?
- Security: It uses multiple fonts, applies wave and curve distortions, and adds various types of noise (lines, dots, arcs) that are much harder for bots to OCR (Optical Character Recognition).
- Ease of Use: The API is incredibly simple. You just provide the text, and it does the rest.
- Customization: You can easily change colors, fonts, and noise levels.
Method 3: Integrating with a Web Framework (Flask Example)
A CAPTCHA is useless without a web interface to display it and a backend to verify it. Here’s a simple example using Flask.

Step 1: Install Flask
pip install Flask
Step 2: Project Structure
/flask_captcha_app
|-- app.py
|-- /templates
| |-- index.html
Step 3: The Code
app.py (Backend)
from flask import Flask, render_template, request, session, redirect, url_for
from captcha.image import ImageCaptcha
import random
import string
app = Flask(__name__)
# A secret key is needed for session management
app.secret_key = b'_5#y2L"F4Q8z\n\xec]/'
def generate_captcha_text(length=6):
characters = string.ascii_uppercase + string.digits
return ''.join(random.choice(characters) for _ in range(length))
@app.route('/', methods=['GET', 'POST'])
def index():
if request.method == 'POST':
# Get user input
user_input = request.form.get('captcha_input', '').upper()
# Get the correct CAPTCHA from the session
correct_captcha = session.get('captcha_text')
if user_input == correct_captcha:
return "<h1 style='color:green;'>Success! You are human.</h1>"
else:
return "<h1 style='color:red;'>Failure. Incorrect CAPTCHA.</h1><br><a href='/'>Try Again</a>"
# For a GET request, generate a new CAPTCHA
captcha_text = generate_captcha_text()
session['captcha_text'] = captcha_text # Store the correct answer in the session
# Generate the image
image = ImageCaptcha(width=280, height=90)
data = image.generate(captcha_text)
# Pass the image data (as a base64 string) to the template
# This avoids saving the image to a file
import io
import base64
buffered = io.BytesIO()
image.write(buffered)
img_str = base64.b64encode(buffered.getvalue()).decode('utf-8')
return render_template('index.html', captcha_image=img_str)
if __name__ == '__main__':
app.run(debug=True)
templates/index.html (Frontend)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">Flask CAPTCHA Example</title>
<style>
body { font-family: sans-serif; text-align: center; margin-top: 50px; }
.captcha-container { margin: 20px; }
img { border: 1px solid #ccc; }
</style>
</head>
<body>
<h1>Verify You Are Human</h1>
<div class="captcha-container">
<!-- The image is embedded directly in the HTML -->
<img src="data:image/png;base64,{{ captcha_image }}" alt="CAPTCHA Image">
</div>
<form method="POST">
<input type="text" name="captcha_input" required placeholder="Enter the code">
<button type="submit">Submit</button>
</form>
</body>
</html>
Step 4: Run the Flask App
- Navigate to the
flask_captcha_appdirectory in your terminal. - Run
python app.py. - Open your browser and go to
http://127.0.0.1:5000.
You will see a form with a dynamically generated CAPTCHA image. When you submit the form, the Flask backend will check your input against the value stored in the session.
Summary and Best Practices
| Feature | Manual (Pillow) | captcha Library |
Web Framework (Flask) |
|---|---|---|---|
| Ease of Use | Medium | Very Easy | Easy (with setup) |
| Security | Low (easy for bots) | High (designed for security) | High (depends on library) |
| Customization | Full control | Good control | Depends on library |
| Best For | Learning the basics, simple projects | Most applications, quick implementation | Full web applications |
Key Security Considerations:
- Never store the CAPTCHA answer in a cookie. Always use a server-side session.
- Expire the CAPTCHA. After a successful or failed attempt, or after a short time (e.g., 5 minutes), the CAPTCHA should be invalidated and a new one generated.
- Rate Limiting: To prevent brute-force attacks, limit the number of login attempts from a single IP address.
- Use HTTPS: In a production environment, always use HTTPS to protect the data in transit.
