Download XKCD Comics using Python

XKCD is a popular webcomic that features humor, science, and geek culture. The comic is famous for its witty jokes as well as references to culture and science. We can download comics using the XKCD API with Python's requests and Pillow libraries. In this article, we will download XKCD comics using Python.

Understanding XKCD API

XKCD provides an open API that allows developers to access comics programmatically. To use the API, we send an HTTP GET request to the URL https://xkcd.com/{comic_id}/info.0.json. The request returns a JSON object containing information about the specified XKCD comic, including the image URL.

Installing Required Libraries

To download XKCD comics using Python, you need to install the requests and Pillow libraries. The requests library allows us to make HTTP requests to the XKCD API, and Pillow enables image manipulation and saving.

pip install requests
pip install Pillow

Downloading a Single XKCD Comic

First, let's create a function to download a specific comic by its ID ?

import requests
import io
from PIL import Image

def download_comic(comic_id):
    # Construct the URL for the XKCD API
    url = f'https://xkcd.com/{comic_id}/info.0.json'
    
    # Make an HTTP GET request to the XKCD API
    response = requests.get(url)
    
    # Parse the JSON response
    data = response.json()
    
    # Extract the image URL from the JSON data
    image_url = data['img']
    
    # Make an HTTP GET request to the image URL
    image_response = requests.get(image_url)
    
    # Open the image using Pillow
    image = Image.open(io.BytesIO(image_response.content))
    
    return image

# Download and save a single comic
comic = download_comic(353)
comic.save('xkcd_comic_353.png')
print("Comic 353 downloaded successfully!")
Comic 353 downloaded successfully!

Downloading Multiple XKCD Comics

Now let's create a function to download a range of comics ?

import requests
import io
from PIL import Image

def download_comic(comic_id):
    url = f'https://xkcd.com/{comic_id}/info.0.json'
    response = requests.get(url)
    data = response.json()
    image_url = data['img']
    image_response = requests.get(image_url)
    image = Image.open(io.BytesIO(image_response.content))
    return image

def download_comics_range(start_id, end_id):
    for comic_id in range(start_id, end_id + 1):
        try:
            # Download the comic
            image = download_comic(comic_id)
            
            # Save the image to a file
            filename = f'xkcd_{comic_id}.png'
            image.save(filename)
            print(f'Saved {filename}')
        except Exception as e:
            print(f'Error downloading comic {comic_id}: {e}')

# Download comics 1 through 5
download_comics_range(1, 5)
Saved xkcd_1.png
Saved xkcd_2.png
Saved xkcd_3.png
Saved xkcd_4.png
Saved xkcd_5.png

Getting Comic Information

The API also provides metadata about each comic. Let's extract and display this information ?

import requests

def get_comic_info(comic_id):
    url = f'https://xkcd.com/{comic_id}/info.0.json'
    response = requests.get(url)
    data = response.json()
    
    print(f"Comic #{data['num']}")
    print(f"Title: {data['title']}")
    print(f"Date: {data['month']}/{data['day']}/{data['year']}")
    print(f"Alt text: {data['alt']}")
    print(f"Image URL: {data['img']}")

# Get information for comic 353
get_comic_info(353)
Comic #353
Title: Python
Date: 12/5/2007
Alt text: I wrote 20 short programs in Python yesterday.  It was wonderful.  Perl, I'm leaving you.
Image URL: https://imgs.xkcd.com/comics/python.png

Error Handling and Best Practices

It's important to handle errors gracefully when downloading comics. Some comic IDs might not exist, or network issues could occur ?

import requests
import io
from PIL import Image
import time

def download_comic_safe(comic_id):
    try:
        url = f'https://xkcd.com/{comic_id}/info.0.json'
        response = requests.get(url, timeout=10)
        
        if response.status_code != 200:
            print(f"Comic {comic_id} not found (status: {response.status_code})")
            return None
            
        data = response.json()
        image_url = data['img']
        
        image_response = requests.get(image_url, timeout=10)
        image = Image.open(io.BytesIO(image_response.content))
        
        return image, data
        
    except requests.exceptions.RequestException as e:
        print(f"Network error for comic {comic_id}: {e}")
        return None
    except Exception as e:
        print(f"Error processing comic {comic_id}: {e}")
        return None

def download_comics_with_delay(start_id, end_id, delay=1):
    for comic_id in range(start_id, end_id + 1):
        result = download_comic_safe(comic_id)
        
        if result:
            image, data = result
            filename = f"xkcd_{comic_id}_{data['title'].replace(' ', '_')}.png"
            image.save(filename)
            print(f"Downloaded: {data['title']} ({filename})")
        
        # Be respectful to the server
        time.sleep(delay)

# Download comics 1-3 with 1 second delay between requests
download_comics_with_delay(1, 3)
Downloaded: Barrel - Part 1 (xkcd_1_Barrel_-_Part_1.png)
Downloaded: Petit Trees (sketch) (xkcd_2_Petit_Trees_(sketch).png)
Downloaded: Island (sketch) (xkcd_3_Island_(sketch).png)

Conclusion

Using Python's requests and Pillow libraries, we can easily download XKCD comics through their API. Always include proper error handling and add delays between requests to be respectful to the server. This foundation can be extended to build more sophisticated XKCD-related applications.

Updated on: 2026-03-27T07:14:04+05:30

842 Views

Kickstart Your Career

Get certified by completing the course

Get Started
Advertisements