**我面临的问题是,即使只尝试200个请求也会导致程序吃掉容器的6GB内存,最终被OOM杀死。**想法是我提取HTML中存在的所有文本节点,然后处理它们以提取它们的名称,该标签的HTML和文本。因此,为了生成特定标签的html,我使用了www.example.com的Render函数golang.org/x/net/html。在该函数中,我提供了strings.Builder作为io.Writer来编写生成的html。但是由于某种原因,builder占用了太多的内存。
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()
}
字符串
如果你想要特定URL列表,这里是。我一次发出大约60个请求。
我尝试了bytes.Buffer
和sync.Pool
使用bytes.Buffer,但两者都有相同的问题。使用pprof
我注意到strings.Builder's WriteString
方法导致了巨大的内存使用。
1条答案
按热度按时间ddrv8njm1#
因此,这里的基本问题是接受任何
content-type
,这是不可接受的,因为大多数网站都需要发送text/html
。问题是,即使url发送任何不代表html数据
golang.org/x/net/html
的内容,仍然接受它而不会抛出错误。让我们举一个例子,其中
application/pdf
被返回,然后主体将包含pdf的二进制数据,html.Parse
解析并不返回任何错误,这是一个奇怪的行为,思考库为抓取/抓取接受二进制数据而做的。**解决方案是:**检查响应头,然后继续,如果只有数据是html,否则会有歧义或更高的内存使用(可能更低),但我们无法预测会发生什么。