Heim >Backend-Entwicklung >Golang >Zeit zu gehen? Zeit zum Wiederaufbau! Twitter machen

Zeit zu gehen? Zeit zum Wiederaufbau! Twitter machen

王林
王林Original
2024-08-12 22:34:351089Durchsuche

Die wichtigsten Funktionen eines neuen sozialen Netzwerks für Benutzer, die Musk und Twitter satt haben, sind wie folgt:

  • Twitters archive.zip-Datei importieren
  • Die Anmeldung ist so einfach wie möglich
  • Ähnliche, wenn nicht identische Benutzerfunktionen

Weniger wichtige, aber auf jeden Fall hilfreiche Funktionen der Plattform;

  • Ethisch monetarisiert und moderiert
  • Nutzen Sie KI, um problematische Inhalte zu identifizieren
  • Blauer Haken bei Nutzung von Onfido- oder SMART-Identitätsdiensten

In diesem Beitrag konzentrieren wir uns auf die erste Funktion. Importieren der archive.zip-Datei von Twitter.

Die Datei

Twitter macht es nicht ganz einfach, an Ihre Daten zu kommen. Es ist großartig, dass sie Ihnen Zugriff darauf gewähren (gesetzlich gesehen müssen sie das). Das Format ist Mist.

Es handelt sich eigentlich um ein Mini-Webarchiv und alle Ihre Daten stecken in JavaScript-Dateien. Es handelt sich eher um eine Web-App als um eine praktische Datenspeicherung.

Wenn Sie die Datei „Ihr Archiv.html“ öffnen, erhalten Sie etwa Folgendes:

Time to Leave? Time to Rebuild! Making Twitter

Hinweis: Ich habe ziemlich früh die Entscheidung getroffen, Next.js für die Website, Go und GraphQL für das Backend zu verwenden.

Was tun Sie also, wenn es sich bei Ihren Daten nicht um strukturierte Daten handelt?

Nun, Sie analysieren es.

Erstellen eines einfachen Go-Skripts

Schauen Sie sich die offiziellen Dokumente zu den ersten Schritten mit Go an und richten Sie Ihr Projektverzeichnis ein.

Wir werden diesen Prozess gemeinsam meistern. Es scheint eine der wichtigsten Funktionen zu sein, um Leute anzulocken, die sich zu sehr mit TwitterX verbunden fühlen.

Der erste Schritt besteht darin, eine main.go-Datei zu erstellen. In dieser Datei werden wir (hah) ein paar Sachen machen;

  • os.Args: Dies ist ein Slice, das Befehlszeilenargumente enthält.
  • os.Args[0] ist der Name des Programms und os.Args[1] ist das erste Argument, das an das Programm übergeben wird.
  • Argumentprüfung: Die Funktion prüft, ob mindestens ein Argument bereitgestellt wird. Wenn nicht, wird eine Meldung ausgegeben, in der Sie nach einem Pfad gefragt werden.
  • Funktion ausführen: Diese Funktion gibt vorerst einfach den ihr übergebenen Pfad aus.
package main

import (
    "fmt"
    "os"
)

func run(path string) {
    fmt.Println("Path:", path)
}

func main() {
    if len(os.Args) < 2 {
        fmt.Println("Please provide a path as an argument.")
        return
    }

    path := os.Args[1]
    run(path)
}

Bei jedem Schritt führen wir die Datei wie folgt aus:

go run main.go twitter.zip

Wenn Sie keinen Twitter-Archivexport haben, erstellen Sie eine einfache manifest.js-Datei und geben Sie ihr das folgende JavaScript.

window.__THAR_CONFIG = {
  "userInfo" : {
    "accountId" : "1234567890",
    "userName" : "lukeocodes",
    "displayName" : "Luke ✨"
  },
};

Komprimieren Sie das in Ihre twitter.zip-Datei, die wir durchgehend verwenden werden.

Lesen Sie eine Zip-Datei

Der nächste Schritt besteht darin, den Inhalt der ZIP-Datei zu lesen. Wir möchten dies so effizient wie möglich tun und die Zeit zum Extrahieren der Daten auf der Festplatte verkürzen.

Es gibt viele Dateien in der ZIP-Datei, die auch nicht extrahiert werden müssen.

Wir bearbeiten die Datei main.go;

  • Öffnen der ZIP-Datei: Die Funktion zip.OpenReader() wird verwendet, um die durch den Pfad angegebene ZIP-Datei zu öffnen.
  • Iterieren durch die Dateien: Die Funktion durchläuft jede Datei im ZIP-Archiv mit r.File, einem Teil von zip.File. Die Name-Eigenschaft jeder Datei wird gedruckt.
package main

import (
    "archive/zip"
    "fmt"
    "log"
    "os"
)

func run(path string) {
    // Open the zip file
    r, err := zip.OpenReader(path)
    if err != nil {
        log.Fatal(err)
    }
    defer r.Close()

    // Iterate through the files in the zip archive
    fmt.Println("Files in the zip archive:")
    for _, f := range r.File {
        fmt.Println(f.Name)
    }
}

func main() {
    // Example usage
    if len(os.Args) < 2 {
        log.Fatal("Please provide the path to the zip file as an argument.")
    }

    path:= os.Args[1]
    run(path)
}

Nur JS! Wir sind auf der Suche nach strukturierten Daten

Diese Archivdatei ist wirklich nicht hilfreich. Wir möchten nur nach .js-Dateien suchen, und zwar nur im Verzeichnis /data.

  • Öffnen der ZIP-Datei: Die ZIP-Datei wird mit zip.OpenReader() geöffnet.
  • Überprüfung des /data-Verzeichnisses: Das Programm durchläuft die Dateien im ZIP-Archiv. Es verwendet strings.HasPrefix(f.Name, „data/“), um zu überprüfen, ob sich die Datei im Verzeichnis /data befindet.
  • Suchen nach .js-Dateien: Das Programm prüft auch, ob die Datei eine .js-Erweiterung hat, indem es filepath.Ext(f.Name) verwendet.
  • Inhalt lesen und drucken: Wenn eine .js-Datei im Verzeichnis /data gefunden wird, liest und druckt das Programm deren Inhalt.
package main

import (
    "archive/zip"
    "fmt"
    "io/ioutil"
    "log"
    "os"
    "path/filepath"
    "strings"
)

func readFile(file *zip.File) {
    // Open the file inside the zip
    rc, err := file.Open()
    if err != nil {
        log.Fatal(err)
    }
    defer rc.Close()

    // Read the contents of the file
    contents, err := ioutil.ReadAll(rc) // deprecated? :/ 
    if err != nil {
        log.Fatal(err)
    }

    // Print the contents
    fmt.Printf("Contents of %s:\n", file.Name)
    fmt.Println(string(contents))
}

func run(path string) {
    // Open the zip file
    r, err := zip.OpenReader(path)
    if err != nil {
        log.Fatal(err)
    }
    defer r.Close()

    // Iterate through the files in the zip archive
    fmt.Println("JavaScript files in the zip archive:")
    for _, f := range r.File {
        // Use filepath.Ext to check the file extension
        if strings.HasPrefix(f.Name, "data/") && strings.ToLower(filepath.Ext(f.Name)) == ".js" {
            readFile(f)
            return // Exit after processing the first .js file so we don't end up printing a gazillion lines when testing
        }
    }
}

func main() {
    // Example usage
    if len(os.Args) < 2 {
        log.Fatal("Please provide the path to the zip file as an argument.")
    }

    path:= os.Args[1]
    run(path)
}

Analysieren Sie den JS! Wir wollen diese Daten

Wir haben die strukturierten Daten gefunden. Jetzt müssen wir es analysieren. Die gute Nachricht ist, dass es bereits Pakete für die Verwendung von JavaScript in Go gibt. Wir werden Goja verwenden.

Wenn Sie sich in diesem Abschnitt befinden, mit Goja vertraut sind und die Ausgabe der Datei gesehen haben, werden Sie möglicherweise feststellen, dass wir in Zukunft Fehler haben werden.

Goja installieren:

go get github.com/dop251/goja

Jetzt bearbeiten wir die Datei main.go, um Folgendes zu tun:

  • Parsing mit goja: Die Funktion goja.New() erstellt eine neue JavaScript-Laufzeit und vm.RunString(processedContents) führt den verarbeiteten JavaScript-Code innerhalb dieser Laufzeit aus.
  • Behandeln Sie Fehler beim Parsen
package main

import (
    "archive/zip"
    "fmt"
    "io/ioutil"
    "log"
    "os"
    "path/filepath"
    "strings"
)

func readFile(file *zip.File) {
    // Open the file inside the zip
    rc, err := file.Open()
    if err != nil {
        log.Fatal(err)
    }
    defer rc.Close()

    // Read the contents of the file
    contents, err := ioutil.ReadAll(rc) // deprecated? :/ 
    if err != nil {
        log.Fatal(err)
    }

    // Parse the JavaScript file using goja
    vm := goja.New()
    _, err = vm.RunString(contents)
    if err != nil {
        log.Fatalf("Error parsing JS file: %v", err)
    }

    fmt.Printf("Parsed JavaScript file: %s\n", file.Name)
}

func run(path string) {
    // Open the zip file
    r, err := zip.OpenReader(path)
    if err != nil {
        log.Fatal(err)
    }
    defer r.Close()

    // Iterate through the files in the zip archive
    fmt.Println("JavaScript files in the zip archive:")
    for _, f := range r.File {
        // Use filepath.Ext to check the file extension
        if strings.HasPrefix(f.Name, "data/") && strings.ToLower(filepath.Ext(f.Name)) == ".js" {
            readFile(f)
            return // Exit after processing the first .js file so we don't end up printing a gazillion lines when testing
        }
    }
}

func main() {
    // Example usage
    if len(os.Args) < 2 {
        log.Fatal("Please provide the path to the zip file as an argument.")
    }

    path:= os.Args[1]
    run(path)
}

ÜBERRASCHUNG. „Fenster ist nicht definiert“ könnte ein bekannter Fehler sein. Grundsätzlich führt Goja eine EMCA-Laufzeit aus. Fenster ist Browserkontext und leider nicht verfügbar.

Analysieren Sie tatsächlich den JS

Zu diesem Zeitpunkt hatte ich einige Probleme. Einschließlich der Tatsache, dass keine Daten zurückgegeben werden können, da es sich um eine JS-Datei der obersten Ebene handelt.

Kurz gesagt, wir müssen den Inhalt der Dateien ändern, bevor wir sie in die Laufzeit laden.

Ändern wir die Datei main.go;

  • reConfig: A regex that matches any assignment of the form window.someVariable = { and replaces it with var data = {.
  • reArray: A regex that matches any assignment of the form window.someObject.someArray = [ and replaces it with var data = [
  • Extracting data: Running the script, we use vm.Get("data") to retrieve the value of the data variable from the JavaScript context.
package main

import (
    "archive/zip"
    "fmt"
    "io/ioutil"
    "log"
    "os"
    "path/filepath"
    "regexp"
    "strings"

    "github.com/dop251/goja"
)

func readFile(file *zip.File) {
    // Open the file inside the zip
    rc, err := file.Open()
    if err != nil {
        log.Fatal(err)
    }
    defer rc.Close()

    // Read the contents of the file
    contents, err := ioutil.ReadAll(rc)
    if err != nil {
        log.Fatal(err)
    }

    // Regular expressions to replace specific patterns
    reConfig := regexp.MustCompile(`window\.\w+\s*=\s*{`)
    reArray := regexp.MustCompile(`window\.\w+\.\w+\.\w+\s*=\s*\[`)

    // Replace patterns in the content
    processedContents := reConfig.ReplaceAllStringFunc(string(contents), func(s string) string {
        return "var data = {"
    })
    processedContents = reArray.ReplaceAllStringFunc(processedContents, func(s string) string {
        return "var data = ["
    })

    // Parse the JavaScript file using goja
    vm := goja.New()
    _, err = vm.RunString(processedContents)
    if err != nil {
        log.Fatalf("Error parsing JS file: %v", err)
    }

    // Retrieve the value of the 'data' variable from the JavaScript context
    value := vm.Get("data")
    if value == nil {
        log.Fatalf("No data variable found in the JS file")
    }

    // Output the parsed data
    fmt.Printf("Processed JavaScript file: %s\n", file.Name)
    fmt.Printf("Data extracted: %v\n", value.Export())
}

func run(path string) {
    // Open the zip file
    r, err := zip.OpenReader(path)
    if err != nil {
        log.Fatal(err)
    }
    defer r.Close()

    // Iterate through the files in the zip archive
    for _, f := range r.File {
        // Check if the file is in the /data directory and has a .js extension
        if strings.HasPrefix(f.Name, "data/") && strings.ToLower(filepath.Ext(f.Name)) == ".js" {
            readFile(f)
            return // Exit after processing the first .js file so we don't end up printing a gazillion lines when testing
        }
    }
}

func main() {
    // Example usage
    if len(os.Args) < 2 {
        log.Fatal("Please provide the path to the zip file as an argument.")
    }

    path:= os.Args[1]
    run(path)
}

Hurrah. Assuming I didn't muck up the copypaste into this post, you should now see a rather ugly print of the struct data from Go.

JSON would be nice

Edit the main.go file to marshall the JSON output.

  • Use value.Export() to get the data from the struct
  • Use json.MarshallIndent() for pretty printed JSON (use json.Marshall if you want to minify the output).
package main

import (
    "archive/zip"
    "encoding/json"
    "fmt"
    "io/ioutil"
    "log"
    "os"
    "path/filepath"
    "regexp"
    "strings"

    "github.com/dop251/goja"
)

func readFile(file *zip.File) {
    // Open the file inside the zip
    rc, err := file.Open()
    if err != nil {
        log.Fatal(err)
    }
    defer rc.Close()

    // Read the contents of the file
    contents, err := ioutil.ReadAll(rc) // deprecated :/
    if err != nil {
        log.Fatal(err)
    }

    // Regular expressions to replace specific patterns
    reConfig := regexp.MustCompile(`window\.\w+\s*=\s*{`)
    reArray := regexp.MustCompile(`window\.\w+\.\w+\.\w+\s*=\s*\[`)

    // Replace patterns in the content
    processedContents := reConfig.ReplaceAllStringFunc(string(contents), func(s string) string {
        return "var data = {"
    })
    processedContents = reArray.ReplaceAllStringFunc(processedContents, func(s string) string {
        return "var data = ["
    })

    // Parse the JavaScript file using goja
    vm := goja.New()
    _, err = vm.RunString(processedContents)
    if err != nil {
        log.Fatalf("Error parsing JS file: %v", err)
    }

    // Retrieve the value of the 'data' variable from the JavaScript context
    value := vm.Get("data")
    if value == nil {
        log.Fatalf("No data variable found in the JS file")
    }

    // Convert the data to a Go-native type
    data := value.Export()

    // Marshal the Go-native type to JSON
    jsonData, err := json.MarshalIndent(data, "", "  ")
    if err != nil {
        log.Fatalf("Error marshalling data to JSON: %v", err)
    }

    // Output the JSON data
    fmt.Println(string(jsonData))
}

func run(zipFilePath string) {
    // Open the zip file
    r, err := zip.OpenReader(zipFilePath)
    if err != nil {
        log.Fatal(err)
    }
    defer r.Close()

    // Iterate through the files in the zip archive
    for _, f := range r.File {
        // Check if the file is in the /data directory and has a .js extension
        if strings.HasPrefix(f.Name, "data/") && strings.ToLower(filepath.Ext(f.Name)) == ".js" {
            readFile(f)
            return // Exit after processing the first .js file
        }
    }
}

func main() {
    // Example usage
    if len(os.Args) < 2 {
        log.Fatal("Please provide the path to the zip file as an argument.")
    }

    zipFilePath := os.Args[1]
    run(zipFilePath)
}

That's it!

go run main.go twitter.zip
}
  "userInfo": {
    "accountId": "1234567890",
    "displayName": "Luke ✨",
    "userName": "lukeocodes"
  }
}

Open source

I'll be open sourcing a lot of this work so that others who want to parse the data from the archive, can store it how they like.

Das obige ist der detaillierte Inhalt vonZeit zu gehen? Zeit zum Wiederaufbau! Twitter machen. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn
Vorheriger Artikel:GORM, PostgreSQL und AtlasNächster Artikel:GORM, PostgreSQL und Atlas