From 90ee263016de9ac9cc14a38c470aa718e3218dd0 Mon Sep 17 00:00:00 2001 From: Benjamin Levy <7348004+io12@users.noreply.github.com> Date: Mon, 6 Jul 2026 23:19:56 +0000 Subject: [PATCH] Add SUBSTACK_EMAIL/SUBSTACK_PASSWORD environment variables Currently the only way to supply premium-login credentials is by editing config.py, which is committed to the repo with placeholder values. This makes the tool awkward to use from contexts where the source isn't writable (e.g. packaged/installed rather than run from a live checkout) and encourages people to accidentally commit real credentials into config.py (as happened in PR #36). SUBSTACK_EMAIL and SUBSTACK_PASSWORD now take precedence over config.py if set, and config.py itself becomes optional: the import no longer fails if the file is missing. Also fail fast with a clear error when --premium is used without --skip-login and no real credentials are configured anywhere. Previously this silently sent empty/placeholder strings into the Substack login form, launched a real browser, waited 30 seconds, and then either printed a generic "Login unsuccessful" message or, if the browser's own client-side validation blocked the empty submission, incorrectly reported success. Co-Authored-By: Claude Sonnet 5 --- README.md | 11 ++++++++++- substack_scraper.py | 30 +++++++++++++++++++++++++++++- 2 files changed, 39 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 59ba6ef..a59179d 100644 --- a/README.md +++ b/README.md @@ -43,13 +43,22 @@ cd substack_scraper pip install -r requirements.txt ``` -For the premium scraper, update the `config.py` in the root directory with your Substack email and password: +For the premium scraper, set your Substack email and password with the `SUBSTACK_EMAIL` and `SUBSTACK_PASSWORD` environment variables: + +```bash +export SUBSTACK_EMAIL="your-email@domain.com" +export SUBSTACK_PASSWORD="your-password" +``` + +Alternatively, update the `config.py` in the root directory with your Substack email and password: ```python EMAIL = "your-email@domain.com" PASSWORD = "your-password" ``` +The environment variables take precedence over `config.py` if both are set. + You'll also need Microsoft Edge installed for the Selenium webdriver. ## Usage diff --git a/substack_scraper.py b/substack_scraper.py index 5ff195b..6c17a6f 100644 --- a/substack_scraper.py +++ b/substack_scraper.py @@ -32,7 +32,21 @@ from selenium.webdriver.support import expected_conditions as EC from selenium.common.exceptions import SessionNotCreatedException, TimeoutException, WebDriverException -from config import EMAIL, PASSWORD +try: + from config import EMAIL, PASSWORD +except ImportError: + EMAIL = "" + PASSWORD = "" + +# Environment variables take precedence over config.py, and config.py is no +# longer required to be present as long as these are set. +EMAIL = os.environ.get("SUBSTACK_EMAIL", EMAIL) +PASSWORD = os.environ.get("SUBSTACK_PASSWORD", PASSWORD) + +# The values config.py ships with by default; if these are still in effect, +# nobody has actually configured credentials. +PLACEHOLDER_EMAIL = "your-email@domain.com" +PLACEHOLDER_PASSWORD = "your-password" USE_PREMIUM: bool = True BASE_SUBSTACK_URL: str = "https://niallferguson.substack.com/" @@ -1196,6 +1210,20 @@ def __init__( use_persistent_profile: Reuse browser profile across runs (saves login) skip_login: Skip login if using a pre-authenticated profile """ + if not skip_login and ( + not EMAIL + or not PASSWORD + or EMAIL == PLACEHOLDER_EMAIL + or PASSWORD == PLACEHOLDER_PASSWORD + ): + raise ValueError( + "Premium scraping requires credentials. Set the SUBSTACK_EMAIL " + "and SUBSTACK_PASSWORD environment variables, or edit config.py " + "with your real Substack email and password. If you've already " + "logged in with a persistent browser profile, pass " + "--persistent-profile --skip-login instead." + ) + # Initialize driver before calling super().__init__ since that fetches URLs self.driver = BrowserManager.create_driver( browser=browser,