GO / GOLANG Webscraping

Der_Picknicker

Lt. Commander
Registriert
Nov. 2020
Beiträge
1.501
Hallo zusammen,

ich habe bereits einige Programmiererfahrungen in C vor allem im Embedded Bereich. Nun möchte ich aber neues Lernen und damit auch eine neue Sprache. GO / GOLANG. Ich habe mir für das Vorhaben des Lernens die Aufgabe überlegt, dass ich gerne einen Preistracker bauen möchte. Dieser soll lediglich auf einer Website funktionieren.

Ich habe bereits verschiedene Anleitungen durchgearbeitet. Zunächst mit der Bibliothek Colly => http://go-colly.org/
Dies funktioniert jedoch nicht, da der Code dann verschiedene Preise zurückgibt, da dies wahrscheinlich seitens der Online-Shops erkannt wurde, dass da kein echter Mensch vor sitzt ;)

Dieser Code ist von mir getestet und funktioniert, wie von der Anleitung geschrieben:
Go:
package main
    
import (
    "context"
    "fmt"
    "github.com/chromedp/cdproto/cdp"
    "github.com/chromedp/chromedp"
    "log"
)

// Product data structure to store the scraped data
type Product struct {
    name, price string
}

func main() {
    // to keep track of all the scraped objects
    var products []Product

    // initialize a controllable Chrome instance
    ctx, cancel := chromedp.NewContext(
       context.Background(),
    )
    // to release the browser resources when
    // it is no longer needed
    defer cancel()

    // browser automation logic
    var productNodes []*cdp.Node
    err := chromedp.Run(ctx,
       chromedp.Navigate("https://scrapingclub.com/exercise/list_infinite_scroll/"),
       chromedp.Nodes(".post", &productNodes, chromedp.ByQueryAll),
    )
    if err != nil {
       log.Fatal("Error:", err)
    }

    // scraping logic
    var name, price string
    for _, node := range productNodes {
       // extract data from the product HTML node
       err = chromedp.Run(ctx,
          chromedp.Text("h4", &name, chromedp.ByQuery, chromedp.FromNode(node)),
          chromedp.Text("h5", &price, chromedp.ByQuery, chromedp.FromNode(node)),
       )

       if err != nil {
          log.Fatal("Error:", err)
       }

       // initialize a new product instance
       // with scraped data
       product := Product{}
       product.name = name
       product.price = price

       products = append(products, product)
    }

    fmt.Println(products)
}

Ich brauche jetzt jedoch eure Hilfe, weil ich nicht weiß, wie ich den Code auf die Seite von Home24 anpassen kann. Hier mal eine beispielhaftes Produkt, welches ich gerne überwachen würde:
https://www.home24.de/produkt/pendelleuchte-blacky-iii-rauchglas-eisen-8-flammig

Mit dem CSS-Inspector / Selector habe ich bereits folgende Informationen herausgefunden:
CSS:
<span data-testid="current-price" display="inline-block" font-size="fs_20,fs_24,fs_28,fs_32" font-weight="bold" class="css-f97reh">319,99 €</span>

Dort steht der aktuelle Preis harncodiert drinnen, dies wird jedoch erst bei aufrufen der Seite sichtbar. Bei Colly werden dort bestimmt 20 verschiedene Preise angezeigt. Wie bekomme ich nun ChromeDP dazu, dies so zu machen, wie ich es mir vorstelle?

Danke fürs lesen und eure Zeit.
 
Webscraper umgehen/blockieren ist ein ähnlicher Wettlauf wie Adblocker umgehen/erkennen.
Du solltest ziemlich sicher eine Bibliothek dir suchen, dir sehr gut gewartet wird, das sieht mir bei colly nicht aus. Laut Github ist das letzte Release 3 Jahre her.
Ist da überhaupt ein Browser dahinter? "Heutzutage" macht man sowas eigentlich per Browser im Headless mode.

Ggf. mal bei Bibliotheken schauen, die für End-To-End Testing von Webseiten eingesetzt werden, z.b. https://playwright.dev/ und testen ob du damit weiter kommst.
 
@Tornhoof ...

Das habe ich auch gemerkt. Deshalb habe ich die Bibliothek bereits gewechselt. Ich verwende jetzt ChromeDP. Dies ist genau das was du geschrieben hattest. Ein Browser im Headlessmode.

Jedoch brauche ich Hilfe, wie ich den Code anpasse, damit dieser auf die Website von Home24 angepasst wird. Ich bin bei weitem kein Web-Developer ich komme aus einer ganz anderen Ecke und freue mich über jeden Denkanstoß ;)
 
Vorgehen Nummer 1 beim Scraping: Den Source der Seite ansehen und nicht die Seite selbst.
Code:
$ wget 'https://www.home24.de/produkt/pendelleuchte-blacky-iii-rauchglas-eisen-8-flammig'
Und in Zeile 4 - 8 sehen wir:
Code:
  <script>
    window.dataLayer = [{"pageType":"Product","shop":"H24DE","country":"DE","language":"de_DE","visitorLocation":"external","appVersion":"v3.21.0","appName":"PageApp-Pdp","botInfo":"Unknown Bot (wget_61A7AAC0B743BBF34412F781BE839B0C):monitor:Headless Browsers\u002FAutomation Tools","cartValue":0,"cartCount":0,"shopVersion":2,"productPrice":319.99,"categoryBreadcrumb":"Startseite | Lampen | Innenbeleuchtung | Pendelleuchten","productCampaigns":["specialdeals","hc-campaigns","sr-campaigns","munich-showroom","xlgspx-showroom-all","xlgspx-xmasanta-showroom","nonmp","showroom-munichSR"],"productBrand":"Globo Lighting","productDeliveryTime":8,"productName":"Pendelleuchte Blacky III","productSimpleSKU":"000000001000216368","variantName":"Rauchglas \u002F Eisen - 8-flammig","productConfigSKU":"000000001000216368-P","pageProdGroup":"Startseite","productRating":4.5,"productRatingCount":27,"productAvailability":"InStock","isRenovai":false,"isOlapic":true,"is3D":false,"contentCluster":null},{"eventCategory":"product","eventAction":"view","event":"productDetailView","eventData":{"products":[{"id":"000000001000216368","name":"Pendelleuchte Blacky III","price":319.99,"brand":"Globo Lighting","category":"Startseite | Lampen | Innenbeleuchtung | Pendelleuchten","variant":"Rauchglas \u002F Eisen - 8-flammig","deliveryTime":8,"resultId":null,"campaigns":["specialdeals","hc-campaigns","sr-campaigns","munich-showroom","xlgspx-showroom-all","xlgspx-xmasanta-showroom","nonmp","showroom-munichSR"],"productConfigSKU":"000000001000216368-P"}]}}];
    window.dataLayer.push({ 'gtm.start': new Date().getTime(), event: 'gtm.js' });
  </script>
Da steckt alles Nötige drin. Nochmal formatiert:
JSON:
[
    {
        "pageType": "Product",
        "shop": "H24DE",
        "country": "DE",
        "language": "de_DE",
        "visitorLocation": "external",
        "appVersion": "v3.21.0",
        "appName": "PageApp-Pdp",
        "botInfo": "Unknown Bot (wget_61A7AAC0B743BBF34412F781BE839B0C):monitor:Headless Browsers\u002FAutomation Tools",
        "cartValue": 0,
        "cartCount": 0,
        "shopVersion": 2,
        "productPrice": 319.99,
        "categoryBreadcrumb": "Startseite | Lampen | Innenbeleuchtung | Pendelleuchten",
        "productCampaigns": [
            "specialdeals",
            "hc-campaigns",
            "sr-campaigns",
            "munich-showroom",
            "xlgspx-showroom-all",
            "xlgspx-xmasanta-showroom",
            "nonmp",
            "showroom-munichSR"
        ],
        "productBrand": "Globo Lighting",
        "productDeliveryTime": 8,
        "productName": "Pendelleuchte Blacky III",
        "productSimpleSKU": "000000001000216368",
        "variantName": "Rauchglas \u002F Eisen - 8-flammig",
        "productConfigSKU": "000000001000216368-P",
        "pageProdGroup": "Startseite",
        "productRating": 4.5,
        "productRatingCount": 27,
        "productAvailability": "InStock",
        "isRenovai": false,
        "isOlapic": true,
        "is3D": false,
        "contentCluster": null
    },
    {
        "eventCategory": "product",
        "eventAction": "view",
        "event": "productDetailView",
        "eventData": {
            "products": [
                {
                    "id": "000000001000216368",
                    "name": "Pendelleuchte Blacky III",
                    "price": 319.99,
                    "brand": "Globo Lighting",
                    "category": "Startseite | Lampen | Innenbeleuchtung | Pendelleuchten",
                    "variant": "Rauchglas \u002F Eisen - 8-flammig",
                    "deliveryTime": 8,
                    "resultId": null,
                    "campaigns": [
                        "specialdeals",
                        "hc-campaigns",
                        "sr-campaigns",
                        "munich-showroom",
                        "xlgspx-showroom-all",
                        "xlgspx-xmasanta-showroom",
                        "nonmp",
                        "showroom-munichSR"
                    ],
                    "productConfigSKU": "000000001000216368-P"
                }
            ]
        }
    }
]
Fazit: ein Chrome Headless ist überflüssig, da ein simpler GET-Request ausreicht.
 
  • Gefällt mir
Reaktionen: f00bar, Der_Picknicker und Der Lord
ich habe auch den ein oder anderen scraper. schreibe ich allerdings in python. lasse aber python dann trotzdem wget ausführen, denn python als client, wird von vielen webseiten (oder von cloudflare) erkannt / gesperrt während wget mit einem browser user agent, noch funktioniert.

man muss einfach ausprobieren was geht und was nicht. wie du die seite selbst geparst bekommst ist dann dein problem da ist ja auch jede seite anders
 
@Yuuri ...

BOAR DANKE DIR. Den Weg werde ich mal weiterverfolgen. Das klingt doch relativ gut, ich muss quasi nur den GET-Request machen und anschließend kann ich die Daten entsprechend weiterverarbeiten.


@kieleich ... Python. mag ich nicht so gerne... Es soll wie gesagt nur ein kleines Lernprojekt sein ;) ...
 
@Yuuri … Dein Tipp war Goldwert. Ich lade momentan zwar noch die ganze .html Herunter, lösche alles via Regex und kann dann in Go mit einer simplen Funktion das Mapping vornehmen. Dahingehend ist Go wirklich klasse.

Kennst du einen Weg, wie ich nicht jedes Mal die ganze Seite herunterladen muss? So ginge es zwar auch, anders wäre es jedoch eleganter ;)
 
Nein, der Code wird ja nur ausgespuckt, wenn du diese Seite anforderst. Du könntest auf der Seite noch nach ner API schauen, die die Preise enthält, aber viel besser wirds nicht. Das hat Scrapen eben so an sich.
 
kieleich schrieb:
wird von vielen webseiten (oder von cloudflare) erkannt / gesperrt während wget mit einem browser user agent
Du kannst auch bei requests den User Agent angeben, musst eben nur den Header manuell setzen.
 
reicht nicht. da ist irgendwas mit tls fingerprinting statt dem user agent am werk
 
@kieleich Interessant. Kannst du mir eine Beispiel Seite geben?
 
Yuuri schrieb:
Nein, der Code wird ja nur ausgespuckt, wenn du diese Seite anforderst.
Hi, danke dir. Ich werde es so belassen. Wenn ich fertig bin, poste ich hier mal den Code…

Ich bin schon fast am Ziel:
  • Herunterladen der Website
  • Suchen nach dem Datalayer
  • In JSON-Formatieren und anschließend in einem Struct ablegen.
  • Alle Dateien in einem Ordner anzeigen
  • Daten aus dem Ordner löschen (für den nächsten Durchlauf)

Was fehlt:
  • CSV Import, der Links
  • CSV Export, des Ergebnisses
  • Automatisierung bzw. Deployment via DOCKER.
 
Zurück
Oben