Rumah  >  Artikel  >  pembangunan bahagian belakang  >  Masa untuk Pergi? Masa untuk Membina Semula! Membuat Twitter

Masa untuk Pergi? Masa untuk Membina Semula! Membuat Twitter

王林
王林asal
2024-08-12 22:34:351052semak imbas

Ciri paling kritikal rangkaian sosial baharu untuk pengguna yang bosan dengan Musk dan Twitter, adalah seperti berikut;

  • Import fail archive.zip Twitter
  • Semudah mungkin untuk mendaftar
  • Ciri pengguna yang serupa jika tidak serupa

Ciri platform yang kurang kritikal tetapi pasti membantu;

  • Dikewangkan dan disederhanakan secara beretika
  • Gunakan AI untuk membantu mengenal pasti kandungan bermasalah
  • Tandakan biru dengan penggunaan perkhidmatan identiti Onfido atau SMART

Dalam siaran ini, kami akan menumpukan pada ciri pertama. Mengimport fail archive.zip Twitter.

Fail itu

Twitter tidak menjadikan data anda begitu mudah untuk diperoleh. Sangat bagus bahawa mereka memberi anda akses kepadanya (secara sah, mereka perlu). Formatnya adalah omong kosong.

Ia sebenarnya datang sebagai arkib web mini dan semua data anda tersekat dalam fail JavaScript. Ia lebih kepada apl web berbanding penyimpanan data yang mudah.

Apabila anda membuka fail archive.html anda, anda mendapat sesuatu seperti ini;

Time to Leave? Time to Rebuild! Making Twitter

Nota: Saya membuat keputusan agak awal untuk membina menggunakan Next.js untuk tapak, Go dan GraphQL untuk bahagian belakang.

Jadi, apakah yang anda lakukan apabila data anda bukan data berstruktur?

Nah, anda menghuraikannya.

Mencipta skrip Go asas

Terus ke dokumen rasmi tentang cara memulakan Go, dan sediakan direktori projek anda.

Kami akan menggodam proses ini bersama-sama. Nampaknya salah satu ciri yang paling penting untuk menarik orang yang berasa terlalu terikat dengan TwitterX.

Langkah pertama ialah mencipta fail main.go. Dalam fail ini kita akan GO (hah) dan melakukan beberapa PERKARA;

  • os.Args: Ini ialah kepingan yang menyimpan hujah baris perintah.
  • os.Args[0] ialah nama atur cara dan os.Args[1] ialah hujah pertama yang dihantar kepada atur cara.
  • Semakan Argumen: Fungsi menyemak sama ada sekurang-kurangnya satu hujah disediakan. Jika tidak, ia mencetak mesej yang meminta laluan.
  • fungsi run: Fungsi ini hanya mencetak laluan yang dihantar kepadanya, buat masa ini.
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)
}

Pada setiap langkah, kami akan menjalankan fail seperti itu;

go run main.go twitter.zip

Jika anda tidak mempunyai eksport arkib Twitter, buat fail manifest.js mudah dan berikan JavaScript berikut.

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

Mampatkannya ke dalam fail twitter.zip anda yang akan kami gunakan sepanjang masa.

Baca fail Zip

Langkah seterusnya ialah membaca kandungan fail zip. Kami mahu melakukan ini secekap mungkin dan mengurangkan masa data diekstrak pada cakera.

Terdapat banyak fail dalam zip yang tidak perlu diekstrak juga.

Kami akan mengedit fail main.go;

  • Membuka fail ZIP: Fungsi zip.OpenReader() digunakan untuk membuka fail ZIP yang ditentukan oleh laluan.
  • Mengulang melalui fail: Fungsi menggelung pada setiap fail dalam arkib ZIP menggunakan r.File, iaitu sekeping zip.Fail. Sifat Nama setiap fail dicetak.
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)
}

JS sahaja! Kami sedang memburu data berstruktur

Fail arkib ini sangat tidak membantu. Kami ingin menyemak hanya fail .js dan hanya dalam direktori /data.

  • Membuka fail ZIP: Fail ZIP dibuka menggunakan zip.OpenReader().
  • Menyemak direktori /data: Atur cara bergerak melalui fail dalam arkib ZIP. Ia menggunakan rentetan.HasPrefix(f.Name, "data/") untuk menyemak sama ada fail berada dalam direktori /data.
  • Mencari fail .js: Program ini juga menyemak sama ada fail tersebut mempunyai sambungan .js menggunakan failpath.Ext(f.Name).
  • Membaca dan mencetak kandungan: Jika fail .js ditemui dalam direktori /data, atur cara membaca dan mencetak kandungannya.
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)
}

Menghuraikan JS! Kami mahu data itu

Kami telah menemui data berstruktur. Sekarang kita perlu menghuraikannya. Berita baiknya ialah terdapat pakej sedia ada untuk menggunakan JavaScript di dalam Go. Kami akan menggunakan goja.

Jika anda berada di bahagian ini, biasa dengan Goja, dan anda telah melihat output fail, anda mungkin melihat kami akan mengalami ralat pada masa hadapan kami.

Pasang goja:

go get github.com/dop251/goja

Sekarang kita akan mengedit fail main.go untuk melakukan perkara berikut;

  • Menghuraikan dengan goja: Fungsi goja.New() mencipta masa jalan JavaScript baharu dan vm.RunString(processedContents) menjalankan kod JavaScript yang diproses dalam masa jalan itu.
  • Kendalikan ralat dalam penghuraian
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)
}

KEJUTAN. tetingkap tidak ditakrifkan mungkin ralat biasa. Pada asasnya goja menjalankan masa jalan EMCA. tetingkap ialah konteks penyemak imbas dan malangnya tidak tersedia.

SEBENARNYA Parse JS

Saya telah melalui beberapa isu pada ketika ini. Termasuk tidak dapat mengembalikan data kerana ia adalah fail JS peringkat teratas.

Pendek cerita, kita perlu mengubah suai kandungan fail sebelum memuatkannya ke dalam masa jalan.

Jom ubah suai fail 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.

Atas ialah kandungan terperinci Masa untuk Pergi? Masa untuk Membina Semula! Membuat Twitter. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

Kenyataan:
Kandungan artikel ini disumbangkan secara sukarela oleh netizen, dan hak cipta adalah milik pengarang asal. Laman web ini tidak memikul tanggungjawab undang-undang yang sepadan. Jika anda menemui sebarang kandungan yang disyaki plagiarisme atau pelanggaran, sila hubungi admin@php.cn
Artikel sebelumnya:GORM, PostgreSQL & AtlasArtikel seterusnya:GORM, PostgreSQL & Atlas