A Scrapy-based ETL project that extracts book data from books.toscrape.com and loads it into MongoDB with duplicate prevention.
This project follows an ETL flow:
- Extract: scrape title, price, URL, and image URL.
- Transform: normalize URLs and prepare item payloads.
- Load: store in MongoDB through a Scrapy pipeline.
The spider handles pagination automatically and the pipeline avoids duplicate inserts using a SHA256 hash of each book URL.
- Scrapes all pages from books.toscrape.com.
- Extracts title, price, product URL, and product image URL.
- Supports pagination with next-page traversal.
- Stores data in MongoDB.
- Drops duplicate books using deterministic URL hashing.
- Uses environment variables for MongoDB configuration.
Web-Scraper/
|-- .env
|-- README.md
|-- info.txt
|-- steps.txt
|-- task.txt
|-- data_extracted.json
`-- books/
|-- scrapy.cfg
`-- books/
|-- __init__.py
|-- items.py
|-- middlewares.py
|-- pipelines.py
|-- settings.py
`-- spiders/
|-- __init__.py
`-- book.py
- Python 3
- Scrapy
- MongoDB
- PyMongo
- python-dotenv
python -m venv venv
venv\Scripts\activatepip install scrapy pymongo python-dotenvPlace .env in the root folder (same level as README.md), not inside the books folder.
Example:
MONGO_URI="mongodb+srv://username:password@cluster.mongodb.net/?appName=MyApp"
MONGO_DATABASE="books_db"In settings.py, .env is loaded with a file-based path:
BASE_DIR = Path(__file__).resolve().parents[2]
load_dotenv(dotenv_path=BASE_DIR / ".env")This avoids current-working-directory bugs. The settings file is inside books/books/settings.py, so parents[2] resolves to the Web-Scraper root where .env exists.
Mongo settings are read in books/books/settings.py:
MONGO_URI = os.getenv("MONGO_URI")
MONGO_DATABASE = os.getenv("MONGO_DATABASE")If either value is missing, startup fails fast with a clear ValueError.
Pipeline registration:
ITEM_PIPELINES = {
"books.pipelines.MongoPipeline": 300,
}From the books directory:
cd books
scrapy crawl bookSample stored document:
{
"_id": "sha256_hash_of_url",
"title": "Book Title",
"price": "PS51.77",
"url": "https://books.toscrape.com/catalogue/book_1/index.html",
"img": "https://books.toscrape.com/media/cache/...jpg"
}Defines item fields used by the spider and pipeline.
- Starts from books.toscrape.com.
- Extracts item data using CSS selectors.
- Uses response.urljoin() for absolute URLs.
- Follows next-page links.
MongoPipeline lifecycle methods:
- from_crawler(): reads settings into pipeline.
- open_spider(): creates MongoDB client and db handle.
- process_item(): computes hash id, checks duplicates, inserts item.
- close_spider(): closes MongoDB connection.
- article.product_pod: product card container.
- h3 > a::attr(title): book title.
- h3 > a::attr(href): relative product URL.
- .price_color::text: price text.
- .image_container > a > img::attr(src): image path.
- li.next > a::attr(href): pagination link.
- response.url
- response.status
- response.headers
- response.body
- response.text
- Keep .env in the project root, alongside README.md.
- Match the variable names in .env with the names read in settings.py.
- Use file-based path resolution for configuration files so the project works from any terminal location.
- Run the spider from the Scrapy project folder with the virtual environment activated.
- Create virtual environment.
- Activate environment.
- Inspect target in Scrapy shell.
- Define item model.
- Generate spider.
- Implement extraction and pagination.
- Add Mongo pipeline.
- Add duplicate prevention.
- Run and verify data.
Megh Patel 2026