Maison > développement back-end > Golang > le corps du texte

Il est temps de partir ? Il est temps de reconstruire ! Créer Twitter

王林
Libérer: 2024-08-12 22:34:35
original
1025 Les gens l'ont consulté

Les fonctionnalités les plus critiques d'un nouveau réseau social pour les utilisateurs qui en ont assez de Musk et de Twitter sont les suivantes :

  • Importer le fichier archive.zip de Twitter
  • Inscription aussi simple que possible
  • Fonctionnalités utilisateur similaires sinon identiques

Fonctionnalités moins critiques mais certainement utiles de la plateforme ;

  • Monétisé et modéré de manière éthique
  • Utilisez l'IA pour identifier les contenus problématiques
  • Coche bleue avec l'utilisation des services d'identité Onfido ou SMART

Dans cet article, nous nous concentrerons sur la première fonctionnalité. Importation du fichier archive.zip de Twitter.

Le dossier

Twitter n'a pas rendu vos données aussi faciles à obtenir. C'est génial qu'ils vous y donnent accès (légalement, ils le doivent). Le format est nul.

Il s'agit en fait d'une mini archive Web et toutes vos données sont bloquées dans des fichiers JavaScript. Il s'agit plus d'une application Web que d'un stockage pratique de données.

Lorsque vous ouvrez le fichier Your archive.html, vous obtenez quelque chose comme ceci ;

Time to Leave? Time to Rebuild! Making Twitter

Remarque : j'ai pris la décision assez tôt de construire en utilisant Next.js pour le site, Go et GraphQL pour le backend.

Alors, que faites-vous lorsque vos données ne sont pas des données structurées ?

Eh bien, vous l'analysez.

Création d'un script Go de base

Rendez-vous à la documentation officielle pour savoir comment démarrer avec Go et configurer le répertoire de votre projet.

Nous allons pirater ce processus ensemble. Cela semble être l'une des fonctionnalités les plus importantes pour attirer les personnes qui se sentent trop attachées à TwitterX.

La première étape consiste à créer un fichier main.go. Dans ce fichier, nous allons ALLER (hah) et faire quelques trucs ;

  • os.Args : il s'agit d'une tranche qui contient les arguments de ligne de commande.
  • os.Args[0] est le nom du programme et os.Args[1] est le premier argument transmis au programme.
  • Vérification des arguments : la fonction vérifie si au moins un argument est fourni. Sinon, il imprime un message demandant un chemin.
  • fonction run : cette fonction imprime simplement le chemin qui lui est transmis, pour l'instant.
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)
}
Copier après la connexion

À chaque étape, nous exécuterons le fichier comme ceci ;

go run main.go twitter.zip
Copier après la connexion
Copier après la connexion

Si vous n'avez pas d'exportation d'archive Twitter, créez un simple fichier manifest.js et donnez-lui le JavaScript suivant.

window.__THAR_CONFIG = {
  "userInfo" : {
    "accountId" : "1234567890",
    "userName" : "lukeocodes",
    "displayName" : "Luke ✨"
  },
};
Copier après la connexion

Compressez-le dans votre fichier twitter.zip que nous utiliserons tout au long.

Lire un fichier Zip

L'étape suivante consiste à lire le contenu du fichier zip. Nous voulons le faire aussi efficacement que possible et réduire le temps d'extraction des données sur le disque.

Il y a de nombreux fichiers dans le zip qui n'ont pas non plus besoin d'être extraits.

Nous modifierons le fichier main.go ;

  • Ouverture du fichier ZIP : La fonction zip.OpenReader() permet d'ouvrir le fichier ZIP spécifié par chemin.
  • Itération dans les fichiers : la fonction parcourt chaque fichier de l'archive ZIP à l'aide de r.File, qui est une tranche de zip.File. La propriété Name de chaque fichier est imprimée.
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)
}
Copier après la connexion

JS uniquement ! Nous recherchons des données structurées

Ce fichier d'archive est vraiment inutile. Nous voulons vérifier uniquement les fichiers .js, et uniquement dans le répertoire /data.

  • Ouverture du fichier ZIP : Le fichier ZIP s'ouvre à l'aide de zip.OpenReader().
  • Vérification du répertoire /data : Le programme parcourt les fichiers de l'archive ZIP. Il utilise strings.HasPrefix(f.Name, "data/") pour vérifier si le fichier réside dans le répertoire /data.
  • Recherche de fichiers .js : le programme vérifie également si le fichier a une extension .js en utilisant filepath.Ext(f.Name).
  • Lecture et impression du contenu : Si un fichier .js est trouvé dans le répertoire /data, le programme lit et imprime son contenu.
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)
}
Copier après la connexion

Analysez le JS ! Nous voulons ces données

Nous avons trouvé les données structurées. Nous devons maintenant l'analyser. La bonne nouvelle est qu’il existe des packages permettant d’utiliser JavaScript dans Go. Nous utiliserons du goja.

Si vous êtes dans cette section, familier avec Goja, et que vous avez vu la sortie du fichier, vous verrez peut-être que nous aurons des erreurs dans notre futur.

Installer goja :

go get github.com/dop251/goja
Copier après la connexion

Nous allons maintenant modifier le fichier main.go pour effectuer les opérations suivantes :

  • Analyse avec goja : la fonction goja.New() crée un nouveau runtime JavaScript et vm.RunString(processedContents) exécute le code JavaScript traité dans ce runtime.
  • Gérer les erreurs d'analyse
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)
}
Copier après la connexion

SUPRISE. la fenêtre n'est pas définie peut être une erreur familière. Fondamentalement, goja exécute un runtime EMCA. la fenêtre est le contexte du navigateur et malheureusement indisponible.

Réellement analyser le JS

J'ai rencontré quelques problèmes à ce stade. Y compris l'impossibilité de renvoyer des données car il s'agit d'un fichier JS de premier niveau.

Pour faire court, nous devons modifier le contenu des fichiers avant de les charger dans le runtime.

Modifions le fichier 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)
}
Copier après la connexion

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)
}
Copier après la connexion

That's it!

go run main.go twitter.zip
Copier après la connexion
Copier après la connexion
}
  "userInfo": {
    "accountId": "1234567890",
    "displayName": "Luke ✨",
    "userName": "lukeocodes"
  }
}
Copier après la connexion

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.

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

source:dev.to
Déclaration de ce site Web
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn
Tutoriels populaires
Plus>
Derniers téléchargements
Plus>
effets Web
Code source du site Web
Matériel du site Web
Modèle frontal
À propos de nous Clause de non-responsabilité Sitemap
Site Web PHP chinois:Formation PHP en ligne sur le bien-être public,Aidez les apprenants PHP à grandir rapidement!