如何在Go中将并发web刮板的结果输出到CSV?
我是新手,正在尝试利用Go中的并发性构建一个基本的scraper,从url中提取标题、元描述和元关键字 我能够以并发方式将结果打印到终端,但不知道如何将输出写入CSV。我已经尝试了很多我能想到的关于Go的有限知识的变体,很多最终打破了并发性——所以我有点失去了理智 下面是我的代码和URL输入文件-提前感谢您提供的任何提示如何在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
// 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()
的示例,则可以使用等待组等待并发函数在文件关闭之前完成执行。以下是一个例子: