A Python port of the original Ruby wayback-machine-downloader, built for users who prefer a Python-based workflow for downloading archived websites from the Internet Archive Wayback Machine.
This tool helps recover, mirror, and archive old websites from Wayback Machine snapshots. It is useful for digital preservation, website recovery, static site restoration, OSINT research, historical web analysis, and rebuilding sites that are no longer online.
This Python version includes a number of extra fixes, improvements, and quality-of-life changes over the original Ruby implementation.
- Download the latest capture of each file for a target
- Download every timestamped capture with timestamp-prefixed file IDs
- Build a composite snapshot as of a point in time
- Resume interrupted runs using
.cdx.jsonand.downloaded.txt - Rewrite archived links for local browsing with
--local - Discover linked page assets with
--page-requisites - Recursively mirror subdomains with
--recursive-subdomains - Keep the implementation dependency-light and fully testable offline
- Python 3.10 or newer
Install the published package from PyPI:
python -m pip install wayback-machine-downloaderThe PyPI distribution name is wayback-machine-downloader; the import package
remains wayback_downloader.
Install the package in editable mode while developing:
python -m pip install -e .Or run it directly from the repository:
python -m wayback_downloader --helpThe package also exposes a console script after installation:
wayback-machine-downloader --helpDownload the latest version of every file for a site:
python -m wayback_downloader https://example.comList the planned captures without downloading:
python -m wayback_downloader --list https://example.comDownload all historical captures:
python -m wayback_downloader --all-timestamps https://example.comBuild a composite snapshot as of a specific timestamp:
python -m wayback_downloader --snapshot-at 20130101000000 https://example.comRewrite an existing downloaded tree for local browsing:
python -m wayback_downloader --local-only ./websites/example.comBy default, downloads are written under:
./websites/<backup-name>/
<backup-name> is usually the target host. For example:
websites/example.com/
The downloader also uses two state files in the output directory:
.cdx.jsonCached snapshot listing fetched from the CDX API..downloaded.txtLogical file IDs that have been written successfully.
These files let later runs resume instead of starting from scratch. Use
--reset to delete them before a run, or --keep to preserve them after a
successful run.
Download only one exact URL:
python -m wayback_downloader --exact-url https://example.com/index.htmlLimit by timestamp range:
python -m wayback_downloader --from 20060101 --to 20071231 https://example.comFilter URLs:
python -m wayback_downloader --only "/\\.(css|js|png)$/i" https://example.com
python -m wayback_downloader --exclude admin https://example.comDownload pages and immediately queue linked assets:
python -m wayback_downloader --page-requisites --local https://example.comRecursively mirror discovered subdomains:
python -m wayback_downloader --recursive-subdomains --subdomain-depth 2 https://example.comThe downloader supports three selection strategies:
- Latest per logical file Default behavior. For each logical file ID, the newest capture wins.
- All timestamps
Enabled with
--all-timestamps. The timestamp becomes part of the logical file ID so every capture is kept. - Composite snapshot
Enabled with
--snapshot-at. For each file, choose the newest capture at or before the requested timestamp.
Several implementation details are worth knowing because they influence the output tree:
- Host and trailing-slash directory targets are normalized into CDX prefix
queries unless
--exact-urlis used. - Query strings are folded into filenames using a short digest, such as
app__q12ab34cd56ef.css. - Directory-like captures are stored as
.../index.html. - If a file blocks a needed directory later in the run, it is moved to
index.htmlso both captures can coexist.
The --local option rewrites archived absolute URLs into local relative
references after files are saved. It handles:
- Wayback-hosted rewritten URLs
- direct absolute HTTP/HTTPS links
- HTML attributes such as
href,src, andaction - CSS
url(...)references - JavaScript string literals containing absolute URLs
--local-only performs only the rewrite phase on an existing directory and
does not contact the archive.
This repository includes GitHub Actions workflows for:
- CI testing on push and pull request across Python 3.10-3.13, plus a Windows smoke job
- building
sdistandwheelartifacts on every CI run - manual publishing to TestPyPI
- publishing to PyPI when a GitHub Release is published
To use Trusted Publishing with the included workflows:
- Create GitHub environments named
testpypiandpypi. - In TestPyPI, register a trusted publisher for this repository using workflow file
testpypi.ymland environmenttestpypi. - In PyPI, register a trusted publisher for this repository using workflow file
release.ymland environmentpypi.
After that:
- run the
Publish to TestPyPIworkflow manually from the Actions tab to test uploads - publish a GitHub Release to trigger the
Publish to PyPIworkflow
Build and validate the distribution archives:
python -m pip install --upgrade build twine
python -m build
python -m twine check dist/*Upload to TestPyPI first:
python -m twine upload --repository testpypi dist/*Upload to PyPI:
python -m twine upload dist/*Use an API token when Twine prompts for credentials:
- username:
__token__ - password: your
pypi-...token
Run the test suite:
python -B -m unittest discover -s tests -t .Compile modules as a quick import sanity check:
python -m compileall wayback_downloader testsThe tests use fake transports and temporary directories, so they do not depend
on live access to web.archive.org.