go爬虫--使用goquery包和正则以及gojquery的总结使用

这是第一版   将爬取来的数据存到本地  没有过滤筛选数据   其中用到了正则和goquery两种方法 

package main

import (
	"fmt"
	"io"
	"log"
	"net/http"
	"os"
	"strconv"
	"strings"

	// "github.com/antchfx/htmlquery"  // xpath方法  没有练习
	"github.com/PuerkitoBio/goquery"
)

func main() {
	// 指定爬取起始,终止页
	var start, end int
	fmt.Println("请输入爬取的起始页")
	fmt.Scan(&start)
	fmt.Println("请输入爬取的终止始页")
	fmt.Scan(&end)
	// working(start, end)  // 这个方法只是将爬来的代码  原封不动的再存到本地  没有分析数据
	spider(start, end) //这个使用了 过滤筛选  数据 用到了正则和goquery
}
func working(start, end int) {
	for i := start; i < end; i++ {
		url := "https://tieba.baidu.com/f?kw=%E9%BB%91tfboys&ie=utf-8&pn=" + strconv.Itoa(i*50)
		result, err := httpGet(url)
		if err != nil {
			fmt.Println("错误")
			continue
		}
		f, err := os.Create("第" + strconv.Itoa(i) + "页.html")
		if err != nil {
			fmt.Println("create  html error ")
			return
		}
		f.WriteString(result)
		f.Close() // 这里不能用defer 因为写了defer就要for循环完才能执行defer 而现在是每个循环要创建一个文件 然后再关闭一个资源
	}
}
func spider(start, end int) {
	f, err := os.OpenFile("sao2015.txt", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
	if err != nil {
		fmt.Println("create file fail")
		return
	}
	for m := 1; m < 13; m++ {
		for i := start; i < end; i++ {
			fmt.Println(i)
			url := "https://www.054gan.com/video/2015-" + strconv.Itoa(m) + "/" + strconv.Itoa(i) + ".html"
			result, err := httpGet(url)
			if err != nil {
				fmt.Println("错误")
				continue
			}
			dom, err := goquery.NewDocumentFromReader(strings.NewReader(result))
			if err != nil {
				log.Fatalln(err)
			}

			dom.Find(".cat_pos a font").Each(func(i int, selection *goquery.Selection) {
				fmt.Println("i", i, "select text", selection.Text())
				f.WriteString(selection.Text() + "----" + url + "\n")
			})

		}
	}
	f.Close()
}
func httpGet(url string) (result string, err error) {
	resp, err := http.Get(url)
	if err != nil {
		fmt.Println("错误")
		return
	}
	// 循环读取
	defer resp.Body.Close()

	buf := make([]byte, 4096)
	for {
		n, err2 := resp.Body.Read(buf)

		if n == 0 || err2 == io.EOF { // 这两个条件满足任一个即可
			fmt.Println("读取网页完成")
			break // 结束本次循环
		}
		if err2 != nil && err2 != io.EOF {
			err = err2
			return // 结束循环 同事结束此方法
		}
		result += string(buf[:n])
	}
	return
}

第二版  并发版

package main

import (
	"fmt"
	"io"
	"net/http"
	"os"
	"strconv"
)

func main() {
	// 指定爬取起始,终止页
	var start, end int
	fmt.Println("请输入爬取的起始页")
	fmt.Scan(&start)
	fmt.Println("请输入爬取的终止始页")
	fmt.Scan(&end)
	working(start, end)
}
func working(start, end int) {

	page := make(chan int)
	for i := start; i <= end; i++ {
		go spider(i, page)
	}
	// 一直会page阻塞在这里  每当回在爬取完成后往page中写入一个值  然后这里就可以循环一次 和select的用法还不一样这里是循环 没有涉及通道的判断
	for i := start; i <= end; i++ {
		fmt.Printf("第%d页爬取完成 \n", <-page)
	}
	fmt.Println("爬取结束")
	close(page)
}
func spider(i int, page chan int) {
	url := "https://tieba.baidu.com/f?kw=%E9%BB%91tfboys&ie=utf-8&pn=" + strconv.Itoa(i*50)

	result, err := httpGet(url)
	if err != nil {
		fmt.Println("错误")
		return
	}
	// fmt.Println("result=", result)  // 输出的是字符串
	f, err := os.Create("第" + strconv.Itoa(i) + "页.html")
	if err != nil {
		fmt.Println("create  html error ")
		return
	}
	f.WriteString(result)
	f.Close() // 这里不能用defer 因为写了defer就要for循环完才能执行defer 而现在是每个循环要创建一个文件 然后再关闭一个资源
	page <- i
}
func httpGet(url string) (result string, err error) {
	resp, err := http.Get(url)
	if err != nil {
		fmt.Println("错误")
		return
	}
	// 循环读取
	defer resp.Body.Close()

	buf := make([]byte, 4096)
	for {
		n, err2 := resp.Body.Read(buf)
		if n == 0 || err2 == io.EOF { // 这两个条件满足任一个即可
			fmt.Println("读取网页完成 \n")
			break // 结束本次循环
		}
		if err2 != nil && err2 != io.EOF {
			err = err2
			return // 结束循环 同事结束此方法
		}
		result += string(buf[:n])
	}
	return
}

总结:并发就是加了一个通道   在最后监听   如果每个通道任务执行完毕   那么就像这个通道传入一个数字;这个样监听通达的循环才能执行下去,直至结束。

goquery  总结:先举个小例子  来入门

func main() {
    html := `<html>
            <body>
                <h1 id="title">春晓</h1>
                <p class="content1">
                春眠不觉晓,
                处处闻啼鸟。
                夜来风雨声,
                花落知多少。
                </p>
            </body>
            </html>
            `
    dom,err:=goquery.NewDocumentFromReader(strings.NewReader(html))
    if err!=nil{
        log.Fatalln(err)
    }

    dom.Find("p").Each(func(i int, selection *goquery.Selection) {
        fmt.Println(selection.Text())
    })
}
NewDocumentFromReader() 返回了一个*Document和error。Document代表一个将要被操作的HTML文档。

Find() 是获取当前匹配元素集中每个元素的子代,参数是x选择器 ,它返回一个包含这些匹配元素的新选择对象。在例子中我们使用的是元素选择器P,它会帮我们匹配出所有的p标签 。

Each() 是迭代器,它会循环遍历选择的节点,它的参数是一个匿名函数,匿名函数拥有2个参数,一个是元素的索引位置,还有一个就是选择的结果集匹配到的内容都在它的里面。

Text() 则是获取匹配元素集中的文本内容

选择器: jqery的选择器大多 goquery也能使用  例如  

  1. 基于HTML Element 元素的选择器   比如 div  span  p   a
  2. ID 选择器
  3. Class 选择器
  4. 属性选择器  p[class=content1]  div[my^=zh] div[my~=zh] div[my$=zh]  div[my!=zh] div[my*=zh]
  5. parent > childre(父元素下匹配所有的子元素) 
  6. parent + brother(相邻的同级标签)
  7. parent childrens(父元素下的所有子元素)
  8. parent ~ brother(不相邻的同级标签)

过滤器

  1. container ()     p:container('a')筛选出内容包含a的p标签

类似函数的位置操作

  1. 类似函数的位置操作
  • Find(selection) *Selection //根据选择器查找节点集
  • Eq(index int) *Selection   //根据索引获取某个节点集
  • First() *Selection             //获取第一个子节点集
  • Last() *Selection              //获取最后一个子节点集
  • Next() *Selection //获取下一个兄弟节点集
  • NextAll() *Selection //获取后面所有兄弟节点集
  • Prev() *Selection //前一个兄弟节点集
  • Get(index int) *html.Node //根据索引获取一个节点
  • Index() int //返回选择对象中第一个元素的位置
  • Slice(start, end int) *Selection //根据起始位置获取子节点集
  1. 循环遍历选择的节点
  • Each(f func(int, *Selection)) *Selection //遍历

  • EachWithBreak(f func(int, *Selection) bool) *Selection //可中断遍历

  • Map(f func(int, *Selection) string) (result []string) //返回字符串数组

  1. 检测或获取节点属性值
  • Attr(), RemoveAttr(), SetAttr() //获取,移除,设置属性的值

  • AddClass(), HasClass(), RemoveClass(), ToggleClass()

  • Html() //获取该节点的html

  • Length() //返回该Selection的元素个数

  • Text() //获取该节点的文本值

  1. 在文档树之间来回跳转(常用的查找节点方法)
  • Children() //返回selection中各个节点下的孩子节点

  • Contents() //获取当前节点下的所有节点

  • Find() //查找获取当前匹配的元素

  • Next() //下一个元素

  • Prev() //上一个元素

高阶用法

doc.Find("#content-left .article ").Each(func(i int, s *goquery.Selection) {
    url, _ := s.Find("a[class]").Attr("href")
    hash["all_content"] = sub_doc.Find(".content").Text()
}

 

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值