Skip to main content

Scrapy Ingest

PyPI version Python versions Downloads Documentation License: MIT Changelog

A Scrapy addon that saves items, requests, logs, and stats to PostgreSQL, MySQL, Elasticsearch, or OpenSearch — with parent_url tracking, failed-request errors, and full job log capture (including print()).

You choose where data goes by setting connection URLs — relational database, Elasticsearch/OpenSearch, or both.

Install

pip install scrapy-ingest

This installs all runtime dependencies, including psycopg2-binary, PyMySQL, and opensearch-py.

Minimal setup (settings.py)

Only the item pipeline is required — requests, logs, stats, parent_url, and error logging are enabled automatically:

ITEM_PIPELINES = {
    "scrapy_ingest.pipelines.DbInsertPipeline": 300,
}

DB_URL = "postgresql://user:password@localhost:5432/database"

Run your spider:

scrapy crawl your_spider

Log level follows Scrapy LOG_LEVEL.

Choose your destination

Set DB_URL (or DB_* fields) for Postgres/MySQL, SEARCH_URL for Elasticsearch/OpenSearch, or both. At least one must be configured.

Mode Settings Result
Database only DB_URL or DB_* SQL only
Elasticsearch / OpenSearch only SEARCH_URL Indexes only
Both DB_URL + SEARCH_URL SQL first, then Elasticsearch/OpenSearch
# Database only
DB_URL = "postgresql://user:password@localhost:5432/database"

# Elasticsearch / OpenSearch only
SEARCH_URL = "http://localhost:9200"

# Both
DB_URL = "postgresql://user:password@localhost:5432/database"
SEARCH_URL = "http://localhost:9200"

When both are configured, SQL is written first, then Elasticsearch/OpenSearch indexes. An indexing failure after a successful SQL write is logged but does not roll back database rows.

Database configuration

PostgreSQL or MySQL — pick one connection style.

Single URL:

DB_URL = "postgresql://user:password@localhost:5432/database"
# DB_URL = "mysql://user:password@localhost:3306/database"

Discrete fields (no URL encoding for special characters in passwords):

DB_TYPE = "postgres"   # or "mysql" / "mariadb"
DB_HOST = "localhost"
DB_PORT = 5432         # MySQL: 3306
DB_USER = "user"
DB_PASSWORD = "password"
DB_NAME = "database"

Tables are created automatically when CREATE_TABLES = True (default).

Elasticsearch / OpenSearch configuration

Elasticsearch and OpenSearch use the same REST bulk API. The package connects via opensearch-py, which works with both.

Minimum:

SEARCH_URL = "http://localhost:9200"

With authentication (optional):

SEARCH_URL = "https://search.example.com:9200"
SEARCH_USER = "elastic"
SEARCH_PASSWORD = "secret"
SEARCH_SSL_VERIFY = True   # set False only for self-signed certs in dev

Index names use the prefix ingest by default:

Index Contents
ingest-jobs Job metadata: status, counts, elapsed time, stats
ingest-job_items Scraped items as JSON documents
ingest-job_requests Requests: url, fingerprint, parent_url, status, error
ingest-job_logs Log lines: level, logger, message, exception

Override the prefix:

SEARCH_INDEX_PREFIX = "my_crawl"
# → my_crawl-jobs, my_crawl-job_items, ...

Table name settings (JOBS_TABLE, ITEMS_TABLE, etc.) also apply as the suffix part of index names.

Indexes are created on first write if they do not exist.

What is stored

Relational database (Postgres / MySQL)

Table Contents
jobs One row per crawl: id, unique job_id string, spider, status, start/finish, counts, items/min, stats
job_items JSON items (crawled_at added). job_id = jobs.id (CASCADE)
job_requests url, parent_url, parent_id, status, response_time_secs, fingerprint, error, success
job_logs time, logger, level, message, exception

Request parent_url is the page that scheduled the request (e.g. sitemap → product). Start URLs are null. The request fingerprint is a SHA1 hash of method + canonical URL for parent lookup.

Elasticsearch / OpenSearch

Same crawl data as JSON documents in the indexes listed above. Documents include the string job_id (not the integer SQL primary key). Elasticsearch/OpenSearch mode does not link parent_id; parent_url is stored on each request document.

Batch flush and crawl summary

Data flushes on batch size, every 10 seconds, and on engine/process stop.

When the spider closes, a crawl summary is printed to stderr (visible even when LOG_LEVEL is ERROR) with job id, spider, database URL, Elasticsearch/OpenSearch URL, table/index names, counts, and elapsed time. Set INGEST_SHOW_SUMMARY = False to hide it.

Useful settings

Setting Default Description
DB_URL / DB_* — Database connection (enables SQL ingest)
SEARCH_URL — Elasticsearch or OpenSearch URL (enables indexing)
SEARCH_USER / SEARCH_PASSWORD — HTTP basic auth for Elasticsearch/OpenSearch (optional)
SEARCH_INDEX_PREFIX ingest Prefix for index names
SEARCH_SSL_VERIFY True Verify HTTPS certificates
CREATE_TABLES True Auto-create SQL tables on startup
INGEST_BATCH_SIZE 50 Flush when this many rows are buffered
INGEST_FLUSH_INTERVAL 10 Periodic flush in seconds
INGEST_SHOW_SUMMARY True Print crawl summary when the spider closes
DB_TYPE postgres postgres / mysql / mariadb for discrete DB_* fields
ITEMS_TABLE, REQUESTS_TABLE, LOGS_TABLE, JOBS_TABLE see defaults Override SQL table / index suffix names
TIMEZONE Asia/Karachi Timezone for created_at
JOB_ID auto Omit to auto-generate (Rs_Spider-178826754-a1b2)

Troubleshooting

  • Password has @ or $ in DB_URL? Encode them: @ → %40, $ → %24. Or use discrete DB_* fields.
  • Yield items from callbacks (not only return inside a generator).
  • Elasticsearch / OpenSearch connection refused? Check SEARCH_URL, firewall, and that the cluster is running. Test with curl http://localhost:9200.
  • HTTPS / self-signed cert? Set SEARCH_SSL_VERIFY = False in development only.

Standalone components

If you only want part of the collection:

# Items only
ITEM_PIPELINES = {"scrapy_ingest.pipelines.ItemsPipeline": 300}

# Requests only (parent_url + errors)
ITEM_PIPELINES = {"scrapy_ingest.pipelines.RequestsPipeline": 300}

# Logs only
EXTENSIONS = {"scrapy_ingest.extensions.LoggingExtension": 500}

Links

License

MIT License. See LICENSE.

Metadata

Release files for scrapy-ingest 1.2.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for scrapy-ingest 1.2.0
File Size Uploaded
scrapy_ingest-1.2.0.tar.gz 33.9 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for scrapy-ingest 1.2.0
File Interpreter ABI Platform
scrapy_ingest-1.2.0-py3-none-any.whl Python 3 none any Details

Total release size: 74.4 kB

Release files / scrapy_ingest-1.2.0.tar.gz

Download URL scrapy_ingest-1.2.0.tar.gz
Size 33.9 kB
Tags Source
SHA-256 checksum
How to use checksums
4282e30dfa33147701a029c0d0dcaf61babdd9ac4cc358d22235969186a7bcca
BLAKE2b-256 checksum
How to use checksums
793140b3490463b1bb7909776ecbe900564a64a3b4352179ca29731329dbda51
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.14.3

Release files / scrapy_ingest-1.2.0-py3-none-any.whl

Download URL scrapy_ingest-1.2.0-py3-none-any.whl
Size 40.5 kB
Tags Python 3
SHA-256 checksum
How to use checksums
2089fe2cb3a6f470bfd1e888643edb2942dacd8c87d15705b73cc3d19234adde
BLAKE2b-256 checksum
How to use checksums
9bd6503249a2110ce227a259aabc99eed467b9d013dfa0ff827812f1ce755921
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.14.3

Release history Release notifications | RSS feed

1.8.0

2 release files

1.7.0

2 release files

1.6.0

2 release files

1.5.0

2 release files

1.4.0

2 release files

1.3.1

2 release files

1.3.0

2 release files

This release

1.2.0 This release

2 release files

1.1.1

2 release files

1.1.0

2 release files

1.0.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page