A Python-based data pipeline that collects product and emissions data from multiple sources and combines them into a cohesive, structured output.
This pipeline:
-
Scrapes product data from Shopify stores (Acure and Saie)
- Product names and prices
-
Extracts emissions data from PDF reports
- Scope 1, 2, and 3 emissions measurements
- Carbon offset information
- Reduction targets
-
Combines all data into structured tables with a clear schema
Commons_TakeHoem/
├── data/
│ ├── raw/ # Input PDF files
│ └── output/ # Generated output files
├── src/
│ ├── __init__.py
│ ├── product_scraper.py # Shopify product scraper with Selenium
name normalization
│ ├── pdf_parser.py # PDF emissions data parser
│ ├── data_schema.py # Data schema definitions
│ └── data_combiner.py # Data combination logic
├── main_pipeline.py # Main pipeline orchestrator
├── requirements.txt # Python dependencies
└── README.md
-
Install Python dependencies:
pip install -r requirements.txt
-
Install OCR dependencies (optional, for scanned PDFs):
# macOS brew install tesseract poppler # Ubuntu/Debian sudo apt-get install tesseract-ocr poppler-utils # Windows - download from: # https://github.com/UB-Mannheim/tesseract/wiki # https://poppler.freedesktop.org/
- OCR is used to extract text from scanned/image-based PDFs
- If not installed, the parser will still work but may miss data from scanned PDFs
-
Set up OpenAI API key:
export OPENAI_API_KEY="your-api-key-here"
- If no API key is set, the normalizer will use manual mappings only
-
Ensure PDF files are in the
data/raw/directory:Acure_2025_ClimateLabel.pdfSaie_2024_ImpactReport.pdf
Run the complete pipeline:
python main_pipeline.pyThis will:
- Scrape products from Acure and Saie Shopify stores
- Parse emissions data from PDFs in
data/raw/ - Combine all data and save to
data/output/as CSV files
# Specify custom directories
python main_pipeline.py --pdf-dir data/raw --output-dir data/output
# Choose output format (csv, excel, parquet, or sql)
python main_pipeline.py --format excel
# Display answers to emissions questions
python main_pipeline.py --answer-questionsThe pipeline generates three main tables:
brand: Brand nameproduct_name: Product nameprice: Product price (USD)product_url: URL to product pageproduct_id: Unique product identifier
brand: Brand nameyear: Year of the emissions reportmeasures_scope1: Boolean flag for Scope 1 measurementsmeasures_scope2: Boolean flag for Scope 2 measurementsmeasures_scope3: Boolean flag for Scope 3 measurementsscope1_value: Scope 1 emissions value in tCO2e (float or empty)scope2_value: Scope 2 emissions value in tCO2e (float or empty)scope3_value: Scope 3 emissions value in tCO2e (float or empty)total_ghg: Total GHG emissions in tCO2e when no clear scope separation (float or empty)total_emissions: Total emissions in tCO2e (float or empty)has_offsets: Boolean flag indicating if brand offsets emissionsoffsets_currency: Currency of offsets (string or None)has_reduction_targets: Boolean flag for reduction targetsreduction_target_percentage: Target reduction percentage (float or empty)reduction_target_year: Target year for reduction (int or empty)reduction_target_base_year: Base year for reduction target (int or empty)carbonneutral_target: Boolean flag indicating if brand has a carbon neutral targetcarbonneutral_year: Year of carbon neutral target (int or empty, must start with 20XX)
One row per product with emissions data joined from the emissions table:
brand_name: Brand nameproduct_name: Name of the productyear: Year of the emissions reportproduct_cost: Product price in USDmeasure_scope1: Scope 1 emissions value in tCO2e, or "NA" if not measuredmeasure_scope2: Scope 2 emissions value in tCO2e, or "NA" if not measuredmeasure_scope3: Scope 3 emissions value in tCO2e, or "NA" if not measuredtotal_ghg: Total GHG emissions in tCO2e when no clear scope separation, or "NA"offset_emissions: Boolean indicating if brand offsets emissions (true/false)carbonneutral_target: Boolean indicating if brand has a carbon neutral target (true/false)carbonneutral_year: Year of carbon neutral target, or "NA" if no target
The pipeline extracts data to answer:
-
Does the brand measure their emissions (scopes 1, 2, and 3)?
- Extracted from PDF text analysis
- Values stored in emissions table
-
Does the brand offset their emissions?
- Searches for "carbon footprint offset" and similar keywords
- Stored in
has_offsetsfield (boolean: true/false)
-
Do they have reduction targets? If so, what are those targets?
- Extracts target percentages, years, and base years
- Stored in reduction target fields
To add a new brand:
-
Add to product scraper:
# In src/product_scraper.py, add to scrape_all_brands(): brands = [ {'url': 'https://newbrand.com', 'name': 'NewBrand'}, # ... existing brands ]
-
Add PDF to data/raw/:
- Place PDF file in
data/raw/directory - Filename should contain brand name (e.g.,
NewBrand_2024_Report.pdf)
- Place PDF file in
-
Run pipeline:
python main_pipeline.py
The pipeline will automatically detect and process the new brand.
- CSV: Separate CSV file for each table
- Excel: Single Excel file with multiple sheets
- Parquet: Efficient columnar format for large datasets
- SQL: SQLite database with separate tables
requests: HTTP requests for web scrapingbeautifulsoup4: HTML parsingpandas: Data manipulation and analysispdfplumber: PDF text extractionsqlalchemy: Database operations (for SQL output)openpyxl: Excel file support
-
Product Scraping:
- Uses Shopify's public API endpoint (
/products.json) which is rate-limited - Uses Selenium to visit individual product pages
- Uses Shopify's public API endpoint (
-
PDF Parsing:
- Uses multiple extraction methods: text extraction, table parsing, OCR (for scanned PDFs), and computer vision API (optional)
- Extracts Scope 1, 2, and 3 emissions values by finding numbers followed by "CO" (with possible letters/subscripts) near scope keywords
- If no clear scope separation exists and only one emissions number is found, stores it in
total_ghg - Extracts carbon neutral targets by looking for "net-zero", "climate pledge", and "carbon neutral" with years starting with 20XX
- Detects offsetting by searching for "carbon footprint offset" and similar keywords (returns bool)
- Automatically detects and extracts data from tables
- Falls back to OCR if text extraction yields insufficient content
- Optional: Uses OpenAI Vision API for better extraction from complex PDFs (requires OpenAI API key)
-
General:
- The pipeline is designed to be re-runnable and will overwrite previous outputs
- Selenium runs in headless mode (no browser window)
No products scraped:
- Check if Shopify stores are accessible
- Verify store URLs are correct
- Some stores may block automated requests
No emissions data extracted:
- Verify PDF files are in the correct directory
- Check if PDFs contain text (not just images)
- Review
raw_text_snippetsin emissions table for manual extraction
Import errors:
- Ensure all dependencies are installed:
pip install -r requirements.txt - Check Python version (3.7+ required)
This project is for demonstration purposes.