赞
踩
1.明确目标,url
2.发送请求获取应答数据
3.保存,过滤,提取有用信息
4.使用分析,得到的数据
package main import ( "fmt" "io" "net/http" "os" "project/wdzinx/wdlog" "strconv" "sync" ) var wg sync.WaitGroup //HTTPGet .. func HTTPGet(url string) (result string, err error) { resp, err := http.Get(url) if err != nil { return "", err } defer resp.Body.Close() //结束关闭body //循环读取网页数据 buf := make([]byte, 4096) for { n, err := resp.Body.Read(buf) if n == 0 { fmt.Println("读取网页完成") break } if err != nil && err != io.EOF { return "", err } result += string(buf[:n]) } return } //Crawlerone ... func Crawlerone(i int) { url := "https://tieba.baidu.com/f?kw=%E5%88%80%E9%94%8B%E9%93%81%E9%AA%91&ie=utf-8&pn=" + strconv.Itoa((i-1)*50) result, err := HTTPGet(url) if wdlog.WDShowError(err) { return } //将读到的整网页数据保存成文件 file, err := os.Create("./file/第" + strconv.Itoa(i) + "页" + ".html") if err != nil { fmt.Println("os.create fail:", err) return } file.WriteString(result) file.Close() fmt.Println("第", i, "页爬取完成") wg.Done() } func worker(start, end int) { fmt.Println("开始干活,从第", start, "页到第", end, "页......") for i := start; i <= end; i++ { wg.Add(1) go Crawlerone(i) } fmt.Println("全部网页爬取完成") wg.Wait() } func main() { //起始,终止页 var start, end int fmt.Print("请输入起始页(>=1)----->") fmt.Scan(&start) fmt.Print("请输入终止页(>=1)----->") fmt.Scan(&end) worker(start, end) }
上面的的例子就是一个最简单的爬虫,去掉了数据处理的模块,其中网址的url,50一次增加,这是网站自身决定的规律(横向爬取,以页为单位),每次爬虫的时候都需要我们分析不同网站的url变换规律,抓取数据如下图
复杂的例子还需要我们会一点正则
实际上,能用自带string解决的问题就不用正则,复杂的才需要正则去解,这里简单说一下正则,实际上强烈不推荐深入学习正则。
. 匹配任意一个字符
[] 匹配中括号中的任意一个字符
- 在[]中表示匹配任意一个16进制数字
^ 在[]开头,表示匹配非括号中字符的任意字符
? 紧跟它前面的单元匹配一次货零次 如[0-9]?\.[0-9] 可以匹配到 1.1 2.0 .5
+ 紧跟在它前面的单元匹配一次或多次 如 [a-z0-9A-z_.-]+@[a-z0-9A-z_.-]+\.[a-z0-9A-z_.-]+ 匹配email
* 紧跟在它前面的单元匹配零次或多次
{n,m}紧跟在它前面的单元最少匹配n次,最多匹配m次 或者{n,} 或固定{n} n次 注{,m} 已过期
如[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3} 匹配ip地址
\ 转义字符
() 将()内中的内容计算为一个单元 如([0-9]{1,3}\.){3}[0-9]{1,3} 匹配ip地址
| 表示或者
更多学习可以看官网: https://code.google.com/p/re2/wiki/Syntax
国内大佬把下来的:http://www.sun190.com/2015/01/re2-正则表达式/
go中的正则表达式的标准RE2,使用只需2步
第一步:解析编译正则表达式 ,函数:
func regexp.MustCompile(str string) * Regexp
第二步:执行,函数:
func (reg * Regexp) FindAllString(s string,n int)[][]string
n:匹配的次数,-1表示比配所有
更多正则函数,说明,regexp包说明见连接:https://studygolang.com/pkgdoc
(?s:(.*?))
//?s: 表示单行模式,该模式下 . 可以匹配任意符号,包括换行符
//*? 重复>=0次匹配x,越少越好(优先跳出重复)
//aaaa(?s:(.*?)) bbbb 表示以aaaa开头的,以bbbb结束 中间的字符
那下面我们抓一下豆瓣电影排行的数据,
package main import ( "bytes" "fmt" "io" "net/http" "os" "project/wdzinx/wdlog" "regexp" "strconv" "sync" ) var wg sync.WaitGroup //HTTPGet .. func HTTPGet(url string) (result string, err error) { //-----------------将http请求伪装成浏览器的 b := new(bytes.Buffer) request, err := http.NewRequest("GET", url, b) if err != nil { return "", err } request.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36") request.Header.Set("content-type", "text/vnd.wap.wml") //-----------------伪装完成----------- client := http.Client{} resp, err := client.Do(request) if err != nil { return "", err } defer resp.Body.Close() //结束关闭body //循环读取网页数据 buf := make([]byte, 4096) for { n, err := resp.Body.Read(buf) if n == 0 { fmt.Println("读取网页完成") break } if err != nil && err != io.EOF { return "", err } result += string(buf[:n]) } return } //FindInfo .. func FindInfo(i int, sdata string) { //解析数据---使用正则表达式 regexpn := regexp.MustCompile(`<img width="100" alt="(?s:(.*?))"`) moviename := regexpn.FindAllStringSubmatch(sdata, -1) regexpd := regexp.MustCompile(`导演:\s?(?s:(.*?))\s`) moviedaoyan := regexpd.FindAllStringSubmatch(sdata, -1) regexpp := regexp.MustCompile(`average">(?s:(.*?))</span>`) moviepingfen := regexpp.FindAllStringSubmatch(sdata, -1) regexpr := regexp.MustCompile(`<span>([0-9]{0,20})人评价</span>`) movieprpj := regexpr.FindAllStringSubmatch(sdata, -1) //将读到的整网页数据保存成文件 file, err := os.OpenFile("../../file/第"+strconv.Itoa(i)+"页"+".txt", os.O_CREATE|os.O_RDWR|os.O_APPEND, 0644) if err != nil { fmt.Println("os.create fail:", err) return } //fmt.Println("moviename", len(moviename)) for n, info := range moviename { _, err = file.WriteString("电影名称:" + info[1] + " -->导演:" + moviedaoyan[n][1] + " --->评分:" + moviepingfen[n][1] + " --->评价人数:" + movieprpj[n][1] + "\n") if err != nil { fmt.Println("写入错误") break } } file.Close() } //Crawlerone ... func Crawlerone(i int) { url := "https://movie.douban.com/top250?start=" + strconv.Itoa((i-1)*25) + "&filter=" result, err := HTTPGet(url) if wdlog.WDShowError(err) { return } FindInfo(i, result) fmt.Println("第", i, "页爬取完成") wg.Done() } func worker(start, end int) { fmt.Println("开始干活,从第", start, "页到第", end, "页......") for i := start; i <= end; i++ { wg.Add(1) go Crawlerone(i) } fmt.Println("全部网页爬取完成") wg.Wait() } func main() { //起始,终止页 var start, end int fmt.Print("请输入起始页(>=1)----->") fmt.Scan(&start) fmt.Print("请输入终止页(>=1)----->") fmt.Scan(&end) worker(start, end) }
其实上面的例子也很简单,就是在上一个的基础上,加入了数据采集功能。使用正则完成。
豆瓣比贴吧安全性强一点,但也强不了多少,把http的请求行伪装一下就分辩不出来了。
采集到的数据截图如下:
那基于上面的方法,我们是不是可以将网页中的一些url信息提取出来,再打开这些url,进行纵向抓取,下面的例子是百度图片的抓取方法,采用的是传统分页式,
package main import ( "fmt" "io" "net/http" "os" "project/wdzinx/wdlog" "regexp" "strconv" "sync" "sync/atomic" ) var key string var num int var wg sync.WaitGroup var name int32 = 0 //正则表达式获取图片url func getinfo(data string) [][]string { rege := regexp.MustCompile(`{"thumbURL":"(?s:(.*?))"`) info := rege.FindAllStringSubmatch(data, -1) return info } func worker(url string) { //打开第一层url resp, err := http.Get(url) if wdlog.WDShowError(err) { fmt.Println("url 拉取错误") return } var data string buf := make([]byte, 4096) for { n, err := resp.Body.Read(buf) if n == 0 { break } if wdlog.WDShowError(err) || err == io.EOF { break } data += string(buf[:n]) } resp.Body.Close() imglist := getinfo(data) //将正则摘取出来的图片url再次打开下载 for _, urlname := range imglist { image, err := http.Get(urlname[1]) if wdlog.WDShowError(err) { fmt.Println("url 拉取错误") return } //名字从1开始取值。原子互斥,防止文件重名,也可以直接用url做名字 imgpath := "../../file/" + strconv.Itoa(int(atomic.AddInt32(&name, 1))) + `.jpg` imgfile, err := os.OpenFile(imgpath, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0644) if wdlog.WDShowError(err) { continue } imgbuf := make([]byte, 4096) for { n, err := image.Body.Read(imgbuf) if n == 0 || err != nil { break } imgfile.Write(imgbuf[:n]) } image.Body.Close() imgfile.Close() } wg.Done() } func main() { fmt.Printf("请输入爬取关键字--->") fmt.Scan(&key) fmt.Printf("请输入要爬取页数--->") fmt.Scan(&num) //循环爬取每页 for i := 0; i <= num; i++ { url := `https://image.baidu.com/search/flip?tn=baiduimage&ie=utf-8&word=` + key + `&pn=` + strconv.Itoa(i*20) + `&gsm=&ct=&ic=0&lm=-1&width=0&height=0` wg.Add(1) go worker(url) } wg.Wait() }
操作截图:
爬取得到的图片
持续更新中…
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。