Http請求報(bào)文格式分析
package main
import (
"fmt"
"net"
)
func main() {
//監(jiān)聽
listener, err := net.Listen("tcp", ":8000")
if err != nil {
fmt.Println("listener err", err)
return
}
defer listener.Close()
//阻塞等待用戶的連接
conn, err := listener.Accept()
if err != nil {
fmt.Println("Accept err = ", err)
return
}
defer conn.Close()
//接收客戶端的數(shù)據(jù)
buf := make([]byte, 1024*4)
readSize, err := conn.Read(buf)
if readSize == 0 { //對方斷開,出問題了
fmt.Println("Read err = ", err)
return
}
fmt.Printf("#%v#", string(buf[:readSize]))
}
#GET / HTTP/1.1 //請求行
Host: 127.0.0.1:8000
Connection: keep-alive
Cache-Control: max-age=0
sec-ch-ua: "Chromium";v="116", "Not)A;Brand";v="24", "Microsoft Edge";v="116"
sec-ch-ua-mobile: ?0
sec-ch-ua-platform: "Windows"
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36 Edg/116.0.1938.69
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Sec-Fetch-Site: none
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Sec-Fetch-Dest: document
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6
#
HTTP編程
go語言標(biāo)準(zhǔn)庫內(nèi)建提供了net/http包,涵蓋了HTTP客戶端和服務(wù)端的具體實(shí)現(xiàn)。使用net/http包,我們可以很方便地編寫HTTP客戶端或服務(wù)端的程序。文章來源:http://www.zghlxwxcb.cn/news/detail-708956.html
func ListenAndServe(addr string, handler Handler) error
ListenAndServe監(jiān)聽TCP地址addr,并且會(huì)使用handler參數(shù)調(diào)用Serve函數(shù)處理接收到的連接。handler參數(shù)一般會(huì)設(shè)為nil,此時(shí)會(huì)使用DefaultServeMux。文章來源地址http://www.zghlxwxcb.cn/news/detail-708956.html
package main
import "net/http"
// HandleConn 第一個(gè)參數(shù),給客戶端回復(fù)數(shù)據(jù),req 讀取客戶端發(fā)送的數(shù)據(jù)
func HandleConn(w http.ResponseWriter, req *http.Request) {
_, err := w.Write([]byte("hello go")) //給客戶端回復(fù)數(shù)據(jù)
if err != nil {
return
}
}
func main() {
//HandleFunc注冊一個(gè)處理器函數(shù)handler和對應(yīng)的模式pattern(注冊到DefaultServeMux)。
//ServeMux的文檔解釋了模式的匹配機(jī)制。
//注冊處理函數(shù),用戶連接,自動(dòng)調(diào)用指定的處理函數(shù)
//func HandleFunc(pattern string, handler func(ResponseWriter, *Request))
http.HandleFunc("/", HandleConn)
//監(jiān)聽綁定
//ListenAndServe監(jiān)聽TCP地址addr,
//并且會(huì)使用handler參數(shù)調(diào)用Serve函數(shù)處理接收到的連接。handler參數(shù)一般會(huì)設(shè)為nil,此時(shí)會(huì)使用DefaultServeMux。
http.ListenAndServe(":8000", nil)
}
http服務(wù)器獲取客戶端的一些信息
type Request struct {
// Method指定HTTP方法(GET、POST、PUT等)。對客戶端,""代表GET。
Method string
// URL在服務(wù)端表示被請求的URI,在客戶端表示要訪問的URL。
//
// 在服務(wù)端,URL字段是解析請求行的URI(保存在RequestURI字段)得到的,
// 對大多數(shù)請求來說,除了Path和RawQuery之外的字段都是空字符串。
// (參見RFC 2616, Section 5.1.2)
//
// 在客戶端,URL的Host字段指定了要連接的服務(wù)器,
// 而Request的Host字段(可選地)指定要發(fā)送的HTTP請求的Host頭的值。
URL *url.URL
// 接收到的請求的協(xié)議版本。本包生產(chǎn)的Request總是使用HTTP/1.1
Proto string // "HTTP/1.0"
ProtoMajor int // 1
ProtoMinor int // 0
// Header字段用來表示HTTP請求的頭域。如果頭域(多行鍵值對格式)為:
// accept-encoding: gzip, deflate
// Accept-Language: en-us
// Connection: keep-alive
// 則:
// Header = map[string][]string{
// "Accept-Encoding": {"gzip, deflate"},
// "Accept-Language": {"en-us"},
// "Connection": {"keep-alive"},
// }
// HTTP規(guī)定頭域的鍵名(頭名)是大小寫敏感的,請求的解析器通過規(guī)范化頭域的鍵名來實(shí)現(xiàn)這點(diǎn)。
// 在客戶端的請求,可能會(huì)被自動(dòng)添加或重寫Header中的特定的頭,參見Request.Write方法。
Header Header
// Body是請求的主體。
//
// 在客戶端,如果Body是nil表示該請求沒有主體買入GET請求。
// Client的Transport字段會(huì)負(fù)責(zé)調(diào)用Body的Close方法。
//
// 在服務(wù)端,Body字段總是非nil的;但在沒有主體時(shí),讀取Body會(huì)立刻返回EOF。
// Server會(huì)關(guān)閉請求的主體,ServeHTTP處理器不需要關(guān)閉Body字段。
Body io.ReadCloser
// ContentLength記錄相關(guān)內(nèi)容的長度。
// 如果為-1,表示長度未知,如果>=0,表示可以從Body字段讀取ContentLength字節(jié)數(shù)據(jù)。
// 在客戶端,如果Body非nil而該字段為0,表示不知道Body的長度。
ContentLength int64
// TransferEncoding按從最外到最里的順序列出傳輸編碼,空切片表示"identity"編碼。
// 本字段一般會(huì)被忽略。當(dāng)發(fā)送或接受請求時(shí),會(huì)自動(dòng)添加或移除"chunked"傳輸編碼。
TransferEncoding []string
// Close在服務(wù)端指定是否在回復(fù)請求后關(guān)閉連接,在客戶端指定是否在發(fā)送請求后關(guān)閉連接。
Close bool
// 在服務(wù)端,Host指定URL會(huì)在其上尋找資源的主機(jī)。
// 根據(jù)RFC 2616,該值可以是Host頭的值,或者URL自身提供的主機(jī)名。
// Host的格式可以是"host:port"。
//
// 在客戶端,請求的Host字段(可選地)用來重寫請求的Host頭。
// 如過該字段為"",Request.Write方法會(huì)使用URL字段的Host。
Host string
// Form是解析好的表單數(shù)據(jù),包括URL字段的query參數(shù)和POST或PUT的表單數(shù)據(jù)。
// 本字段只有在調(diào)用ParseForm后才有效。在客戶端,會(huì)忽略請求中的本字段而使用Body替代。
Form url.Values
// PostForm是解析好的POST或PUT的表單數(shù)據(jù)。
// 本字段只有在調(diào)用ParseForm后才有效。在客戶端,會(huì)忽略請求中的本字段而使用Body替代。
PostForm url.Values
// MultipartForm是解析好的多部件表單,包括上傳的文件。
// 本字段只有在調(diào)用ParseMultipartForm后才有效。
// 在客戶端,會(huì)忽略請求中的本字段而使用Body替代。
MultipartForm *multipart.Form
// Trailer指定了會(huì)在請求主體之后發(fā)送的額外的頭域。
//
// 在服務(wù)端,Trailer字段必須初始化為只有trailer鍵,所有鍵都對應(yīng)nil值。
// (客戶端會(huì)聲明哪些trailer會(huì)發(fā)送)
// 在處理器從Body讀取時(shí),不能使用本字段。
// 在從Body的讀取返回EOF后,Trailer字段會(huì)被更新完畢并包含非nil的值。
// (如果客戶端發(fā)送了這些鍵值對),此時(shí)才可以訪問本字段。
//
// 在客戶端,Trail必須初始化為一個(gè)包含將要發(fā)送的鍵值對的映射。(值可以是nil或其終值)
// ContentLength字段必須是0或-1,以啟用"chunked"傳輸編碼發(fā)送請求。
// 在開始發(fā)送請求后,Trailer可以在讀取請求主體期間被修改,
// 一旦請求主體返回EOF,調(diào)用者就不可再修改Trailer。
//
// 很少有HTTP客戶端、服務(wù)端或代理支持HTTP trailer。
Trailer Header
// RemoteAddr允許HTTP服務(wù)器和其他軟件記錄該請求的來源地址,一般用于日志。
// 本字段不是ReadRequest函數(shù)填寫的,也沒有定義格式。
// 本包的HTTP服務(wù)器會(huì)在調(diào)用處理器之前設(shè)置RemoteAddr為"IP:port"格式的地址。
// 客戶端會(huì)忽略請求中的RemoteAddr字段。
RemoteAddr string
// RequestURI是被客戶端發(fā)送到服務(wù)端的請求的請求行中未修改的請求URI
// (參見RFC 2616, Section 5.1)
// 一般應(yīng)使用URI字段,在客戶端設(shè)置請求的本字段會(huì)導(dǎo)致錯(cuò)誤。
RequestURI string
// TLS字段允許HTTP服務(wù)器和其他軟件記錄接收到該請求的TLS連接的信息
// 本字段不是ReadRequest函數(shù)填寫的。
// 對啟用了TLS的連接,本包的HTTP服務(wù)器會(huì)在調(diào)用處理器之前設(shè)置TLS字段,否則將設(shè)TLS為nil。
// 客戶端會(huì)忽略請求中的TLS字段。
TLS *tls.ConnectionState
}
package main
import (
"fmt"
"net/http"
)
// HandleConn 第一個(gè)參數(shù),給客戶端回復(fù)數(shù)據(jù),req 讀取客戶端發(fā)送的數(shù)據(jù)
func HandleConn(w http.ResponseWriter, req *http.Request) {
fmt.Println("r.Method = ", req.Method) //r.Method = GET
fmt.Println("r.URL = ", req.URL) // /
fmt.Println("Header = ", req.Header)
fmt.Println("Body = ", req.Body)
_, err := w.Write([]byte("hello go")) //給客戶端回復(fù)數(shù)據(jù)
if err != nil {
return
}
}
func main() {
//HandleFunc注冊一個(gè)處理器函數(shù)handler和對應(yīng)的模式pattern(注冊到DefaultServeMux)。
//ServeMux的文檔解釋了模式的匹配機(jī)制。
//注冊處理函數(shù),用戶連接,自動(dòng)調(diào)用指定的處理函數(shù)
//func HandleFunc(pattern string, handler func(ResponseWriter, *Request))
http.HandleFunc("/", HandleConn)
//監(jiān)聽綁定
//ListenAndServe監(jiān)聽TCP地址addr,
//并且會(huì)使用handler參數(shù)調(diào)用Serve函數(shù)處理接收到的連接。handler參數(shù)一般會(huì)設(shè)為nil,此時(shí)會(huì)使用DefaultServeMux。
http.ListenAndServe(":8000", nil)
}
http客戶端編程
type Response struct {
Status string // 例如"200 OK"
StatusCode int // 例如200
Proto string // 例如"HTTP/1.0"
ProtoMajor int // 例如1
ProtoMinor int // 例如0
// Header保管頭域的鍵值對。
// 如果回復(fù)中有多個(gè)頭的鍵相同,Header中保存為該鍵對應(yīng)用逗號(hào)分隔串聯(lián)起來的這些頭的值
// (參見RFC 2616 Section 4.2)
// 被本結(jié)構(gòu)體中的其他字段復(fù)制保管的頭(如ContentLength)會(huì)從Header中刪掉。
//
// Header中的鍵都是規(guī)范化的,參見CanonicalHeaderKey函數(shù)
Header Header
// Body代表回復(fù)的主體。
// Client類型和Transport類型會(huì)保證Body字段總是非nil的,即使回復(fù)沒有主體或主體長度為0。
// 關(guān)閉主體是調(diào)用者的責(zé)任。
// 如果服務(wù)端采用"chunked"傳輸編碼發(fā)送的回復(fù),Body字段會(huì)自動(dòng)進(jìn)行解碼。
Body io.ReadCloser
// ContentLength記錄相關(guān)內(nèi)容的長度。
// 其值為-1表示長度未知(采用chunked傳輸編碼)
// 除非對應(yīng)的Request.Method是"HEAD",其值>=0表示可以從Body讀取的字節(jié)數(shù)
ContentLength int64
// TransferEncoding按從最外到最里的順序列出傳輸編碼,空切片表示"identity"編碼。
TransferEncoding []string
// Close記錄頭域是否指定應(yīng)在讀取完主體后關(guān)閉連接。(即Connection頭)
// 該值是給客戶端的建議,Response.Write方法的ReadResponse函數(shù)都不會(huì)關(guān)閉連接。
Close bool
// Trailer字段保存和頭域相同格式的trailer鍵值對,和Header字段相同類型
Trailer Header
// Request是用來獲取此回復(fù)的請求
// Request的Body字段是nil(因?yàn)橐呀?jīng)被用掉了)
// 這個(gè)字段是被Client類型發(fā)出請求并獲得回復(fù)后填充的
Request *Request
// TLS包含接收到該回復(fù)的TLS連接的信息。 對未加密的回復(fù),本字段為nil。
// 返回的指針是被(同一TLS連接接收到的)回復(fù)共享的,不應(yīng)被修改。
TLS *tls.ConnectionState
}
package main
import (
"fmt"
"net/http"
)
func main() {
response, err := http.Get("http://www.baidu.com")
if err != nil {
fmt.Println("Get response err = ", err)
return
}
defer response.Body.Close() //內(nèi)容在body里面
fmt.Println("response.status = ", response.Status) //response.status = 200 OK
fmt.Println("response.StatusCode = ", response.StatusCode) //200
fmt.Println("response.Header = ", response.Header)
//fmt.Println("response.Body = ", response.Body) //response.Body = &{[] 0xc000226080 <nil> <nil>}
buf := make([]byte, 4*1024)
var tmp string
for true {
BodySize, err := response.Body.Read(buf)
if BodySize == 0 {
fmt.Println("read err = ", err)
break
}
tmp += string(buf[:BodySize])
}
fmt.Println("tmp = ", tmp)
}
單任務(wù)百度貼吧小爬蟲
package main
import (
"fmt"
"net/http"
"os"
"strconv"
)
// https://tieba.baidu.com/f?kw=%E7%BB%9D%E5%9C%B0%E6%B1%82%E7%94%9F&ie=utf-8&pn=150
// HttpGet 爬取網(wǎng)頁內(nèi)容
func HttpGet(url string) (result string, err error) {
response, err1 := http.Get(url)
if err1 != nil {
err = err1
return
}
defer response.Body.Close()
//讀取網(wǎng)頁body
buf := make([]byte, 1024*4)
for true {
readSize, err := response.Body.Read(buf)
if readSize == 0 { //讀取結(jié)束,或者出問題
fmt.Println("response body read err = ", err)
break
}
result += string(buf[:readSize])
}
return
}
func DoWork(start, end int) {
fmt.Printf("正在爬取%d到%d的頁面\n", start, end)
//明確目標(biāo)(要知道你準(zhǔn)備在那個(gè)范圍或者網(wǎng)站去搜索)
for i := start; i <= end; i++ {
//strconv.Itoa((i-1)*50)//整型轉(zhuǎn)string
url := "https://tieba.baidu.com/f?kw=%E7%BB%9D%E5%9C%B0%E6%B1%82%E7%94%9F&ie=utf-8&pn=" + strconv.Itoa((i-1)*50)
fmt.Println("url =", url)
//爬(將所有的網(wǎng)站的內(nèi)容全部爬下來)
result, err := HttpGet(url)
if err != nil {
fmt.Println("HttpGet err = ", err)
continue
}
//把內(nèi)容寫入到文件
fileName := strconv.Itoa(1) + ".html"
file, err := os.Create(fileName)
if err != nil {
fmt.Println("create err = ", err)
continue
}
_, err1 := file.WriteString(result)
if err1 != nil {
fmt.Println("write string err = ", err)
continue
} //寫內(nèi)容
err2 := file.Close()
if err2 != nil {
fmt.Println("close err = ", err2)
continue
} //關(guān)閉文件
}
}
func main() {
var start, end int
fmt.Println("請輸入起始頁(>=1):")
fmt.Scan(&start)
fmt.Println("請輸入終止頁(>=起始頁):")
fmt.Scan(&end)
DoWork(start, end)
}
并發(fā)版網(wǎng)絡(luò)爬蟲
package main
import (
"fmt"
"net/http"
"os"
"strconv"
)
// https://tieba.baidu.com/f?kw=%E7%BB%9D%E5%9C%B0%E6%B1%82%E7%94%9F&ie=utf-8&pn=150
// HttpGet 爬取網(wǎng)頁內(nèi)容
func httpGet1(url string) (result string, err error) {
response, err1 := http.Get(url)
if err1 != nil {
err = err1
return
}
defer response.Body.Close()
//讀取網(wǎng)頁body
buf := make([]byte, 1024*4)
for true {
readSize, err := response.Body.Read(buf)
if readSize == 0 { //讀取結(jié)束,或者出問題
fmt.Println("response body read err = ", err)
break
}
result += string(buf[:readSize])
}
return
}
// 爬取一個(gè)網(wǎng)頁
func SpiderPage(i int, page chan int) {
//strconv.Itoa((i-1)*50)//整型轉(zhuǎn)string
url := "https://tieba.baidu.com/f?kw=%E7%BB%9D%E5%9C%B0%E6%B1%82%E7%94%9F&ie=utf-8&pn=" + strconv.Itoa((i-1)*50)
fmt.Printf("真正爬%d頁的網(wǎng)頁:%s\n", i, url)
//爬(將所有的網(wǎng)站的內(nèi)容全部爬下來)
result, err := httpGet1(url)
if err != nil {
fmt.Println("HttpGet err = ", err)
return
}
//把內(nèi)容寫入到文件
fileName := strconv.Itoa(i) + ".html"
file, err := os.Create(fileName)
if err != nil {
fmt.Println("create err = ", err)
return
}
_, err1 := file.WriteString(result)
if err1 != nil {
fmt.Println("write string err = ", err)
return
} //寫內(nèi)容
err2 := file.Close()
if err2 != nil {
fmt.Println("close err = ", err2)
return
} //關(guān)閉文件
page <- i //寫i
}
func doWork1(start, end int) {
fmt.Printf("正在爬取%d到%d的頁面\n", start, end)
page := make(chan int)
//明確目標(biāo)(要知道你準(zhǔn)備在那個(gè)范圍或者網(wǎng)站去搜索)
for i := start; i <= end; i++ {
go SpiderPage(i, page)
}
for i := start; i <= end; i++ {
fmt.Printf("第%d個(gè)頁面爬取完成\n", <-page)
}
}
func main() {
var start, end int
fmt.Println("請輸入起始頁(>=1):")
fmt.Scan(&start)
fmt.Println("請輸入終止頁(>=起始頁):")
fmt.Scan(&end)
doWork1(start, end)
}
到了這里,關(guān)于go語言基礎(chǔ)---8的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!