对于厌倦了马斯克和 Twitter 的用户来说,新社交网络最关键的功能如下;
平台不太重要但绝对有用的功能;
在这篇文章中,我们将重点关注第一个功能。导入 Twitter 的 archive.zip 文件。
Twitter 并没有让你的数据变得那么容易获取。很高兴他们允许您访问它(从法律上讲,他们必须这样做)。格式太垃圾了
它实际上是一个迷你网络存档,您的所有数据都保存在 JavaScript 文件中。它更像是一个网络应用程序,而不是方便的数据存储。
当您打开您的 archive.html 文件时,您会看到类似这样的内容;
注意:我很早就决定使用 Next.js 构建网站,使用 Go 和 GraphQL 构建后端。
那么,当您的数据不是结构化数据时该怎么办?
好吧,你解析一下。
前往官方文档了解如何开始使用 Go,并设置您的项目目录。
我们将一起破解这个过程。这似乎是吸引那些过于依赖 TwitterX 的人的最重要功能之一。
第一步是创建一个 main.go 文件。在这个文件中,我们将继续(哈哈)并做一些事情;
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) }
在每一步,我们都会像这样运行文件;
go run main.go twitter.zip
如果您没有 Twitter 存档导出,请创建一个简单的 manifest.js 文件并为其提供以下 JavaScript。
window.__THAR_CONFIG = { "userInfo" : { "accountId" : "1234567890", "userName" : "lukeocodes", "displayName" : "Luke ✨" }, };
将其压缩到我们将在整个过程中使用的 twitter.zip 文件中。
下一步是读取 zip 文件的内容。我们希望尽可能高效地做到这一点,并减少在磁盘上提取数据的时间。
zip中有很多文件也不需要解压。
我们将编辑 main.go 文件;
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) }
这个存档文件非常没有帮助。我们只想检查 /data 目录中的 .js 文件。
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) }
我们找到了结构化数据。现在我们需要解析它。好消息是,已经有在 Go 中使用 JavaScript 的现有包。我们将使用 goja。
如果您正在阅读本节,熟悉 Goja,并且已经看过该文件的输出,您可能会发现我们将来会出现错误。
安装goja:
go get github.com/dop251/goja
现在我们将编辑 main.go 文件以执行以下操作;
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) }
惊喜。窗口未定义可能是一个熟悉的错误。基本上 goja 运行 EMCA 运行时。窗口是浏览器上下文,遗憾的是不可用。
此时我遇到了一些问题。包括无法返回数据,因为它是顶级 JS 文件。
长话短说,我们需要在将文件加载到运行时之前修改它们的内容。
我们来修改main.go文件;
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.
Edit the main.go file to marshall the JSON 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" } }
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.
以上是是时候离开了吗?重建的时间到了!制作推特的详细内容。更多信息请关注PHP中文网其他相关文章!