Skip to content

glad I found this - some notes #8

Description

@derpcfreak

Hi, I was solving the same problem you did - getting the heise login cookie.

From the beginning I did not only want to download the magazines, I also wanted to use their plus RSS feed and automatically download and archive the articles.

Since I use the tool "SingleFile" (https://hub.docker.com/r/capsulecode/singlefile) from a docker container to download the articles (giving it the cookie) I always had their f***ing cookie banner stored.

If using your way of getting the cookie the cookie does not contain the cookie consent e.g.:

.heise.de       TRUE    /       TRUE    1702090640      consentDate     2026-01-25T13:11:40.572Z
.heise.de       TRUE    /       TRUE    1702090640      consentUUID     bbd6c666-9353-4ae8-9716-192449656e25_51

Without the above information any heise page you call with a "virtual" browser will contain the banner.

What I ended up with is using a Selenium container docker.io/selenium/standalone-chrome, that does the login for me using a python script that remote controls Selenium. I login and I also click the Cookie banner accept button.
Using this way, the cookie contains the Cookie consent and I can use it anywhere, not just for downloading.

When the Selenium container listens on localhost:4444 I use the following script to automatically download the cookie with consent:

#!/usr/bin/env python3

####################################################################################
# import python modules we will need
####################################################################################
import sys
import os
import time
import argparse
from netscape_cookies import save_cookies_to_file # for saving cookies in netscape text format
from netscape_cookies import to_netscape_string   # for saving cookies in netscape text format
####################################################################################

####################################################################################
# variables
####################################################################################
# Store the script filename so we can show it in custom error messages.
script_name = os.path.basename(sys.argv[0])
####################################################################################

####################################################################################
# retrieve loginURL, username and password from command line parameters
####################################################################################
# Use a tiny custom argparse subclass so we can keep the exact error message
# style from the original script instead of argparse's default wording.
class MyArgumentParser(argparse.ArgumentParser):
    def error(self, message):
        print(f"\nERROR: {script_name} called with incorrect parameters!")
        print(f"Usage: python {script_name} <heise.de loginURL> <username> <password> <cookiefile>\n")
        sys.exit(1)

# Define the three required positional command line arguments.
parser = MyArgumentParser(add_help=True)
parser.add_argument("loginURL")
parser.add_argument("username")
parser.add_argument("password")
parser.add_argument("netscapecookiefile")
args = parser.parse_args()

# Copy parsed arguments into variable names names
loginURL = args.loginURL
username = args.username
password = args.password
netscapecookiefile = args.netscapecookiefile
####################################################################################

####################################################################################
# configure selenium
####################################################################################
from selenium.webdriver.chrome.options import Options as ChromeOptions
from selenium import webdriver
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import Select
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.common.exceptions import TimeoutException
from selenium.common.exceptions import WebDriverException

## configure options for chrome inside selenium
options = ChromeOptions()

# Ask the browser to prefer English UI text where possible.
options.add_argument('--lang=en_US')

# "eager" returns control before every last resource finishes loading.
# This often makes Selenium sessions less fragile on pages with slow extras.
options.page_load_strategy = "eager" # should fix 'cannot determine loading status' error

# Create the remote browser session via the local Selenium server / Grid.
# If the server is not reachable, stop early with a readable error.
try:
    driver = webdriver.Remote(
                              options=options,
                              command_executor="http://localhost:4444/wd/hub"
                              )
except WebDriverException as e:
    print("\nERROR: could not connect to selenium server at http://localhost:4444/wd/hub")
    print("       please check if selenium is running and reachable\n")
    print("Original error:", e)
    print("")
    sys.exit(1)

# Maximize once after session startup; the later explicit size keeps the
# viewport predictable for element positioning.
driver.maximize_window()
####################################################################################

####################################################################################
# Interact with a webpage using selenium
####################################################################################
# Wrap the browser work in finally so the browser session is closed even if
# something fails in the middle of the workflow.
try:
    ## Open the page
    # Use a fixed window size to keep the page layout consistent across runs.
    driver.set_window_size(1800, 800)
    print("      venv: Opening URL:", loginURL)
    driver.get(loginURL)

    ## find username element and fill it
    print("      venv: Find username elment and fill it")
    # Wait until the username field is actually visible before typing.
    usernameelement = WebDriverWait(driver, 20).until(
        EC.visibility_of_element_located((By.XPATH, "//*[@id='login-user']"))
    )
    usernameelement.clear()
    usernameelement.send_keys(str(username))

    ## find password element and fill it
    print("      venv: Find password elment and fill it")
    # Wait until the password field is visible before typing.
    passwordelement = WebDriverWait(driver, 20).until(
        EC.visibility_of_element_located((By.XPATH, "//*[@id='login-password']"))
    )
    passwordelement.clear()
    passwordelement.send_keys(str(password))

    ## find submit button and click it
    print("      venv: Find submit button and click it")
    # Use the button's name attribute to detect it
    submitelement = WebDriverWait(driver, 20).until(
        EC.element_to_be_clickable((By.CSS_SELECTOR, "button[name='rm_login']"))
    )
    submitelement.click()

    ## click the cookie consent banner
    try:
        ### - switch to cookie policy iframe
        print("      venv: Switching to Cookie Policy iFrame")
        # The consent UI lives inside an iframe, so Selenium must switch into
        # that frame before trying to find the button.
        WebDriverWait(driver, 20).until(
            EC.frame_to_be_available_and_switch_to_it((By.XPATH, "//*[@title='SP Consent Message']"))
        )

        # do a small delay, just for sure
        time.sleep(2)

        ### - find the 'Agree' button by xpath
        print("      venv: Find the 'Agree' button")
        # Wait until the specific consent button inside the iframe is clickable.
        consentbutton = WebDriverWait(driver, 20).until(
            EC.element_to_be_clickable((By.XPATH, '//*[@id="notice"]/div[4]/div/div[1]/div[1]/button'))
        )

        ### - click the 'Agree' button
        print("      venv: Click the 'Agree' button")
        try:
            # First try a normal Selenium click.
            consentbutton.click()
        except Exception:
            # Fall back to a JavaScript click if the normal click is blocked
            # by overlays or other timing-related page behavior.
            print("      venv: normal click failed, trying javascript click")
            driver.execute_script("arguments[0].click();", consentbutton)

        # do a small delay, just for sure
        time.sleep(2)

        # Always return to the main page content after working inside the iframe.
        driver.switch_to.default_content()

    except TimeoutException:
        # If the consent iframe or button never becomes ready, abort so we do
        # not continue with an incomplete session state.
        print("      venv: ERROR: cookie consent iframe or button not found")
        driver.switch_to.default_content()
        sys.exit(1)

    ## save cookies to a file
    # Read the browser cookies after login and consent handling has completed.
    cookievalues = driver.get_cookies()

    # Write cookies in Netscape text format for reuse in other tools.
    save_cookies_to_file(cookievalues, netscapecookiefile) # write in netscape format using netscape_cookies

    # Report the cookie output file to the user.
    print("      venv: wrote Netscape compatible cookie to: ",netscapecookiefile)

finally:
    # Always close the Selenium session, even on errors.
    driver.quit()
####################################################################################

Just wanted to inform you about the issue. If you have any idea how to get the cookie consent into the cookie with your method or any other faster method, feel free to send a message here.

Otherwise just close this issue, which is not an issue at all.

Thanks for your good work.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions