Heim >Backend-Entwicklung >Golang >Speicherverlust der HTML-Rendering-Funktion
Das Problem, mit dem ich konfrontiert bin, besteht darin, dass selbst der Versuch von nur 200 Anfragen dazu führt, dass das Programm 6 GB des Speichers des Containers belegt und schließlich von oom beendet wird. Meine Idee ist, alle im HTML vorhandenen Textknoten zu extrahieren und sie dann zu verarbeiten, um ihren Namen, den HTML-Code und den Text dieses Tags zu extrahieren. Um also HTML für ein bestimmtes Tag zu generieren, verwende ich die Renderfunktion von golang.org/x/net/html. Wo ich strings.builder als io.writer bereitstelle, um den generierten HTML-Code zu schreiben. Aber aus irgendeinem Grund beansprucht der Builder zu viel Speicher.
package main import ( "encoding/csv" "io" "log" "net/http" "strings" "golang.org/x/net/html" ) func main() { mux := http.NewServeMux() mux.HandleFunc("/data", GetData) if err := http.ListenAndServe(":8001", mux); err != nil { log.Println(err) } } type TagInfo struct { Tag string Name string Text string } // http.handler func GetData(w http.ResponseWriter, r *http.Request) { u := r.URL.Query().Get("url") doc, err := GetDoc(u) if err != nil { log.Println(err) w.WriteHeader(500) return } var buf strings.Builder data := Extract(doc, &buf) csvw := csv.NewWriter(io.Discard) for _, d := range data { csvw.Write([]string{d.Name, d.Tag, d.Text}) } } // fires request and get text/html func GetDoc(u string) (*html.Node, error) { res, err := http.Get(u) if err != nil { return nil, err } defer res.Body.Close() return html.Parse(res.Body) } func Extract(doc *html.Node, buf *strings.Builder) []TagInfo { var ( tags = make([]TagInfo, 0, 100) f func(*html.Node) ) f = func(n *html.Node) { if n.Type == html.TextNode { text := strings.TrimSpace(n.Data) if text != "" { parent := n.Parent tag := Render(parent, buf) tagInfo := TagInfo{ Tag: tag, Name: parent.Data, Text: n.Data, } tags = append(tags, tagInfo) } } for child := n.FirstChild; child != nil; child = child.NextSibling { f(child) } } f(doc) return tags } // Render the html around the tag // if node is text then pass the // parent node paramter in function func Render(n *html.Node, buf *strings.Builder) string { defer buf.Reset() if err := html.Render(buf, n); err != nil { log.Println(err) return "" } return buf.String() }
Wenn Sie eine bestimmte Liste von URLs wünschen, finden Sie sie hier. Ich habe ungefähr 60 Anfragen gleichzeitig gestellt.
Ich habe versucht, bytes.buffer bytes.buffer
und sync.pool
zu verwenden, aber beide haben das gleiche Problem. Bei der Verwendung von pprof
ist mir aufgefallen, dass die writestring-Methode von strings.builder viel Speicher verbraucht. <code>bytes.buffer
和 sync.pool
但两者都有相同的问题。使用 pprof
我注意到 strings.builder 的 writestring
方法导致大量内存使用。
所以这里的基本问题是接受任何 content-type
,这在抓取方面是不可接受的,大多数网站都需要发送 text/html
Das Grundproblem hier besteht also darin, jeden Inhaltstyp
zu akzeptieren, der im Hinblick auf das Crawlen nicht akzeptabel ist, was die meisten Websites benötigen um text/html
zu senden.
golang.org/x/net/html
Das Problem besteht darin, dass die
alles sendet, was keine HTML-Daten darstellt application/pdf
,然后正文将包含 html.Parse
, diese dennoch akzeptiert, ohne einen Fehler auszulösen.
Nehmen wir ein Beispiel, bei dem die Binärdaten der analysierten PDF-Datei zurückgegeben werden und kein Fehler zurückgegeben wird. Dies ist eine seltsame Verhaltensgedankenbibliothek zum Scrapen/Crawlen, die Binärdaten akzeptiert.
🎜Die Lösung lautet: 🎜Überprüfen Sie die Antwortheader. Wenn nur die Daten HTML sind, fahren Sie fort, andernfalls kommt es zu Mehrdeutigkeiten oder einer höheren Speichernutzung (möglicherweise weniger), aber wir können nicht vorhersagen, was passieren wird. 🎜Das obige ist der detaillierte Inhalt vonSpeicherverlust der HTML-Rendering-Funktion. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!