如何在Go中将并发web刮板的结果输出到CSV?

如何在Go中将并发web刮板的结果输出到CSV?,csv,go,concurrency,web-scraping,goquery,Csv,Go,Concurrency,Web Scraping,Goquery,我是新手,正在尝试利用Go中的并发性构建一个基本的scraper,从url中提取标题、元描述和元关键字 我能够以并发方式将结果打印到终端,但不知道如何将输出写入CSV。我已经尝试了很多我能想到的关于Go的有限知识的变体,很多最终打破了并发性——所以我有点失去了理智 下面是我的代码和URL输入文件-提前感谢您提供的任何提示 // file name: metascraper.go package main import ( // import standard libraries

我是新手,正在尝试利用Go中的并发性构建一个基本的scraper,从url中提取标题、元描述和元关键字

我能够以并发方式将结果打印到终端,但不知道如何将输出写入CSV。我已经尝试了很多我能想到的关于Go的有限知识的变体,很多最终打破了并发性——所以我有点失去了理智

下面是我的代码和URL输入文件-提前感谢您提供的任何提示

// file name: metascraper.go
package main

import (
    // import standard libraries
    "encoding/csv"
    "fmt"
    "io"
    "log"
    "os"
    "time"
    // import third party libraries
    "github.com/PuerkitoBio/goquery"
)

func csvParsing() {
    file, err := os.Open("data/sample.csv")
    checkError("Cannot open file ", err)

    if err != nil {
        // err is printable
        // elements passed are separated by space automatically
        fmt.Println("Error:", err)
        return
    }

    // automatically call Close() at the end of current method
    defer file.Close()
    //
    reader := csv.NewReader(file)
    // options are available at:
    // http://golang.org/src/pkg/encoding/csv/reader.go?s=3213:3671#L94
    reader.Comma = ';'
    lineCount := 0

    fileWrite, err := os.Create("data/result.csv")
    checkError("Cannot create file", err)
    defer fileWrite.Close()

    writer := csv.NewWriter(fileWrite)
    defer writer.Flush()

    for {
        // read just one record
        record, err := reader.Read()
        // end-of-file is fitted into err
        if err == io.EOF {
            break
        } else if err != nil {
            fmt.Println("Error:", err)
            return
        }

        go func(url string) {
            // fmt.Println(msg)
            doc, err := goquery.NewDocument(url)
            if err != nil {
                checkError("No URL", err)
            }

            metaDescription := make(chan string, 1)
            pageTitle := make(chan string, 1)

            go func() {
                // time.Sleep(time.Second * 2)
                // use CSS selector found with the browser inspector
                // for each, use index and item
                pageTitle <- doc.Find("title").Contents().Text()

                doc.Find("meta").Each(func(index int, item *goquery.Selection) {
                    if item.AttrOr("name", "") == "description" {
                        metaDescription <- item.AttrOr("content", "")
                    }
                })
            }()
            select {
            case res := <-metaDescription:
                resTitle := <-pageTitle
                fmt.Println(res)
                fmt.Println(resTitle)

                // Have been trying to output to CSV here but it's not working

                // writer.Write([]string{url, resTitle, res})
                // err := writer.WriteString(`res`)
                // checkError("Cannot write to file", err)

            case <-time.After(time.Second * 2):
                fmt.Println("timeout 2")
            }

        }(record[0])

        fmt.Println()

        lineCount++
    }
}

func main() {

    csvParsing()

    //Code is to make sure there is a pause before program finishes so we can see output
    var input string
    fmt.Scanln(&input)
}

func checkError(message string, err error) {
    if err != nil {
        log.Fatal(message, err)
    }
}

在您提供的代码中,您对以下代码进行了注释:

// Have been trying to output to CSV here but it's not working
err = writer.Write([]string{url, resTitle, res})
checkError("Cannot write to file", err)
fileWrite, err := os.Create("data/result.csv")
checkError("Cannot create file", err)
defer fileWrite.Close()
此代码是正确的,除非您有一个问题。 在前面的函数中,您有以下代码:

// Have been trying to output to CSV here but it's not working
err = writer.Write([]string{url, resTitle, res})
checkError("Cannot write to file", err)
fileWrite, err := os.Create("data/result.csv")
checkError("Cannot create file", err)
defer fileWrite.Close()
一旦退出
csvParsing()
func,此代码将导致fileWriter关闭。 因为您已使用defer关闭了fileWriter,所以无法在并发函数中对其进行写入

解决方案:
您需要在并发函数中使用
延迟fileWrite.Close()
或类似工具,以便在写入文件编写器之前不要关闭它。

在您提供的代码中,您对以下代码进行了注释:

// Have been trying to output to CSV here but it's not working
err = writer.Write([]string{url, resTitle, res})
checkError("Cannot write to file", err)
fileWrite, err := os.Create("data/result.csv")
checkError("Cannot create file", err)
defer fileWrite.Close()
此代码是正确的,除非您有一个问题。 在前面的函数中,您有以下代码:

// Have been trying to output to CSV here but it's not working
err = writer.Write([]string{url, resTitle, res})
checkError("Cannot write to file", err)
fileWrite, err := os.Create("data/result.csv")
checkError("Cannot create file", err)
defer fileWrite.Close()
一旦退出
csvParsing()
func,此代码将导致fileWriter关闭。 因为您已使用defer关闭了fileWriter,所以无法在并发函数中对其进行写入

解决方案:
您需要在并发函数中使用
延迟fileWrite.Close()
或类似工具,这样您就不会在写入文件之前关闭文件写入程序。

您的想法是正确的,您只是在有机会写入文件写入程序之前关闭了文件写入程序。您的想法是正确的,在您有机会写入之前,您只是简单地关闭了
fileWriter
。我尝试了以下
fmt.Println(resTitle)err:=writer.write([]字符串{“url”,“resTitle”,“res”})err2:=writer.write([]字符串{url,resTitle,res})checkError(“无法写入文件”,err2)
并移动了
延迟文件写入.Close()
lineCount++}
之后,但我只得到了一个空白的csv文件,这里是go Playder链接:这个更新的链接应该避免出现“文件已关闭”的错误。为此,我删除了
延迟编写器.Close()
。如果您想要一个带有
延迟writer.Close()
的示例,则可以使用等待组等待并发函数在文件关闭之前完成执行。下面是一个示例:我尝试了以下
fmt.Println(resTitle)err:=writer.Write([]string{“url”,“resTitle”,“res”})err2:=writer.Write([]string{url,resTitle,res})checkError(“无法写入文件”,err)checkError(“无法写入文件”,err2)
并移动了
延迟文件写入.Close()
lineCount++}
之后,我只得到一个空白的csv文件,这里是go Playerly链接:这个更新的链接应该避免出现“文件已关闭”的错误。为此,我删除了
延迟编写器.Close()
。如果您想要一个带有
延迟writer.Close()
的示例,则可以使用等待组等待并发函数在文件关闭之前完成执行。以下是一个例子: