思维导图
Golang中提供了net/http
这个包原生支持request
和response
。
1. 发送请求
1 reqList, err := http.NewRequest("GET" , URL, nil )
Go中提供了一个cookiejar.New
的函数方法,用于保留生成Cookie
信息,这个是为了一些网站要登陆才能爬取的情况,所以我们登陆完之后,会有一个cookie,这个cookie是存储用户信息
的,也就是这个信息是让服务器知道是谁
进行这一次的访问!比如说登陆学校的教务处进行爬取课表,因为课表每个人都可能是不同的,所以就需要登陆,让服务器知道这是谁的课表信息,所以就需要在请求头上加上cookie
进行伪装爬取。
1 2 3 4 jar, err := cookiejar.New(nil ) if err != nil { panic (err) }
构造POST
请求的时候,可以把要传输的数据进行封装好,与URL
一起构造
1 2 3 4 var client http.ClientInfo :="muser=" +muserid+"&" +"passwd=" +password var data = strings.NewReader(Info)req, err := http.NewRequest("POST" , URL, data)
1 2 3 4 5 6 7 8 req.Header.Set("Connection" , "keep-alive" ) req.Header.Set("Pragma" , "no-cache" ) req.Header.Set("Cache-Control" , "no-cache" ) req.Header.Set("Upgrade-Insecure-Requests" , "1" ) req.Header.Set("Content-Type" , "application/x-www-form-urlencoded" ) req.Header.Set("User-Agent" , "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Safari/537.36" ) req.Header.Set("Accept" , "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9" ) req.Header.Set("Accept-Language" , "zh-CN,zh;q=0.9" )
1 2 resp, _:= client.Do(req) bodyText, _ := ioutil.ReadAll(resp.Body)
上文也提到了一个包,当发送完请求之后,cookie就会保存在这个client.Jar
这个包中
1 myStr:=fmt.Sprintf("%s" ,client.Jar)
我们处理打印出这个client.Jar
这个包的信息之后,选出响应的cookie
,然后放在请求头上面即可!就能处理登陆情况下的cookie问题了。
1 req.Header.Set("Cookie" , "ASP.NET_SessionId=" +cook)
至此,发送请求部分就完全完成了!
2. 解析网页 2.1 CSS选择器 github.com/PuerkitoBio/goquery
提供了.NewDocumentFromReader
方法进行网页的解析。
1 doc, err := goquery.NewDocumentFromReader(resp.Body)
2.2 Xpath 语法 github.com/antchfx/htmlquery
提供了.Parse
方法进行网页的解析
1 root, _ := htmlquery.Parse(resp.Body)
2.3 Regex 正则 1 2 3 4 5 reId, _ := regexp.Compile(`id=(\d+)` ) allId := reId.FindAll(bodyText,1 ) for _,item := range allId { id=string (item) }
3. 获取节点信息 3.1 CSS 选择器 通过2.1,我们拿到上一步解析出来的doc
之后,可以进行css选择器语法
,进行结点的选择。
1 2 3 4 5 6 7 8 9 10 11 12 13 doc.Find("#main > div.right > div.detail_main_content" ). Each(func (i int , s *goquery.Selection) { Data.title = s.Find("p" ).Text() Data.time = s.Find("#fbsj" ).Text() Data.author = s.Find("#author" ).Text() Data.count = Read_Count(Read_Id) fmt.Println(Data.title, Data.time, Data.author,Data.count) }) doc.Find("#news_content_display" ).Each(func (i int , s *goquery.Selection) { Data.content = s.Find("p" ).Text() fmt.Println(Data.content) })
3.2 Xpath 语法 通过3.2,我们拿到上一步解析出来的root
之后,可以进行Xpath语法的编写
,进行结点的选择。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 tr := htmlquery.Find(root, "//*[@id='LB_kb']/table/tbody/tr/td" ) for _, row := range tr { classNames := htmlquery.Find(row, "./font" ) classPosistions := htmlquery.Find(row,"./text()[4]" ) classTeachers := htmlquery.Find(row,"./text()[5]" ) if len (classNames)!=0 { className = htmlquery.InnerText(classNames[0 ]) classPosistion = htmlquery.InnerText(classPosistions[0 ]) classTeacher = htmlquery.InnerText(classTeachers[0 ]) fmt.Println(className) fmt.Println(classPosistion) fmt.Println(classTeacher) } }
4. 保存信息 4.1 使用原生SQL语句把数据保存Mysql中
1 2 3 4 5 6 7 const ( usernameClass = "root" passwordClass = "root" ipClass = "127.0.0.1" portClass = "3306" dbnameClass = "class" )
1 2 3 4 5 6 7 8 9 10 11 12 var DB *sql.DBfunc InitDB () { path := strings.Join([]string {usernameClass, ":" , passwordClass, "@tcp(" , ipClass, ":" , portClass, ")/" , dbnameClass, "?charset=utf8" }, "" ) DB, _ = sql.Open("mysql" , path) DB.SetConnMaxLifetime(10 ) DB.SetMaxIdleConns(5 ) if err := DB.Ping(); err != nil { fmt.Println("opon database fail" ) return } fmt.Println("connect success" ) }
1 2 3 4 5 type Class struct { classData string teacherName string position string }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 func InsertData (Data Class) bool { tx, err := DB.Begin() if err != nil { fmt.Println("tx fail" ) return false } stmt, err := tx.Prepare("INSERT INTO class_data (`class`,`teacher`,`position`) VALUES (?, ?, ?)" ) if err != nil { fmt.Println("Prepare fail" ,err) return false } _, err = stmt.Exec(Data.classData,Data.teacherName,Data.position) if err != nil { fmt.Println("Exec fail" ,err) return false } _ = tx.Commit() return true }
4.2 使用GORM把数据保存到Mysql中
1 2 3 4 5 6 7 8 type NewD struct { gorm.Model Title string `gorm:"type:varchar(255);not null;"` Time string `gorm:"type:varchar(256);not null;"` Author string `gorm:"type:varchar(256);not null;"` Count string `gorm:"type:varchar(256);not null;"` Content string `gorm:"type:longtext;not null;"` }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 var db *gorm.DBfunc Init () { var err error path := strings.Join([]string {userName_New, ":" , password_New, "@tcp(" ,ip_New, ":" , port_New, ")/" , dbName_New, "?charset=utf8" }, "" ) db, err = gorm.Open("mysql" , path) if err != nil { panic (err) } fmt.Println("SUCCESS" ) _ = db.AutoMigrate(&NewD{}) sqlDB := db.DB() sqlDB.SetMaxIdleConns(10 ) sqlDB.SetMaxOpenConns(100 ) }
1 2 3 4 5 6 7 8 NewA := NewD{ Title: Data.title, Time: Data.time, Author: Data.author, Count: Data.count, Content: Data.content, } err = db.Create(&NewA).Error