简单爬虫 | 豆包MarsCode AI刷题

137 阅读3分钟

爬虫

爬虫大体上分为两种类型,一种是爬取网页源码(一般是 HTML),另一种就是模拟请求 API 获取数据。这里我们先介绍爬取网页源码。以百度贴吧为例。

——仅供学习交流使用,如有侵权联系删除。

基本框架

写爬虫其实就是写客户端,只不过请求的对象由浏览器变为自己的编译器了。因此我们要了解爬取网站的URL等信息。

package main
import (
    "fmt"
    "net/http"
    "log"
)
func main (){
    res,err := http.Get("path")
    if err != nil {
        log.Fatalln(err.Error())
        return
    }
    defer res.Body.Close()
    body,err := ioutil.ReadAll(res.Body)
    fmt.Println(body)
}

基本就是以上的框架,因此我们爬取其他网页源码只需要对其进行扩展即可。

踩点网站

  • 第一页
https://tieba.baidu.com/f?kw=%E6%B0%B8%E5%8A%AB%E6%97%A0%E9%97%B4&ie=utf-8&pn=0
  • 第二页
https://tieba.baidu.com/f?kw=%E6%B0%B8%E5%8A%AB%E6%97%A0%E9%97%B4&ie=utf-8&pn=50

由此可以推断出其页面之间的规律是pn+=50

初版代码

package main
​
import (
    "fmt"
    "log"
    "net/http"
    "os"
    "strconv"
)
func Working(start, end int){
    for i := start; i <= end; i++{
        url :="https://tieba.baidu.com/f?kw=%E6%B0%B8%E5%8A%AB%E6%97%A0%E9%97%B4&ie=utf-8&pn="+strconv.Itoa((i - 1) * 50)
        result,err := HttpGet(url)
        if err != nil {
            log.Fatalln(err.Error())
            return
        }
        path := "./tieba/"+"第"+strconv.Itoa(i)+"页"+".html"
        f,err := os.Create(path)
        if err != nil {
            log.Fatalln(err.Error())
            return
        }
        f.WriteString(result) //写入
        f.Close() //直接关闭
    }
}
func HttpGet(url string)(result string, err error){
    res, err1 := http.Get(url)
    if err1 != nil{
        err = err1
        return 
    }
    defer res.Body.Close()
​
    buf := make([]byte,4096)
    for {
        n, err2 := res.Body.Read(buf)
        if n == 0 {
            fmt.Println("读取完毕")
            break
        }
        if err != nil {
            err = err2
            return 
        }
        result += string(buf[:n])
    }
    return
}
func main (){
    //https://tieba.baidu.com/f?kw=%E6%B0%B8%E5%8A%AB%E6%97%A0%E9%97%B4&ie=utf-8&pn=50 //第二页
    //https://tieba.baidu.com/f?kw=%E6%B0%B8%E5%8A%AB%E6%97%A0%E9%97%B4&ie=utf-8&pn=100 //
    start, end := 0, 0
    fmt.Print("请输入爬取的起始页:(>=1)")
    fmt.Scan(&start)
    fmt.Print("请输入爬取的终止页:(>=start)")
    fmt.Scan(&end)
    Working(start,end)
}

上面的代码只是对网站源码简单的爬取并保存成.html文件,我们并没有对其进行数据的筛选与获取。以下是上面代码的一部分要点:

  • 在HttpGet中对URL进行访问,并获取源码result返回到Working函数中,并且在遇到错误时候把所有的错误返回到Working中。
  • working中,创建文件之后写入文件,应该立即把文件关闭,避免其他页面的源码写入。

优化

只有一个线程在爬,肯定是比较慢的,因此我们可以搞并发式爬取,这只需要对原有代码一点点改动即可。

package main
import (
    "fmt"
    "log"
    "net/http"
    "os"
    "strconv"
)
func pa(i int,ch chan int){
    url :="https://tieba.baidu.com/f?kw=%E6%B0%B8%E5%8A%AB%E6%97%A0%E9%97%B4&ie=utf-8&pn="+strconv.Itoa((i - 1) * 50)
    result,err := HttpGet(url)
    if err != nil {
        log.Fatalln(err.Error())
        return
    }
    path := "./tieba/"+"第"+strconv.Itoa(i)+"页"+".html"
    f,err := os.Create(path)
    if err != nil {
        log.Fatalln(err.Error())
        return
    }
    f.WriteString(result) //写入
    f.Close() //直接关闭
    ch <- i
}
func Working(start, end int,ch chan int){
    for i := start; i <= end; i++{
        go pa(i,ch)
    }
    for i := start; i <= end; i++{
        fmt.Printf("第%d页读取完毕\n",<-ch)
    }
}
func HttpGet(url string)(result string, err error){
    res, err1 := http.Get(url)
    if err1 != nil{
        err = err1
        return 
    }
    defer res.Body.Close()
​
    buf := make([]byte,4096)
    for {
        n, err2 := res.Body.Read(buf)
        if n == 0 {
            break
        }
        if err != nil {
            err = err2
            return 
        }
        result += string(buf[:n])
    }
    return
}
func main (){
    //https://tieba.baidu.com/f?kw=%E6%B0%B8%E5%8A%AB%E6%97%A0%E9%97%B4&ie=utf-8&pn=50 //第二页
    //https://tieba.baidu.com/f?kw=%E6%B0%B8%E5%8A%AB%E6%97%A0%E9%97%B4&ie=utf-8&pn=100 //
    ch := make(chan int)
    start, end := 0, 0
    fmt.Print("请输入爬取的起始页:(>=1)")
    fmt.Scan(&start)
    fmt.Print("请输入爬取的终止页:(>=start)")
    fmt.Scan(&end)
    Working(start,end,ch )
}

这样的话我们爬取网页源码的速度就快了很多。因此我们未来爬取网页源码的时候就可以使用并发式爬取。

而为了进一步筛选数据,我们可以使用一些第三方库,也可以使用正则表达式。