摘要:即是發(fā)送請求的客戶端,請求的執(zhí)行都是由發(fā)起。響應(yīng)信息執(zhí)行請求成功,如何查看響應(yīng)信息。提交文件文件提交應(yīng)該是請求中較為復(fù)雜的內(nèi)容了。主要涉及兩部分內(nèi)容,即讀取響應(yīng)的與設(shè)置請求的。的包中請求是不重定向的,但測試結(jié)果顯示的是自動重定向的。
前幾天在 "知乎想法" 談到了一個話題,如何模仿學(xué)習(xí),舉了通過 net/http client 模仿 Pyhton 的requests的例子。但并未實(shí)踐,難道想法真的只能是想法嗎?當(dāng)然不是,于是我決定先暫停一周 GO 筆記,來實(shí)踐下自己的想法。
有些新的知識,我們可以通過模仿學(xué)習(xí)
本文將通過 GO 實(shí)現(xiàn) requests 的 quick start 文檔中的所有例子,系統(tǒng)學(xué)習(xí)http client的使用。雖然標(biāo)題是 quick start,但其實(shí)內(nèi)容挺多的。
快速體驗首先,我們來發(fā)起一個 GET 請求,代碼非常簡單。如下:
func get() { r, err := http.Get("https://api.github.com/events") if err != nil { panic(err) } defer func() { _ = r.Body.Close() }() body, _ := ioutil.ReadAll(r.Body) fmt.Printf("%s", body) }
通過 http.Get 方法,獲取到了一個 Response 和一個 error ,即 r 和 err。通過 r 我們能獲取響應(yīng)的信息,err 可以實(shí)現(xiàn)錯誤檢查。
r.Body 被讀取后需要關(guān)閉,可以defer來做這件事。內(nèi)容的讀取可通過 ioutil.ReadAll實(shí)現(xiàn)。
請求方法除了GET,HTTP還有其他一系列方法,包括POST、PUT、DELETE、HEAD、OPTIONS。快速體驗中的GET是通過一種便捷的方式實(shí)現(xiàn)的,它隱藏了很多細(xì)節(jié)。這里暫時先不用它。
我們先來介紹通用的方法,以幫我們實(shí)現(xiàn)所有HTTP方法的請求。主要涉及兩個重要的類型,Client 和 Request。
Client 即是發(fā)送 HTTP 請求的客戶端,請求的執(zhí)行都是由 Client 發(fā)起。它提供了一些便利的請求方法,比如我們要發(fā)起一個Get請求,可通過 client.Get(url) 實(shí)現(xiàn)。更通用的方式是通過 client.Do(req) 實(shí)現(xiàn),req 屬于 Request 類型。
Request 是用來描述請求信息的結(jié)構(gòu)體,比如請求方法、地址、頭部等信息,我們都可以通過它來設(shè)置。Request 的創(chuàng)建可以通過 http.NewRequest 實(shí)現(xiàn)。
接下來列舉 HTTP 所有方法的實(shí)現(xiàn)代碼。
GET
r, err := http.DefaultClient.Do( http.NewRequest(http.MethodGet, "https://api.github.com/events", nil))
POST
r, err := http.DefaultClient.Do( http.NewRequest(http.MethodPost, "http://httpbin.org/post", nil))
PUT
r, err := http.DefaultClient.Do( http.NewRequest(http.MethodPut, "http://httpbin.org/put", nil))
DELETE
r, err := http.DefaultClient.Do( http.NewRequest(http.MethodDelete, "http://httpbin.org/delete", nil))
HEAD
r, err := http.DefaultClient.Do( http.NewRequest(http.MethodHead, "http://httpbin.org/get", nil))
OPTIONS
r, err := http.DefaultClient.Do( http.NewRequest(http.MethodOptions, "http://httpbin.org/get", nil))
上面展示了HTTP所有方法的實(shí)現(xiàn)。這里還幾點(diǎn)需要說明。
DefaultClient,它是 net/http 包提供了默認(rèn)客戶端,一般的請求我們無需創(chuàng)建新的 Client,使用默認(rèn)即可。
GET、POST 和 HEAD 的請求,GO提供了更便捷的實(shí)現(xiàn)方式,Request 不用手動創(chuàng)建。
示例代碼,每個 HTTP 請求方法都有兩種實(shí)現(xiàn)。
GET
r, err := http.DefaultClient.Get("http://httpbin.org/get") r, err := http.Get("http://httpbin.org/get")
POST
bodyJson, _ := json.Marshal(map[string]interface{}{ "key": "value", }) r, err := http.DefaultClient.Post( "http://httpbin.org/post", "application/json", strings.NewReader(string(bodyJson)), ) r, err := http.Post( "http://httpbin.org/post", "application/json", strings.NewReader(string(bodyJson)), )
這里順便演示了如何向 POST 接口提交 JSON 數(shù)據(jù)的方式,主要 content-type 的設(shè)置,一般JSON接口的 content-type 為 application/json。
HEAD
r, err := http.DefaultClient.Head("http://httpbin.org/get") r, err := http.Head("http://httpbin.org/get")
如果看了源碼,你會發(fā)現(xiàn),http.Get 中調(diào)用就是 http.DefaultClient.Get,是同一個意思,只是為了方便,提供這種調(diào)用方法。Head 和 Post 也是如此。
URL參數(shù)通過將鍵/值對置于 URL 中,我們可以實(shí)現(xiàn)向特定地址傳遞數(shù)據(jù)。該鍵/值將跟在一個問號的后面,例如 http://httpbin.org/get?key=val。 手工構(gòu)建 URL 會比較麻煩,我們可以通過 net/http 提供的方法來實(shí)現(xiàn)。
舉個栗子,比如你想傳遞 key1=value1 和 key2=value2 到 http://httpbin.org/get。代碼如下:
req, err := http.NewRequest(http.MethodGet, "http://httpbin.org/get", nil) if err != nil { panic(err) } params := make(url.Values) params.Add("key1", "value1") params.Add("key2", "value2") req.URL.RawQuery = params.Encode() // URL 的具體情況 http://httpbin.org/get?key1=value1&key2=value2 // fmt.Println(req.URL.String()) r, err := http.DefaultClient.Do(req)
url.Values 可以幫助組織 QueryString,查看源碼發(fā)現(xiàn) url.Values 其實(shí)是 map[string][]string。調(diào)用 Encode 方法,將組織的字符串傳遞給請求 req 的 RawQuery。通過 url.Values也可以設(shè)置一個數(shù)組參數(shù),類似如下的形式:
http://httpbin.org/get?key1=v...
怎么做呢?
params := make(url.Values) params.Add("key1", "value1") params.Add("key2", "value2") params.Add("key2", "value3")
觀察最后一行代碼。其實(shí),只要在 key2 上再增加一個值就可以了。
響應(yīng)信息執(zhí)行請求成功,如何查看響應(yīng)信息。要查看響應(yīng)信息,可以大概了解下,響應(yīng)通常哪些內(nèi)容?常見的有主體內(nèi)容(Body)、狀態(tài)信息(Status)、響應(yīng)頭部(Header)、內(nèi)容編碼(Encoding)等。
Body其實(shí),在最開始的時候已經(jīng)演示Body讀取的過程。響應(yīng)內(nèi)容的讀取可通過 ioutil 實(shí)現(xiàn)。
body, err := ioutil.ReadAll(r.Body)
響應(yīng)內(nèi)容多樣,如果是 json,可以直接使用 json.Unmarshal 進(jìn)行解碼,JSON知識不介紹了。
r.Body 實(shí)現(xiàn)了 io.ReadeCloser 接口,為減少資源浪費(fèi)要及時釋放,可以通過 defer 實(shí)現(xiàn)。
defer func() { _ = r.Body.Close() }()StatusCode
響應(yīng)信息中,除了 Body 主體內(nèi)容,還有其他信息,比如 status code 和 charset 等。
r.StatusCode r.Status
r.StatusCode 是 HTTP 返回碼,Status 是返回狀態(tài)描述。
Header響應(yīng)頭信息通過 Response.Header 即可獲取,要說明的一點(diǎn)是,響應(yīng)頭的 Key 是不區(qū)分大小寫。
r.Header.Get("content-type") r.Header.Get("Content-Type")
你會發(fā)現(xiàn) content-type 和 Content-Type 獲取的內(nèi)容是完全一樣的。
Encoding如何識別響應(yīng)內(nèi)容編碼呢?我們需要借助 http://golang.org/x/net/html/... 包實(shí)現(xiàn)。先來定義一個函數(shù),代碼如下:
func determineEncoding(r *bufio.Reader) encoding.Encoding { bytes, err := r.Peek(1024) if err != nil { fmt.Printf("err %v", err) return unicode.UTF8 } e, _, _ := charset.DetermineEncoding(bytes, "") return e }
怎么調(diào)用它?
bodyReader := bufio.NewReader(r.Body) e := determineEncoding(bodyReader) fmt.Printf("Encoding %v ", e) decodeReader := transform.NewReader(bodyReader, e.NewDecoder())
利用 bufio 生成新的 reader,然后利用 determineEncoding 檢測內(nèi)容編碼,并通過 transform 進(jìn)行編碼轉(zhuǎn)化。
圖片下載如果訪問內(nèi)容是一張圖片,我們?nèi)绾伟阉螺d下來呢?比如如下地址的圖片。
https://pic2.zhimg.com/v2-5e8...
其實(shí)很簡單,只需要創(chuàng)建新的文件并把響應(yīng)內(nèi)容保存進(jìn)去即可。
f, err := os.Create("as.jpg") if err != nil { panic(err) } defer func() { _ = f.Close() }() _, err = io.Copy(f, r.Body) if err != nil { panic(err) }
r 即 Response,利用 os 創(chuàng)建了新的文件,然后再通過 io.Copy 將響應(yīng)的內(nèi)容保存進(jìn)文件中。
定制請求頭如何為請求定制請求頭呢?Request 其實(shí)已經(jīng)提供了相應(yīng)的方法,通過 req.Header.Add 即可完成。
舉個例子,假設(shè)我們將要訪問 http://httpbin.org/get,但這個地址針對 user-agent 設(shè)置了發(fā)爬策略。我們需要修改默認(rèn)的 user-agent。
示例代碼:
req, err := http.NewRequest(http.MethodGet, "http://httpbin.org/get", nil) if err != nil { panic(err) } req.Header.Add("user-agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_0)")
如上便可完成任務(wù)。
復(fù)雜的POST請求前面已經(jīng)展示過了向 POST 接口提交 JSON 數(shù)據(jù)的方式。接下來介紹下另外幾種向 POST 接口提交數(shù)據(jù)的方式,即表單提交和文件提交。
表單提交表單提交是一個很常用的功能,故而在 net/http 中,除了提供標(biāo)準(zhǔn)的用法外,還給我們提供了簡化的方法。
我們先來介紹個標(biāo)準(zhǔn)的實(shí)現(xiàn)方法。
舉個例子,假設(shè)要向 http://httpbin.org/post 提交 name 為 poloxue 和 password 為 123456 的表單。
payload := make(url.Values) payload.Add("name", "poloxue") payload.Add("password", "123456") req, err := http.NewRequest( http.MethodPost, "http://httpbin.org/post", strings.NewReader(payload.Encode()), ) if err != nil { panic(err) } req.Header.Add("Content-Type", "application/x-www-form-urlencoded") r, err := http.DefaultClient.Do(req)
POST 的 payload 是形如 name=poloxue&password=123456 的字符串,故而我們可以通過 url.Values 進(jìn)行組織。
提交給 NewRequest 的內(nèi)容必須是實(shí)現(xiàn) Reader 接口的類型,所以需要 strings.NewReader轉(zhuǎn)化下。
Form 表單提交的 content-type 要是 application/x-www-form-urlencoded,也要設(shè)置下。
復(fù)雜的方式介紹完了。接著再介紹簡化的方式,其實(shí)表單提交只需調(diào)用 http.PostForm 即可完成。示例代碼如下:
payload := make(url.Values) payload.Add("name", "poloxue") payload.Add("password", "123456") r, err := http.PostForm("http://httpbin.org/post", form)
竟是如此的簡單。
提交文件文件提交應(yīng)該是 HTTP 請求中較為復(fù)雜的內(nèi)容了。其實(shí)說難也不難,區(qū)別于其他的請求,我們要花些精力來讀取文件,組織提交POST的數(shù)據(jù)。
舉個例子,假設(shè)現(xiàn)在我有一個圖片文件,名為 as.jpg,路徑在 /Users/polo 目錄下?,F(xiàn)在要將這個圖片提交給 http://httpbin.org/post。
我們要先組織 POST 提交的內(nèi)容,代碼如下:
filename := "/Users/polo/as.jpg" f, err := os.Open(filename) if err != nil { panic(err) } defer func() { _ = f.Close() }() uploadBody := &bytes.Buffer{} writer := multipart.NewWriter(uploadBody) fWriter, err := writer.CreateFormFile("uploadFile", filename) if err != nil { fmt.Printf("copy file writer %v", err) } _, err = io.Copy(fWriter, f) if err != nil { panic(err) } fieldMap := map[string]string{ "filename": filename, } for k, v := range fieldMap { _ = writer.WriteField(k, v) } err = writer.Close() if err != nil { panic(err) }
我認(rèn)為,數(shù)據(jù)組織分為幾步完成,如下:
第一步,打開將要上傳的文件,使用 defer f.Close() 做好資源釋放的準(zhǔn)備;
第二步,創(chuàng)建存儲上傳內(nèi)容的 bytes.Buffer,變量名為 uploadBody;
第三步,通過 multipart.NewWriter 創(chuàng)建 writer,用于向 buffer中寫入文件提供的內(nèi)容;
第四步,通過writer.CreateFormFile 創(chuàng)建上傳文件并通過 io.Copy 向其中寫入內(nèi)容;
最后,通過 writer.WriteField 添加其他的附加信息,注意最后要把 writer 關(guān)閉;
至此,文件上傳的數(shù)據(jù)就組織完成了。接下來,只需調(diào)用 http.Post 方法即可完成文件上傳。
r, err := http.Post("http://httpbin.org/post", writer.FormDataContentType(), uploadBody)
有一點(diǎn)要注意,請求的content-type需要設(shè)置,而通過 writer.FormDataContentType() 即能獲得上傳文件的類型。
到此,文件提交也完成了,不知道有沒有非常簡單的感覺。
Cookie主要涉及兩部分內(nèi)容,即讀取響應(yīng)的 cookie 與設(shè)置請求的 cookie。響應(yīng)的 cookie 獲取方式非常簡單,直接調(diào)用 r.Cookies 即可。
重點(diǎn)來說說,如何設(shè)置請求 cookie。cookie設(shè)置有兩種方式,一種設(shè)置在 Client 上,另一種是設(shè)置在 Request 上。
Client 上設(shè)置 Cookie直接看示例代碼:
cookies := make([]*http.Cookie, 0) cookies = append(cookies, &http.Cookie{ Name: "name", Value: "poloxue", Domain: "httpbin.org", Path: "/cookies", }) cookies = append(cookies, &http.Cookie{ Name: "id", Value: "10000", Domain: "httpbin.org", Path: "/elsewhere", }) url, err := url.Parse("http://httpbin.org/cookies") if err != nil { panic(err) } jar, err := cookiejar.New(nil) if err != nil { panic(err) } jar.SetCookies(url, cookies) client := http.Client{Jar: jar} r, err := client.Get("http://httpbin.org/cookies")
代碼中,我們首先創(chuàng)建了 http.Cookie 切片,然后向其中添加了 2 個 Cookie 數(shù)據(jù)。這里通過 cookiejar,保存了 2 個新建的 cookie。
這次我們不能再使用默認(rèn)的 DefaultClient 了,而是要創(chuàng)建新的 Client,并將保存 cookie 信息的 cookiejar 與 client 綁定。接下里,只需要使用新創(chuàng)建的 Client 發(fā)起請求即可。
請求上設(shè)置 Cookie請求上的 cookie 設(shè)置,通過 req.AddCookie即可實(shí)現(xiàn)。示例代碼:
req, err := http.NewRequest(http.MethodGet, "http://httpbin.org/cookies", nil) if err != nil { panic(err) } req.AddCookie(&http.Cookie{ Name: "name", Value: "poloxue", Domain: "httpbin.org", Path: "/cookies", }) r, err := http.DefaultClient.Do(req)
挺簡單的,沒什么要介紹的。
cookie 設(shè)置 Client 和 設(shè)置在 Request 上有何區(qū)別?一個最易想到的區(qū)別就是,Request 的 cookie 只是當(dāng)次請求失效,而 Client 上的 cookie 是隨時有效的,只要你用的是這個新創(chuàng)建的 Client。
重定向和請求歷史默認(rèn)情況下,所有類型請求都會自動處理重定向。
Python 的 requests 包中 HEAD 請求是不重定向的,但測試結(jié)果顯示 net/http 的 HEAD 是自動重定向的。
net/http 中的重定向控制可以通過 Client 中的一個名為 CheckRedirect 的成員控制,它是函數(shù)類型。定義如下:
type Client struct { ... CheckRedirect func(req *Request, via []*Request) error ... }
接下來,我們來看看怎么使用。
假設(shè)我們要實(shí)現(xiàn)的功能:為防止發(fā)生循環(huán)重定向,重定向次數(shù)定義不能超過 10 次,而且要記錄歷史 Response。
示例代碼:
var r *http.Response history := make([]*http.Response, 0) client := http.Client{ CheckRedirect: func(req *http.Request, hrs []*http.Request) error { if len(hrs) >= 10 { return errors.New("redirect to many times") } history = append(history, req.Response) return nil }, } r, err := client.Get("http://github.com")
首先創(chuàng)建了 http.Response 切片的變量,名稱為 history。接著在 http.Client 中為 CheckRedirect 賦予一個匿名函數(shù),用于控制重定向的行為。CheckRedirect 函數(shù)的第一個參數(shù)表示下次將要請求的 Request,第二個參數(shù)表示已經(jīng)請求過的 Request。
當(dāng)發(fā)生重定向時,當(dāng)前的 Request 會保存上次請求的 Response,故而此處可以將 req.Response 追加到 history 變量中。
超時設(shè)置Request 發(fā)出后,如果服務(wù)端遲遲沒有響應(yīng),那豈不是很尷尬。那么我們就會想,能否為請求設(shè)置超時規(guī)則呢?毫無疑問,當(dāng)然可以。
超時可以分為連接超時和響應(yīng)讀取超時,這些都可以設(shè)置。但正常情況下,并不想有那么明確的區(qū)別,那么也可以設(shè)置個總超時。
總超時總的超時時間的設(shè)置是綁定在 Client 的一個名為 Timeout 的成員之上,Timeout 是 time.Duration。
假設(shè)這是超時時間為 10 秒,示例代碼:
client := http.Client{ Timeout: time.Duration(10 * time.Second), }連接超時
連接超時可通過 Client 中的 Transport 實(shí)現(xiàn)。Transport 中有個名為 Dial 的成員函數(shù),可用設(shè)置連接超時。Transport 是 HTTP 底層的數(shù)據(jù)運(yùn)輸者。
假設(shè)設(shè)置連接超時時間為 2 秒,示例代碼:
t := &http.Transport{ Dial: func(network, addr string) (net.Conn, error) { timeout := time.Duration(2 * time.Second) return net.DialTimeout(network, addr, timeout) }, }
在 Dial 的函數(shù)中,我們通過 net.DialTimeout 進(jìn)行網(wǎng)絡(luò)連接,實(shí)現(xiàn)了連接超時功能。
讀取超時讀取超時也要通過 Client 的 Transport 設(shè)置,比如設(shè)置響應(yīng)的讀取為 8 秒。
示例代碼:
t := &http.Transport{ ResponseHeaderTimeout: time.Second * 8, } 綜合所有,Client 的創(chuàng)建代碼如下: t := &http.Transport{ Dial: func(network, addr string) (net.Conn, error) { timeout := time.Duration(2 * time.Second) return net.DialTimeout(network, addr, timeout) }, ResponseHeaderTimeout: time.Second * 8, } client := http.Client{ Transport: t, Timeout: time.Duration(10 * time.Second), }
除了上面的幾個超時設(shè)置,Transport 還有其他一些關(guān)于超時的設(shè)置,可以看下 Transport 的定義,還有發(fā)現(xiàn)三個與超時相關(guān)的定義:
// IdleConnTimeout is the maximum amount of time an idle // (keep-alive) connection will remain idle before closing // itself. // Zero means no limit. IdleConnTimeout time.Duration // ResponseHeaderTimeout, if non-zero, specifies the amount of // time to wait for a server"s response headers after fully // writing the request (including its body, if any). This // time does not include the time to read the response body. ResponseHeaderTimeout time.Duration // ExpectContinueTimeout, if non-zero, specifies the amount of // time to wait for a server"s first response headers after fully // writing the request headers if the request has an // "Expect: 100-continue" header. Zero means no timeout and // causes the body to be sent immediately, without // waiting for the server to approve. // This time does not include the time to send the request header. ExpectContinueTimeout time.Duration
分別是 IdleConnTimeout (連接空閑超時時間,keep-live 開啟)、TLSHandshakeTimeout (TLS 握手時間)和 ExpectContinueTimeout(似乎已含在 ResponseHeaderTimeout 中了,看注釋)。
到此,完成了超時的設(shè)置。相對于 Python requests 確實(shí)是復(fù)雜很多。
請求代理代理還是挺重要的,特別對于開發(fā)爬蟲的同學(xué)。那 net/http 怎么設(shè)置代理?這個工作還是要依賴 Client 的成員 Transport 實(shí)現(xiàn),這個 Transport 還是挺重要的。
Transport 有個名為 Proxy 的成員,具體看看怎么使用吧。假設(shè)我們要通過設(shè)置代理來請求谷歌的主頁,代理地址為 http://127.0.0.1:8087。
示例代碼:
proxyUrl, err := url.Parse("http://127.0.0.1:8087") if err != nil { panic(err) } t := &http.Transport{ Proxy: http.ProxyURL(proxyUrl), TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, } client := http.Client{ Transport: t, Timeout: time.Duration(10 * time.Second), } r, err := client.Get("https://google.com")
主要關(guān)注 http.Transport 創(chuàng)建的代碼。兩個參數(shù),分時 Proxy 和 TLSClientConfig,分別用于設(shè)置代理和禁用 https 驗證。我發(fā)現(xiàn)其實(shí)不設(shè)置 TLSClientConfig 也可以請求成功,具體原因沒仔細(xì)研究。
錯誤處理錯誤處理其實(shí)都不用怎么介紹,GO中的一般錯誤主要是檢查返回的error,HTTP 請求也是如此,它會視情況返回相應(yīng)錯誤信息,比如超時、網(wǎng)絡(luò)連接失敗等。
示例代碼中的錯誤都是通過 panic 拋出去的,真實(shí)的項目肯定不是這樣的,我們需要記錄相關(guān)日志,時刻做好錯誤恢復(fù)工作。
總結(jié)本文以 Python 的 requests 文檔為指導(dǎo)方向,整理了 requests 快速入門文檔中的案例在 GO 的是如何實(shí)現(xiàn)的。要說明的是, GO 其實(shí)也提供了對應(yīng)于 requests 的克隆版本,[github地址](
https://github.com/levigross/...。暫時我也還沒有看,有興趣的朋友可以去研究一下。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/45252.html
?????? ???Hello,大家好我叫是Dream呀,一個有趣的Python博主,小白一枚,多多關(guān)照??? ???CSDN Python領(lǐng)域新星創(chuàng)作者,大二在讀,歡迎大家找我合作學(xué)習(xí) ?入門須知:這片樂園從不缺乏天才,努力才是你的最終入場券!??? ?最后,愿我們都能在看不到的地方閃閃發(fā)光,一起加油進(jìn)步??? ???一萬次悲傷,依然會有Dream,我一直在最溫暖的地方等你,唱的就是我!哈哈哈~...
摘要:背景一個國人編寫的強(qiáng)大的網(wǎng)絡(luò)爬蟲系統(tǒng)并帶有強(qiáng)大的??蚣軐W(xué)習(xí)時走過的一些坑錯誤我所遇到的一些錯誤首先,本爬蟲目標(biāo)使用框架爬取網(wǎng)站的帖子中的問題和內(nèi)容,然后將爬取的數(shù)據(jù)保存在本地。修飾器,表示每天會執(zhí)行一次,這樣就能抓到最新的帖子了。 背景: PySpider:一個國人編寫的強(qiáng)大的網(wǎng)絡(luò)爬蟲系統(tǒng)并帶有強(qiáng)大的WebUI。采用Python語言編寫,分布式架構(gòu),支持多種數(shù)據(jù)庫后端,強(qiáng)大的WebUI...
摘要:在上篇文章實(shí)現(xiàn)簡單爬蟲框架單任務(wù)版爬蟲中我們實(shí)現(xiàn)了一個簡單的單任務(wù)版爬蟲,對于單任務(wù)版爬蟲,每次都要請求頁面,然后解析數(shù)據(jù),然后才能請求下一個頁面。在上篇文章Golang實(shí)現(xiàn)簡單爬蟲框架(2)——單任務(wù)版爬蟲中我們實(shí)現(xiàn)了一個簡單的單任務(wù)版爬蟲,對于單任務(wù)版爬蟲,每次都要請求頁面,然后解析數(shù)據(jù),然后才能請求下一個頁面。整個過程中,獲取網(wǎng)頁數(shù)據(jù)速度比較慢,那么我們就把獲取數(shù)據(jù)模塊做成并發(fā)執(zhí)行。在...
摘要:在上篇文章實(shí)現(xiàn)簡單爬蟲框架單任務(wù)版爬蟲中我們實(shí)現(xiàn)了一個簡單的單任務(wù)版爬蟲,對于單任務(wù)版爬蟲,每次都要請求頁面,然后解析數(shù)據(jù),然后才能請求下一個頁面。在上篇文章Golang實(shí)現(xiàn)簡單爬蟲框架(2)——單任務(wù)版爬蟲中我們實(shí)現(xiàn)了一個簡單的單任務(wù)版爬蟲,對于單任務(wù)版爬蟲,每次都要請求頁面,然后解析數(shù)據(jù),然后才能請求下一個頁面。整個過程中,獲取網(wǎng)頁數(shù)據(jù)速度比較慢,那么我們就把獲取數(shù)據(jù)模塊做成并發(fā)執(zhí)行。在...
摘要:全局安裝目前版本,這個版本不再需要使用命令了?,F(xiàn)在嘗試深入抓取文章內(nèi)容可以發(fā)現(xiàn)因為訪問服務(wù)器太迅猛,導(dǎo)致出現(xiàn)很多次錯誤。解決添加文件修改文件為修改文件為觀察輸出可以看到,程序?qū)崿F(xiàn)了隔一秒再請求下一個內(nèi)容頁。 全局安裝typescript: npm install -g typescript 目前版本2.0.3,這個版本不再需要使用typings命令了。但是vscode捆綁的版本是1....
閱讀 1151·2019-08-30 12:44
閱讀 657·2019-08-29 13:03
閱讀 2566·2019-08-28 18:15
閱讀 2434·2019-08-26 10:41
閱讀 3096·2019-08-26 10:28
閱讀 3045·2019-08-23 16:54
閱讀 1995·2019-08-23 15:16
閱讀 820·2019-08-23 14:55