# Project export: Green Growth

This document was generated by HackStack to give an AI agent context about a hackathon project. Sections are labeled with their provenance; content marked as truncated was cut to keep this document small.

## Project metadata

- Hackathon: TreeHacks 2025
- Tagline: What if we could predict the next big thing in sustainability before it happens?
- Devpost: https://devpost.com/software/green-growth
- GitHub: https://github.com/night-pianist/treehacks
- Demo: https://treehacks2025-rtxkyq.flutterflow.app/
- Video: https://www.youtube.com/embed/puW99fCGE64?enablejsapi=1&hl=en_US&rel=0&start=&version=3&wmode=transparent
- Team: 1 GitHub contributor(s) — Khiet (3 commits)

## Devpost submission (written by the team)

### Overview

Green growth: fostering economic growth and development while ensuring that natural assets continue to provide the resources and environmental services on which our well-being relies ~ Organization for Economic Cooperation and Development (OECD)

### Inspiration

Sustainability activists advocate for a revolutionary shift from fossil fuel to clean energy, and green jobs could represent around 14% of total U.S. jobs by 2030…but is our workforce ready? Acknowledging that marginalized communities are disproportionately affected by both climate change and economic change, our project was inspired by the idea of a just transition, which the International Labour Organization defines as the “greening the economy in a way that is as fair and inclusive as possible to everyone concerned, creating decent work opportunities and leaving no one behind.” Further research brought to light the hardships faced by many workers in the fossil fuel-dependent industries. In fact, roughly 1.7 million workers in the US are projected to lose their jobs. Furthermore, around 20% of fuel sector unemployment consists of fossil fuel jobs in 2023, and this number is projected to grow as the US rapidly moves towards renewable energy sources. We aimed to create a platform that opens new opportunities to job seekers and policymakers hoping to get ahead of the curve by embracing clean energy.

### What it does

Green Growth predicts the next big thing in sustainability to support a just transition. Our platform analyzes a novel dataset of clean energy sector trends to: Predict clean energy growth (%) within a county specified by the user. Predict what sector of clean energy will grow the most within the county. Suggest the top 3 roles to pursue within the clean energy sector. Provide recommended skill sets associated with those jobs. Green Growth helps workers prepare for sustainable careers and adapt to the evolving job market without the risk of displacement. Additionally, it provides data-driven projections that can guide policymakers in making informed investment decisions, ultimately fostering a cleaner, more sustainable world.

### How we built it

Research & Prototyping Social impact assessment through economic, policy, and community lenses By considering macroeconomic concepts like unemployment (frictional, structural, and natural) and the factors of production, we argue that sustainable initiatives support our economy by preserving natural resources to foster our individual and collective well-being in the present and future. By incorporating historical and current trends to produce data-driven insights, we hope to inform policymakers who have the power to invest in their county and create beneficial programs for their constituents. By displaying in-demand jobs and skills, we aim to empower community members, particularly job seekers hoping to transition to the clean energy sector, with previously inaccessible information. By considering macroeconomic concepts like unemployment (frictional, structural, and natural) and the factors of production, we argue that sustainable initiatives support our economy by preserving natural resources to foster our individual and collective well-being in the present and future. By incorporating historical and current trends to produce data-driven insights, we hope to inform policymakers who have the power to invest in their county and create beneficial programs for their constituents. By displaying in-demand jobs and skills, we aim to empower community members, particularly job seekers hoping to transition to the clean energy sector, with previously inaccessible information. Expert interviews with an Environmental Engineering PhD candidate at Stanford, a youth climate activist at Wellesley, and Stanford Ecopreneurship mentors In initial interviews, we sought to understand which communities are most impacted by climate change and what gaps exist in green tech. We also held brief brainstorming sessions to ensure we addressed a genuine pain point in the field. As a result, we chose to develop a project at the intersection of AI, sustainability, and labor. After fleshing out our idea in its entirety, we conducted further outreach to evaluate its potential for real-world impact. Based on stakeholder feedback, we expanded our dataset to include relevant investments, refined our product to focus primarily on job seekers and policymakers, and pondered how our product might be integrated into society in the long-term. In initial interviews, we sought to understand which communities are most impacted by climate change and what gaps exist in green tech. We also held brief brainstorming sessions to ensure we addressed a genuine pain point in the field. As a result, we chose to develop a project at the intersection of AI, sustainability, and labor. After fleshing out our idea in its entirety, we conducted further outreach to evaluate its potential for real-world impact. Based on stakeholder feedback, we expanded our dataset to include relevant investments, refined our product to focus primarily on job seekers and policymakers, and pondered how our product might be integrated into society in the long-term. Tech Stack Scrapy to scrape websites, specifically green jobs boards: Green Jobs Board Green Jobs Network NEWIEE Jobs Board NESEA Jobs Board Green Jobs Board Green Jobs Network NEWIEE Jobs Board NESEA Jobs Board Google Sheets, Google Colab, pandas, NumPy to view and clean CSV files from credible sources: U.S. Energy & Employment Jobs Report, annual reports about energy jobs from the U.S. Department of Energy Clean Investment Monitor, a joint effort of Rhodium Group and MIT's Center for Energy and Environmental Policy Research (CEEPR) U.S. Energy & Employment Jobs Report, annual reports about energy jobs from the U.S. Department of Energy Clean Investment Monitor, a joint effort of Rhodium Group and MIT's Center for Energy and Environmental Policy Research (CEEPR) OpenAI Assistants API to parse, analyze, and store our dataset and output accurate predictions. With the provided credits, we were able to take advantage of the Code Interpreter to process files with diverse data and formatting, and the GPT-4o model to optimize multi-step reasoning. Due to several file types and a complex use case, these tools proved to be pivotal to our product. FlutterFlow to build our website UI and connect backend components. Using the AI-powered page generator, we were able to save a significant amount of time that would have been spent on designing and coding the frontend from scratch. Windsurf by Codeium to help scrape websites. We leveraged an AI agent that integrates seamlessly with code and debugging processes. Perplexity to power our search and help us find relevant datasets and research articles.

### Challenges we ran into

Lack of an aggregate dataset: After coming up with our idea, we spent hours looking for a labeled dataset that could be used to train an AI model. Since we couldn’t find any existing aggregate datasets, we pivoted to creating our own. By compiling, cleaning, and scraping data, we produced a novel dataset to predict future growth in clean energy jobs. Limited, inaccessible datasets related to clean energy employment: Along the lines of the previous challenge, finding relevant datasets at all proved to be time-consuming since many were hidden behind confusing web pages and out-of-date API’s. Our time spent navigating this means any user of our platform has a more streamlined experience. Project scoping: When ideating Green Growth, we needed to decide on a target audience, but we were presented with many choices: laborers, businesses, policymakers, investors, the general public. We decided to narrow our scope and focus our efforts on helping job seekers and policymakers. Web scraping: As this was our first time web scraping, we iteratively learned, problem-solved, and adapted to scraping specific fields from various website formats. One challenge was learning how to scrape data across multiple levels of links and webpages. Throughout the process, we refined our approach to merge data from multiple sources into a single dataset. Connecting the OpenAI Assistants API to FlutterFlow: We spent a majority of our debugging process connecting the Assistant to FlutterFlow. We first ran into the issue of the call to OpenAI not working as we were not getting any responses in the debug area. After carefully going through tutorials and inspecting our elements, we resolved that issue but ran into another problem. This time, we received the desired results but they were not getting displayed on the FlutterFlow UI. We resolved this by changing the conditions of the components.

### Accomplishments we're proud of

Learning several new technologies: From FlutterFlow to Scrapy to Windsurf, we leveraged developer tools that we hadn’t used before that helped make our process much more efficient. Creating our own datasets: We carefully investigated datasets from various sources—including the Bureau of Labor Statistics, the U.S. Energy & Employment Jobs Report, and Clean Investment Monitor—before identifying datasets suitable for the LLM. Furthermore, we researched multiple green job boards before scraping those pages for the most relevant data.

### What we learned

How to scrape data from inconsistently formatted web pages using a Python framework How to deal with the (very real-world) issue of not having enough, or the right kind, of data How to approach a complex problem with a multidisciplinary perspective, leveraging our team’s diverse backgrounds and strengths Insights into sustainability through research, as well as speaking to mentors and professionals in the field. We came in without much prior knowledge, and came out with a newfound passion!

### What's next

Since proper datasets weren’t available, we used an LLM to combine different datasets for predictions. In the future, we aim to build our own AI model with an ideal dataset. Furthermore, we’d like to improve our predictions by incorporating evaluation benchmarks to assess the accuracy of our model. This would allow us to make our predictions more reliable and display the predictions in simple data visualizations. We are currently using U.S. data from 2016 onwards to make predictions. Given more time, we’d like to search for more green jobs and datasets dating from further back to get a clearer image of sustainable growth. Many countries in Europe have been successful with improving sustainability. As a way to expand Green Growth’s impact, we’d like to analyze sustainable growth trends in Europe to provide more insight on how the U.S. can become more sustainable. After gaining more experience with FlutterFlow, we’d like to enhance our platform’s UI and revamp its design, even adding animations and interactive components. We also acknowledge that a significant barrier to ongoing just transitions is the fact that green jobs are not available in the same regions as fossil fuel jobs. Due to this structural and regional issue, we hope to look into partnerships with companies that provide aid regarding relocation services.

## README (from the GitHub repository)

Link to our FlutterFlow code: https://drive.google.com/file/d/1SBJgcaQgJSaKaYPHyh2NF0ixCcfZsLlI/view?usp=sharing 

Due to pushing errors, our FlutterFlow folder is too large to be pushed to our repository, so linked above is a zipfile of our FlutterFlow code.


## Detected evidence (automated analysis)

Indexed codebase: 19 recognized source files, 26 KB.
- Python (language) — detected in the code
- OpenAI (technology) — claimed on Devpost, not found in the code

## Codebase structure (from repository index)

### Files (26 of 26)

```
GreenGrowth/.DS_Store
GreenGrowth/GreenGrowth/__init__.py
GreenGrowth/GreenGrowth/items.py
GreenGrowth/GreenGrowth/middlewares.py
GreenGrowth/GreenGrowth/pipelines.py
GreenGrowth/GreenGrowth/settings.py
GreenGrowth/GreenGrowth/spiders/__init__.py
GreenGrowth/GreenGrowth/spiders/greenjobs_board_spider.py
GreenGrowth/GreenGrowth/spiders/greenjobs_states.py
GreenGrowth/GreenGrowth/spiders/run_spiders.py
GreenGrowth/greenjobs.json
GreenGrowth/greenjobsboard.json
GreenGrowth/scrapy.cfg
greenjobs/greenjobs.json
greenjobs/greenjobs/__init__.py
greenjobs/greenjobs/items.py
greenjobs/greenjobs/middlewares.py
greenjobs/greenjobs/pipelines.py
greenjobs/greenjobs/settings.py
greenjobs/greenjobs/spiders/__init__.py
greenjobs/greenjobs/spiders/greenjobs_board_spider.py
greenjobs/greenjobs/spiders/greenjobs_states.py
greenjobs/greenjobs/spiders/run_spiders.py
greenjobs/greenjobsboard.json
greenjobs/scrapy.cfg
README.md
```

### Dependencies

No dependency index available.

### Recent commits (newest first)

- Create README.md
- update github with flutterflow
- update folder names and flutterflow
- Merge pull request #1 from night-pianist/khiet/scrapy
- successfully scrap data from greenjobboards and greenjobstates to JSON files
- scrap data from greenjobboards and greenjobstates
- scrap greenjobsboard successfully but not states page yet
- Initial commit

## Key source files (fetched from GitHub, selected and truncated for size)

### GreenGrowth/GreenGrowth/items.py

```python
# Define here the models for your scraped items
#
# See documentation in:
# https://docs.scrapy.org/en/latest/topics/items.html

import scrapy


class GreenjobsItem(scrapy.Item):
    # define the fields for your item here like:
    # name = scrapy.Field()
    pass

```

### greenjobs/greenjobs/items.py

```python
# Define here the models for your scraped items
#
# See documentation in:
# https://docs.scrapy.org/en/latest/topics/items.html

import scrapy


class GreenjobsItem(scrapy.Item):
    # define the fields for your item here like:
    # name = scrapy.Field()
    pass

```

### GreenGrowth/GreenGrowth/pipelines.py

```python
# Define your item pipelines here
#
# Don't forget to add your pipeline to the ITEM_PIPELINES setting
# See: https://docs.scrapy.org/en/latest/topics/item-pipeline.html


# useful for handling different item types with a single interface
from itemadapter import ItemAdapter


class GreenjobsPipeline:
    def process_item(self, item, spider):
        return item

```

### greenjobs/greenjobs/pipelines.py

```python
# Define your item pipelines here
#
# Don't forget to add your pipeline to the ITEM_PIPELINES setting
# See: https://docs.scrapy.org/en/latest/topics/item-pipeline.html


# useful for handling different item types with a single interface
from itemadapter import ItemAdapter


class GreenjobsPipeline:
    def process_item(self, item, spider):
        return item

```

### GreenGrowth/GreenGrowth/settings.py

```python
# Scrapy settings for greenjobs project
#
# For simplicity, this file contains only settings considered important or
# commonly used. You can find more settings consulting the documentation:
#
#     https://docs.scrapy.org/en/latest/topics/settings.html
#     https://docs.scrapy.org/en/latest/topics/downloader-middleware.html
#     https://docs.scrapy.org/en/latest/topics/spider-middleware.html

BOT_NAME = "greenjobs"

SPIDER_MODULES = ["greenjobs.spiders"]
NEWSPIDER_MODULE = "greenjobs.spiders"


# Crawl responsibly by identifying yourself (and your website) on the user-agent
#USER_AGENT = "greenjobs (+http://www.yourdomain.com)"

# Obey robots.txt rules
ROBOTSTXT_OBEY = True

# Configure maximum concurrent requests performed by Scrapy (default: 16)
#CONCURRENT_REQUESTS = 32

# Configure a delay for requests for the same website (default: 0)
# See https://docs.scrapy.org/en/latest/topics/settings.html#download-delay
# See also autothrottle settings and docs
#DOWNLOAD_DELAY = 3
# The download delay setting will honor only one of:
#CONCURRENT_REQUESTS_PER_DOMAIN = 16
#CONCURRENT_REQUESTS_PER_IP = 16

# Disable cookies (enabled by default)
#COOKIES_ENABLED = False

# Disable Telnet Console (enabled by default)
#TELNETCONSOLE_ENABLED = False

# Override the default request headers:
#DEFAULT_REQUEST_HEADERS = {
#    "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
#    "Accept-Language": "en",
#}

# Enable or disable spider middlewares
# See https://docs.scrapy.org/en/latest/topics/spider-middleware.html
#SPIDER_MIDDLEWARES = {
#    "greenjobs.middlewares.GreenjobsSpiderMiddleware": 543,
#}

# Enable or disable downloader middlewares
# See https://docs.scrapy.org/en/latest/topics/downloader-middleware.html
#DOWNLOADER_MIDDLEWARES = {
#    "greenjobs.middlewares.GreenjobsDownloaderMiddleware": 543,
#}

# Enable or disable extensions
# See https://docs.scrapy.org/en/latest/topics/extensions.html
#EXTENSIONS = {
#    "scrapy.extensions.telnet.TelnetConsole": None,
#}

# Configure item pipelines
# See https://docs.scrapy.org/en/latest/topics/item-pipeline.html
#ITEM_PIPELINES = {
#    "greenjobs.pipelines.GreenjobsPipeline": 300,
#}

# Enable and configure the AutoThrottle extension (disabled by default)
# See https://docs.scrapy.org/en/latest/topics/autothrottle.html
#AUTOTHROTTLE_ENABLED = True
# The initial download delay
#AUTOTHROTTLE_START_DELAY = 5
# The maximum download delay to be set in case of high latencies
#AUTOTHROTTLE_MAX_DELAY = 60
# The average number of requests Scrapy should be sending in parallel to
# each remote server
#AUTOTHROTTLE_TARGET_CONCURRENCY = 1.0
# Enable showing throttling stats for every response received:
#AUTOTHROTTLE_DEBUG = False

# Enable and configure HTTP caching (disabled by default)
# See https://docs.scrapy.org/en/latest/topics/downloader-middleware.html#httpcache-middleware-settings
#HTTPCACHE_ENABLED = True
#HTTPCACHE_EXPIRATION_SECS = 0
#HTTPCACHE_DIR = "httpcache"
#HTTPCACHE_IGNORE_HTTP_CODES = []
#HTTPCACHE_STORAGE = "scrapy.extensions.httpcache.FilesystemCacheStorage"

# Set settings whose default value is deprecated to a future-proof value
TWISTED_REACTOR = "twisted.internet.asyncioreactor.AsyncioSelectorReactor"
FEED_EXPORT_ENCODING = "utf-8"

```

### greenjobs/greenjobs/settings.py

```python
# Scrapy settings for greenjobs project
#
# For simplicity, this file contains only settings considered important or
# commonly used. You can find more settings consulting the documentation:
#
#     https://docs.scrapy.org/en/latest/topics/settings.html
#     https://docs.scrapy.org/en/latest/topics/downloader-middleware.html
#     https://docs.scrapy.org/en/latest/topics/spider-middleware.html

BOT_NAME = "greenjobs"

SPIDER_MODULES = ["greenjobs.spiders"]
NEWSPIDER_MODULE = "greenjobs.spiders"


# Crawl responsibly by identifying yourself (and your website) on the user-agent
#USER_AGENT = "greenjobs (+http://www.yourdomain.com)"

# Obey robots.txt rules
ROBOTSTXT_OBEY = True

# Configure maximum concurrent requests performed by Scrapy (default: 16)
#CONCURRENT_REQUESTS = 32

# Configure a delay for requests for the same website (default: 0)
# See https://docs.scrapy.org/en/latest/topics/settings.html#download-delay
# See also autothrottle settings and docs
#DOWNLOAD_DELAY = 3
# The download delay setting will honor only one of:
#CONCURRENT_REQUESTS_PER_DOMAIN = 16
#CONCURRENT_REQUESTS_PER_IP = 16

# Disable cookies (enabled by default)
#COOKIES_ENABLED = False

# Disable Telnet Console (enabled by default)
#TELNETCONSOLE_ENABLED = False

# Override the default request headers:
#DEFAULT_REQUEST_HEADERS = {
#    "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
#    "Accept-Language": "en",
#}

# Enable or disable spider middlewares
# See https://docs.scrapy.org/en/latest/topics/spider-middleware.html
#SPIDER_MIDDLEWARES = {
#    "greenjobs.middlewares.GreenjobsSpiderMiddleware": 543,
#}

# Enable or disable downloader middlewares
# See https://docs.scrapy.org/en/latest/topics/downloader-middleware.html
#DOWNLOADER_MIDDLEWARES = {
#    "greenjobs.middlewares.GreenjobsDownloaderMiddleware": 543,
#}

# Enable or disable extensions
# See https://docs.scrapy.org/en/latest/topics/extensions.html
#EXTENSIONS = {
#    "scrapy.extensions.telnet.TelnetConsole": None,
#}

# Configure item pipelines
# See https://docs.scrapy.org/en/latest/topics/item-pipeline.html
#ITEM_PIPELINES = {
#    "greenjobs.pipelines.GreenjobsPipeline": 300,
#}

# Enable and configure the AutoThrottle extension (disabled by default)
# See https://docs.scrapy.org/en/latest/topics/autothrottle.html
#AUTOTHROTTLE_ENABLED = True
# The initial download delay
#AUTOTHROTTLE_START_DELAY = 5
# The maximum download delay to be set in case of high latencies
#AUTOTHROTTLE_MAX_DELAY = 60
# The average number of requests Scrapy should be sending in parallel to
# each remote server
#AUTOTHROTTLE_TARGET_CONCURRENCY = 1.0
# Enable showing throttling stats for every response received:
#AUTOTHROTTLE_DEBUG = False

# Enable and configure HTTP caching (disabled by default)
# See https://docs.scrapy.org/en/latest/topics/downloader-middleware.html#httpcache-middleware-settings
#HTTPCACHE_ENABLED = True
#HTTPCACHE_EXPIRATION_SECS = 0
#HTTPCACHE_DIR = "httpcache"
#HTTPCACHE_IGNORE_HTTP_CODES = []
#HTTPCACHE_STORAGE = "scrapy.extensions.httpcache.FilesystemCacheStorage"

# Set settings whose default value is deprecated to a future-proof value
TWISTED_REACTOR = "twisted.internet.asyncioreactor.AsyncioSelectorReactor"
FEED_EXPORT_ENCODING = "utf-8"

```

### GreenGrowth/GreenGrowth/middlewares.py

```python
# Define here the models for your spider middleware
#
# See documentation in:
# https://docs.scrapy.org/en/latest/topics/spider-middleware.html

from scrapy import signals

# useful for handling different item types with a single interface
from itemadapter import is_item, ItemAdapter


class GreenjobsSpiderMiddleware:
    # Not all methods need to be defined. If a method is not defined,
    # scrapy acts as if the spider middleware does not modify the
    # passed objects.

    @classmethod
    def from_crawler(cls, crawler):
        # This method is used by Scrapy to create your spiders.
        s = cls()
        crawler.signals.connect(s.spider_opened, signal=signals.spider_opened)
        return s

    def process_spider_input(self, response, spider):
        # Called for each response that goes through the spider
        # middleware and into the spider.

        # Should return None or raise an exception.
        return None

    def process_spider_output(self, response, result, spider):
        # Called with the results returned from the Spider, after
        # it has processed the response.

        # Must return an iterable of Request, or item objects.
        for i in result:
            yield i

    def process_spider_exception(self, response, exception, spider):
        # Called when a spider or process_spider_input() method
        # (from other spider middleware) raises an exception.

        # Should return either None or an iterable of Request or item objects.
        pass

    def process_start_requests(self, start_requests, spider):
        # Called with the start requests of the spider, and works
        # similarly to the process_spider_output() method, except
        # that it doesn’t have a response associated.

        # Must return only requests (not items).
        for r in start_requests:
            yield r

    def spider_opened(self, spider):
        spider.logger.info("Spider opened: %s" % spider.name)


class GreenjobsDownloaderMiddleware:
    # Not all methods need to be defined. If a method is not defined,
    # scrapy acts as if the downloader middleware does not modify the
    # passed objects.

    @classmethod
    def from_crawler(cls, crawler):
        # This method is used by Scrapy to create your spiders.
        s = cls()
        crawler.signals.connect(s.spider_opened, signal=signals.spider_opened)
        return s

    def process_request(self, request, spider):
        # Called for each request that goes through the downloader
        # middleware.

        # Must either:
        # - return None: continue processing this request
        # - or return a Response object
        # - or return a Request object
        # - or raise IgnoreRequest: process_exception() methods of
        #   installed downloader middleware will be called
        return None

    def process_response(self, request, response, spider):
        # Called with the response returned from the downloader.

        # Must either;
        # - return a Response object
        # - return a Request object
        # - or raise IgnoreRequest
        return response

    def process_exception(self, request, exception, spider):
        # Called when a download handler or a process_request()
        # (from other downloader middleware) raises an exception.

        # Must either:
        # - return None: continue processing this exception
        # - return a Response object: stops process_exception() chain
        # - return a Request object: stops process_exception() chain
        pass

    def spider_opened(self, spider):
        spider.logger.info("Spider opened: %s" % spider.name)

```

### greenjobs/greenjobs/middlewares.py

```python
# Define here the models for your spider middleware
#
# See documentation in:
# https://docs.scrapy.org/en/latest/topics/spider-middleware.html

from scrapy import signals

# useful for handling different item types with a single interface
from itemadapter import is_item, ItemAdapter


class GreenjobsSpiderMiddleware:
    # Not all methods need to be defined. If a method is not defined,
    # scrapy acts as if the spider middleware does not modify the
    # passed objects.

    @classmethod
    def from_crawler(cls, crawler):
        # This method is used by Scrapy to create your spiders.
        s = cls()
        crawler.signals.connect(s.spider_opened, signal=signals.spider_opened)
        return s

    def process_spider_input(self, response, spider):
        # Called for each response that goes through the spider
        # middleware and into the spider.

        # Should return None or raise an exception.
        return None

    def process_spider_output(self, response, result, spider):
        # Called with the results returned from the Spider, after
        # it has processed the response.

        # Must return an iterable of Request, or item objects.
        for i in result:
            yield i

    def process_spider_exception(self, response, exception, spider):
        # Called when a spider or process_spider_input() method
        # (from other spider middleware) raises an exception.

        # Should return either None or an iterable of Request or item objects.
        pass

    def process_start_requests(self, start_requests, spider):
        # Called with the start requests of the spider, and works
        # similarly to the process_spider_output() method, except
        # that it doesn’t have a response associated.

        # Must return only requests (not items).
        for r in start_requests:
            yield r

    def spider_opened(self, spider):
        spider.logger.info("Spider opened: %s" % spider.name)


class GreenjobsDownloaderMiddleware:
    # Not all methods need to be defined. If a method is not defined,
    # scrapy acts as if the downloader middleware does not modify the
    # passed objects.

    @classmethod
    def from_crawler(cls, crawler):
        # This method is used by Scrapy to create your spiders.
        s = cls()
        crawler.signals.connect(s.spider_opened, signal=signals.spider_opened)
        return s

    def process_request(self, request, spider):
        # Called for each request that goes through the downloader
        # middleware.

        # Must either:
        # - return None: continue processing this request
        # - or return a Response object
        # - or return a Request object
        # - or raise IgnoreRequest: process_exception() methods of
        #   installed downloader middleware will be called
        return None

    def process_response(self, request, response, spider):
        # Called with the response returned from the downloader.

        # Must either;
        # - return a Response object
        # - return a Request object
        # - or raise IgnoreRequest
        return response

    def process_exception(self, request, exception, spider):
        # Called when a download handler or a process_request()
        # (from other downloader middleware) raises an exception.

        # Must either:
        # - return None: continue processing this exception
        # - return a Response object: stops process_exception() chain
        # - return a Request object: stops process_exception() chain
        pass

    def spider_opened(self, spider):
        spider.logger.info("Spider opened: %s" % spider.name)

```

### GreenGrowth/GreenGrowth/spiders/__init__.py

```python
# This package will contain the spiders of your Scrapy project
#
# Please refer to the documentation for information on how to create and manage
# your spiders.

```

### greenjobs/greenjobs/spiders/__init__.py

```python
# This package will contain the spiders of your Scrapy project
#
# Please refer to the documentation for information on how to create and manage
# your spiders.

```

[6 more indexed source files omitted to keep this export small. The full file list is in the Codebase structure section above.]