首页 > 其他分享 >基于Go1.19的站点模板爬虫教程

基于Go1.19的站点模板爬虫教程

时间:2024-07-17 12:28:27浏览次数:11  
标签:body string err Go1.19 nil resp 爬虫 html 模板

以下是基于 Go 1.19 的站点模板爬虫教程。我们将使用 Go 编程语言创建一个简单的网页爬虫,爬取指定网站的内容。我们将使用一些常见的 Go 库,例如 net/httpgolang.org/x/net/html,来处理 HTTP 请求和解析 HTML。

第一步:安装和设置

  1. 安装 Go

    • 如果你还没有安装 Go,请先从 Go 官方网站 下载并安装最新版本。
  2. 创建项目目录

    mkdir go-web-crawler
    cd go-web-crawler
    
  3. 初始化 Go 模块

    go mod init go-web-crawler
    

第二步:编写爬虫代码

  1. 创建主程序文件
    在项目目录中创建一个名为 main.go 的文件。

  2. 编写爬虫逻辑

    package main
    
    import (
        "fmt"
        "net/http"
        "golang.org/x/net/html"
        "log"
    )
    
    // fetch makes an HTTP request and returns the response body as a string
    func fetch(url string) (string, error) {
        resp, err := http.Get(url)
        if err != nil {
            return "", err
        }
        defer resp.Body.Close()
    
        if resp.StatusCode != http.StatusOK {
            return "", fmt.Errorf("error: status code %d", resp.StatusCode)
        }
    
        var body []byte
        body, err = io.ReadAll(resp.Body)
        if err != nil {
            return "", err
        }
    
        return string(body), nil
    }
    
    // parseHTML parses the HTML and prints the links found
    func parseHTML(body string) {
        doc, err := html.Parse(strings.NewReader(body))
        if err != nil {
            log.Fatal(err)
        }
        var f func(*html.Node)
        f = func(n *html.Node) {
            if n.Type == html.ElementNode && n.Data == "a" {
                for _, a := range n.Attr {
                    if a.Key == "href" {
                        fmt.Println(a.Val)
                        break
                    }
                }
            }
            for c := n.FirstChild; c != nil; c = c.NextSibling {
                f(c)
            }
        }
        f(doc)
    }
    
    func main() {
        url := "http://example.com"
        body, err := fetch(url)
        if err != nil {
            log.Fatal(err)
        }
        parseHTML(body)
    }
    

第三步:运行爬虫

  1. 运行程序
    在项目目录中执行以下命令运行爬虫:

    go run main.go
    

    你应该会看到控制台输出网站中所有的链接(<a href="..."> 标签的 href 属性值)。

第四步:扩展爬虫功能

  1. 处理相对链接
    解析链接时,你可能需要将相对链接转换为绝对链接。你可以使用 net/url 包中的 ResolveReference 方法来实现这一点。

  2. 并发请求
    为了提高效率,你可以使用 Goroutines 来并发地处理多个 URL。

  3. 避免重复抓取
    使用 map 数据结构来记录已经抓取过的 URL,避免重复抓取。

  4. 错误处理
    添加更健壮的错误处理机制,确保程序在遇到错误时不会崩溃。

示例代码:处理相对链接和并发请求

package main

import (
    "fmt"
    "log"
    "net/http"
    "golang.org/x/net/html"
    "io"
    "strings"
    "net/url"
    "sync"
)

var visited = make(map[string]bool)
var mu sync.Mutex

func fetch(url string) (string, error) {
    resp, err := http.Get(url)
    if err != nil {
        return "", err
    }
    defer resp.Body.Close()

    if resp.StatusCode != http.StatusOK {
        return "", fmt.Errorf("error: status code %d", resp.StatusCode)
    }

    body, err := io.ReadAll(resp.Body)
    if err != nil {
        return "", err
    }

    return string(body), nil
}

func parseHTML(baseURL *url.URL, body string, ch chan string) {
    doc, err := html.Parse(strings.NewReader(body))
    if err != nil {
        log.Fatal(err)
    }
    var f func(*html.Node)
    f = func(n *html.Node) {
        if n.Type == html.ElementNode && n.Data == "a" {
            for _, a := range n.Attr {
                if a.Key == "href" {
                    link, err := baseURL.Parse(a.Val)
                    if err != nil {
                        continue
                    }
                    mu.Lock()
                    if !visited[link.String()] {
                        visited[link.String()] = true
                        ch <- link.String()
                    }
                    mu.Unlock()
                    break
                }
            }
        }
        for c := n.FirstChild; c != nil; c = c.NextSibling {
            f(c)
        }
    }
    f(doc)
}

func worker(ch chan string, wg *sync.WaitGroup) {
    defer wg.Done()
    for url := range ch {
        body, err := fetch(url)
        if err != nil {
            log.Println(err)
            continue
        }
        u, err := url.Parse(url)
        if err != nil {
            log.Println(err)
            continue
        }
        parseHTML(u, body, ch)
    }
}

func main() {
    startURL := "http://example.com"
    ch := make(chan string)
    var wg sync.WaitGroup

    wg.Add(1)
    go worker(ch, &wg)

    ch <- startURL

    go func() {
        wg.Wait()
        close(ch)
    }()

    for link := range ch {
        fmt.Println(link)
    }
}

以上示例代码使用 Goroutines 和 Channel 来并发地处理 URL,并通过 sync.Mutex 确保对 visited map 的并发访问是安全的。你可以根据需要进一步扩展和优化爬虫的功能。

标签:body,string,err,Go1.19,nil,resp,爬虫,html,模板
From: https://blog.csdn.net/PingGuoAiShangMaiD/article/details/140491580

相关文章

  • 易优CMS模板标签tags标签调用
    【基础用法】标签:tags描述:TAG调用用法:{eyou:tagssort='now'getall='0'loop='100'}<ahref='{$field.link}'>{$field.tag}</a>(文档数:{$field.total}){/eyou:tags}属性:aid=''文档ID,在内容页可以不设置该属性typeid=''栏......
  • 莫队模板
    #include<bits/stdc++.h>usingnamespacestd;usingi64=longlong;constintN=1e6+5;intcnt[N],a[N];structquery{intl,r,id;};intmain(){ios::sync_with_stdio(false);cin.tie(nullptr);intn;cin>>n;......
  • vue基础day01(MVVM、绑定、事件、结构模板)
    vue基础一、什么是vue构建用户界面的渐进式框架,采用自底向上逐层应用开发核心理念:数据驱动视图,组件化开发二、框架和库的区别框架:一套完整的解决方案,对项目的侵入性较大,项目如果需要更换框架,需要重新架构整个项目库:提供了一个小的功能,对项目的侵入性较小,如果某个库无......
  • Asp .Net Core 系列:基于 T4 模板生成代码
    目录简介组成部分分类VisualStudio中使用T4模板创建T4模板文件2.编写T4模板3.转换模板中心控制Manager根据MySQL数据生成实体简介T4模板,即TextTemplateTransformationToolkit,是微软官方在VisualStudio中引入的一种代码生成引擎。自VisualStudio2008开始,T4模板就被......
  • 【模板】插头 DP
    为了利用位运算的良好性质,采用四进制状态压缩代替三进制通过哈希表压缩有限的状态与普通的按行滚动的滚动数组不同,插头DP中的滚动数组是按格滚动的因为布尔数组值默认为false,所以我们可以用“true”代表“可通行”转移时需要找到与之匹配的括号——实现算法时不能够总是代入......
  • Python爬虫Post请求返回值为-1000
    今天写了一个简单的爬虫程序,为了爬取kfc官网的餐厅数据,代码如下#ajax的post请求--肯德基官网defcreate_request(page):url='http://www.kfc.com.cn/kfccda/ashx/GetStoreList.ashx'data={ 'cname':'濮阳', 'pid':'', 'pageIndex':p......
  • 关于静态文件目录与模板引用和Nginx location块的适配设置
    项目配置文件内关于静态文件的设置项#静态文件的URL前缀STATIC_URL='/static/'#项目根目录的静态文件目录STATICFILES_DIRS=[os.path.join(BASE_DIR,'static'),os.path.join(BASE_DIR,'parallel/static'),os.path.join(BASE_DIR,'blog/static&#......
  • 海量元宇宙场景模板,视创云展解锁你的无限创意虚拟空间!
    ​一站式元宇宙虚拟活动云平台视创云展,集成了海量的元宇宙场景模板,并借助其强大的模块化功能体系,使得用户能够轻松跨越技术门槛,迅速创作出高质量的3D场景。用户可自由发挥创意,构建出独一无二的元宇宙空间,完美契合多样化的场景应用需求。1、海量模板,随心选择:视创云展拥有海量......
  • 模板——类模板2——继承,文件,友元
    1.类模板与继承1.1当子类继承的父类是一个类模板时,子类在声明时,要指定父类中T的类型1.2如果不指定,编译器无法给子类分配内存1.3如果想灵活指定父类中的T的类型,子类也需变成类模板template<classT>classBase{public: Tage;};//classSon:publicBase//错误,c++编译......
  • 模板——类模板1--与函数的关系
    1.类模板基本语法template<classT,classT2>类template<classNameType,classAgeType>classPerson{public: Person(NameTypename,AgeTypeage) { this->m_name=name; this->m_age=age; } voidShowPerson() { cout<<"姓名:&quo......