Maison > Article > développement back-end > fuite de mémoire de la fonction de rendu HTML
Le problème auquel je suis confronté est que même en essayant seulement 200 requêtes, le programme occupe 6 Go de la mémoire du conteneur et finit par être tué par oom. Mon idée est d'extraire tous les nœuds de texte présents dans le HTML puis de les traiter pour extraire leur nom, le HTML et le texte de cette balise. Ainsi, pour générer du HTML pour une balise spécifique, j'utilise la fonction de rendu de golang.org/x/net/html. Où je fournis strings.builder en tant que io.writer pour écrire le code HTML généré. Mais pour une raison quelconque, le constructeur prend trop de mémoire.
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() }
Si vous souhaitez une liste spécifique d'URL, la voici. J'ai fait environ 60 demandes à la fois.
J'ai essayé d'utiliser bytes.buffer bytes.buffer
et sync.pool
mais les deux ont le même problème. En utilisant pprof
, j'ai remarqué que la méthode writestring de strings.builder provoquait une utilisation importante de la mémoire. <code>bytes.buffer
和 sync.pool
但两者都有相同的问题。使用 pprof
我注意到 strings.builder 的 writestring
方法导致大量内存使用。
所以这里的基本问题是接受任何 content-type
,这在抓取方面是不可接受的,大多数网站都需要发送 text/html
Le problème fondamental ici est donc d'accepter tout type de contenu
qui n'est pas acceptable en termes d'exploration, la plupart des sites Web ont tous besoin pour envoyer du text/html
.
golang.org/x/net/html
Le problème est que même si l'
tout ce qui ne représente pas des données HTML application/pdf
,然后正文将包含 html.Parse
, elle l'accepte toujours sans générer d'erreur.
Prenons un exemple où les données binaires du pdf analysé sont renvoyées et aucune erreur n'est renvoyée, il s'agit d'une bibliothèque de pensée au comportement étrange pour le scraping/crawl acceptant les données binaires.
🎜La solution est la suivante : 🎜Vérifiez les en-têtes de réponse, si seules les données sont en HTML, puis continuez, sinon il y aura une ambiguïté ou une utilisation de la mémoire plus élevée (peut-être inférieure), mais nous ne pouvons pas prédire ce qui se passera. 🎜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!