Alle Werte eines Dropdown-Menüs im Quelltext anzeigen lassen


loddarmattheus

Erfahrenes Mitglied
Hallo zusammen,
angenommen, ich möchte von (m)einer Webseite alle Werte eines Menus anzeigen lassen - geht das?
Hier mal ein Beispiel. Wenn ich auf Typnummer klicke, werden mir alle Ergebnisse angezeigt. Klicke ich beispieltsweise auf das erste Ergebnis (AE120), wird mir im Quelltext auch nur dieser Wert angezeigt.

123.jpg

Geht es irgendwie, dass ich alle Ergebnisse auf einmal auslesen kann und nicht nur einzeln?
 

StormChaser

Mitglied
Wenn es so ist, wie du beschreibst, werden die Daten erst geladen, nachdem du einen Typ ausgewählt hast. Alle Daten sind also garnicht vorhanden, um sie auszulesen.
 

loddarmattheus

Erfahrenes Mitglied
Hallo nochmal,

ich möchte mein Problem mal etwas plakativer darstellen. Es handelt sich um die URL: Swirl - Staubsaugerbeutel-Finder | Staubsaugen mit Swirl

Im Opera werden mir die Werte aus dem rechten Dropdown nach Klick auf "Element untersuchen" ja alle im rechten Fenster angezeigt (Siehe roter Kasten).
Nur bekomme ich diese Werte nicht weiterverarbeitet, denn ich kann maximal eine Zeile davon kopieren und nicht alle zusammen.

Vielleicht hat ja jemand eine Idee?

swirl.jpg
 

ComFreek

Mod | @comfreek
Moderator
Möchtest du einmal alle Werte statisch per Hand kopieren oder möchtest du, dass deine Website immer die aktuellsten Werte live hernimmt?
 

basti1012

Erfahrenes Mitglied
Im Opera werden mir die Werte aus dem rechten Dropdown nach Klick auf "Element untersuchen" ja alle im rechten Fenster angezeigt (Siehe roter Kasten).
Nur bekomme ich diese Werte nicht weiterverarbeitet, denn ich kann maximal eine Zeile davon kopieren und nicht alle zusammen.
Du meinst jetzt da aus der Konsole rauskopiern?
Mach mal rechts klick auf das <select und klicke dann auf Copy => Copy outerHTML.
Dann hast du den ganzen Quellcode von den <select Menü inclusive alle <option>
 

loddarmattheus

Erfahrenes Mitglied
Guten Morgen,
ich möchte ALLE Werte aus dem roten Kasten einfach rauskopieren, damit ich sie in Excel beispielsweise weiterverarbeiten kann.

Dass mit dem Rechtsklick auf einen Eintrag und Kopieren->Copy outer HTML funktioniert leider nicht, da ich dann immer nur eine Zeile erhalte, z.B. <option value="27154_268_S" data-searchdata=" ">DIV 380</option>

Ich müsste nur alle diese Zeilen irgendwie gleichzeitig markieren können, aber in der Konsole geht nur immer eine Zeile :(
 

ComFreek

Mod | @comfreek
Moderator
Du musst "copy outer HTML" natürlich auf dem <select> ausführen :)

Anyway, ich habe mal die Gunst der Stunde genutzt, selbst ein bisschen wieder meine JS Promise-Skills auf Vordermann zu bringen und habe dir ein Skript geschrieben, das alles extrahiert.

Alle extrahierten Marken und zugehörigen Modelle findest du im Anhang als ZIP-komprimierte JSON-Datei. Das JSON ist ein großes Dictionary, wobei die Keys Marken sind und die Values Arrays von zugehörigen Modellen. Daraus solltest du sehr einfach mit JS oder einer anderen Sprache deiner Wahl ein Format aufbereiten können, das Excel versteht.

Javascript:
// Usage
// ============
// 1. Using Chrome go to https://www.swirl.de/de/Staubsaugerbeutel-Finder-Staubsaugen-mit-Swirl-648.html
// 2. Open dev console
// 3. Copy-paste and run this script in the console
// 4. Save returned JS object using JSON.stringify in the console (e.g. first call JSON.stringify, then manually copy the string to a file)

// I am sorry for mixing English and German, but I believed retaining "marke" and "modell"
// has some advantages since they directly correspond to the things on the website we scrape.

function getOptions(selectId, skip = 0) {
    return Array.from(document.getElementById(selectId).options).slice(skip)
}

function* extractAllModelle(htmlResponseText) {
    const extractingRegex = /<option[^>]*? value="(?<value>.*?)"[^>]*?>(?<innerText>[^<]*?)<\/option>/gmi;
    let match = null;
  
    let alreadySkippedFirst = false;
  
    while (match = extractingRegex.exec(htmlResponseText)) {
        // First match contains <select> "header" text
        if (!alreadySkippedFirst) {
            alreadySkippedFirst = true;
            continue;
        }
        yield match.groups;
    }
}

function getModelleForMarke(marke) {
    const url = `https://www.swirl.de/common/ajax.php?bereich=portal&modul_id=102&klasse=staubfilterbeutelsuche&com=laden_typen`;
  
    // fetch call extracted via Chrome dev tools from site by performing actual UI action on site and waiting
    // for the original request, which we're going to imitate, to fire
    return fetch(
        'https://www.swirl.de/common/ajax.php?bereich=portal&modul_id=102&klasse=staubfilterbeutelsuche&com=laden_typen', {
            'headers': {
                'accept': '*/*',
                'accept-language': 'en,de;q=0.9,en-US;q=0.8',
                'content-type': 'application/x-www-form-urlencoded;charset=UTF-8',
                'sec-fetch-dest': 'empty',
                'sec-fetch-mode': 'cors',
                'sec-fetch-site': 'same-origin',
                'x-requested-with': 'XMLHttpRequest'
            },
            'referrer': 'https://www.swirl.de/de/Staubsaugerbeutel-Finder-Staubsaugen-mit-Swirl-648.html',
            'referrerPolicy': 'no-referrer-when-downgrade',
            'body': `marke=${marke}&sprache=de&texte=staubfilterbeutelsuche%2Ftexte_modul_staubfilterbeutelsuche`,
            'method': 'POST',
            'mode': 'cors',
            'credentials': 'include'
        }
    ).then(response => response.text()).then(html => [...extractAllModelle(html)]);
}

function getModelleForMultipleMarken(marken) {
    const allModelle = marken.map(async function(marke) {
        return {marke, modelle: await getModelleForMarke(marke)};
    });
    return Promise.all(allModelle).then(arrayOfMarken => // Build a dictionary again
        arrayOfMarken.reduce((dict, {marke, modelle}) => {
            dict[marke] = modelle;
            return dict;
        }, {})
    );
}

function* batchIterable(iterable, batchSize) {
    let curBatch = [];

    for (const value of iterable) {
        if (curBatch.length == batchSize) {
            yield curBatch;
            curBatch = [];
        }
      
        curBatch.push(value);
    }
    if (curBatch.length >= 1) {
        yield curBatch;
    }
}

/**
    * promisingReduce<S, T>
    *
  * @param array An array of Promise<T>
    * @param f A function (acc: S, cur: T) => S
    * @param defaultvalue An S
    *
    * @return A Promise<S>
    */
async function promisingReduce(array, f, defaultValue) {
    let acc = defaultValue;
    for (const item of array) {
        acc = f(acc, await item);
    }
    return acc;
}

function batchProcess(array, f, combine, combineDefault) {
    const batchSize = 50;
    const batches = [...batchIterable(array, batchSize)];
    return promisingReduce(batches.map(f), combine, combineDefault);
}

const marken = getOptions('sfbs-select-seite-marken', 1).map(option => parseInt(option.value, 10));
console.log(`Gathered ${marken.length} number of marken`);

function mergeDict(dict1, dict2) {
    return Object.assign({}, dict1, dict2);
}

batchProcess(marken, getModelleForMultipleMarken, mergeDict, {})
 

Anhänge

basti1012

Erfahrenes Mitglied
Dass mit dem Rechtsklick auf einen Eintrag und Kopieren->Copy outer HTML funktioniert leider nicht, da ich dann immer nur eine Zeile erhalte, z.B. <option value="27154_268_S" data-searchdata=" ">DIV 380</option>
@ComFreek hat es ja auch schon gesagt, du mußt in diesen fall das parent Element nehmen und den Rechtsklick auf den <select> machen und da Copy outerHTML
Dann hast du den ganzen Quelltext von
<select>
<option>...</option>
....
...
</select>
bis hier hin.
Du hast ja auch gerade nee Liste bekommen wo alles drinne steht,dann weißt du es beim nächsten mal das du bei sowas das Elternelement nehmen mußt für den Rechtsklick Copy