Automate the Boring Stuff with Go, Ch. 11: Web Requests

// written in 2023: tools, versions and prices may have changed since.

Part of Automate the Boring Stuff with Go, a beginner-friendly guide to programming in Go.

Request

package main

import (
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"os"
)

func main() {

	pwdDir, _ := os.Getwd()
	fLog, err := os.OpenFile(pwdDir+"/log.txt", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0640)
	check(err, fLog)

	url := "http://www.gutenberg.org/cache/epub/1112/pg1112.txt"

	resp, err := http.Get(url)
	check(err, fLog)

	defer resp.Body.Close()

	body, err := ioutil.ReadAll(resp.Body)
	check(err, fLog)

	fmt.Println(string(body))
}

// err check to log
func check(err error, fLog *os.File) {
	if err != nil {
		log.Fatalln(err)
	}
	log.SetOutput(io.MultiWriter(fLog, os.Stdout))
}

Search Term

package main

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

	"github.com/jackdanger/collectlinks"
)

func main() {

	pwdDir, _ := os.Getwd()
	fLog, err := os.OpenFile(pwdDir+"/log.txt", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0640)
	check(err, fLog)

	resp, err := http.Get("https://automatetheboringstuffwithgo.com")
	check(err, fLog)
	defer resp.Body.Close()

	links := collectlinks.All(resp.Body)

	for _, link := range links {
		fmt.Println(link)
	}
}

// err check to log
func check(err error, fLog *os.File) {
	if err != nil {
		log.Fatalln(err)
	}
	log.SetOutput(io.MultiWriter(fLog, os.Stdout))
}

Request Save

package main

import (
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"os"
)

func main() {
	pwdDir, _ := os.Getwd()
	fLog, err := os.OpenFile(pwdDir+"/log.txt", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0640)
	check(err, fLog)

	fSave, err := os.OpenFile(pwdDir+"/save.txt", os.O_CREATE|os.O_WRONLY, 0600)
	check(err, fLog)

	url := "http://www.gutenberg.org/cache/epub/1112/pg1112.txt"

	resp, err := http.Get(url)
	check(err, fLog)
	defer resp.Body.Close()

	body, err := ioutil.ReadAll(resp.Body)
	check(err, fLog)

	fSave.Write(body)
}

// err check to log
func check(err error, fLog *os.File) {
	if err != nil {
		log.Fatalln(err)
	}
	log.SetOutput(io.MultiWriter(fLog, os.Stdout))
}

Download XKCD

package main

import (
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"regexp"

	"strings"

	"strconv"

	"sort"

	"github.com/opesun/goquery"
)

func main() {
	pwdDir, _ := os.Getwd()
	fLog, err := os.OpenFile(pwdDir+"/log.txt", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0640)
	check(err, fLog)

	url := `http://xkcd.com`

	err = os.Mkdir(pwdDir+"/xkcd", 0775)
	check(err, fLog)

	resp, err := http.Get(url)
	log.Printf("URL:	%v", url)
	check(err, fLog)
	defer resp.Body.Close()

	x, err := goquery.Parse(resp.Body)
	check(err, fLog)

	var urlInt []int
	regLastLink, _ := regexp.Compile(`\/[0-9]{1,}\/`)
	for _, i := range x.Find("a").Attrs("href") {
		if regLastLink.MatchString(i) {
			a, err := strconv.Atoi(strings.Trim(i, "/"))
			check(err, fLog)
			urlInt = append(urlInt, a)
		}
	}
	sort.Ints(urlInt)
	fmt.Println(urlInt[len(urlInt)-1])

	for k := urlInt[len(urlInt)-1] + 1; k >= 1; k-- {
		resp, err := http.Get(url + "/" + strconv.Itoa(k))
		log.Printf("URL: 	%v", url+"/"+strconv.Itoa(k))
		check(err, fLog)
		defer resp.Body.Close()

		x, err := goquery.Parse(resp.Body)
		check(err, fLog)

		regStr, _ := regexp.Compile(`comics`)
		for _, i := range x.Find("img").Attrs("src") {
			if regStr.MatchString(i) {

				nameFile := strings.Split(i, "/comics/")

				name := pwdDir + "/xkcd/" + nameFile[1]
				fSave, err := os.OpenFile(name, os.O_CREATE|os.O_WRONLY, 0640)
				check(err, fLog)

				respImg, err := http.Get("http:" + i)
				log.Printf("http	%v", "http:"+i)
				check(err, fLog)

				defer respImg.Body.Close()

				bodyImg, err := ioutil.ReadAll(respImg.Body)
				check(err, fLog)

				log.Printf("name:	%v", name)
				fSave.Write(bodyImg)
			}
		}
	}

	log.SetOutput(io.MultiWriter(fLog, os.Stdout))
}

// err check to log
func check(err error, fLog *os.File) {
	if err != nil {
		log.Println(err)
	}
	log.SetOutput(io.MultiWriter(fLog, os.Stdout))
}

Selenium

package main

import (
	"encoding/csv"
	"github.com/tebeka/selenium"
	"github.com/tebeka/selenium/chrome"
	"log"
	"os"
)

// define a custom data type for the scraped data
type Product struct {
	name, price string
}

func main() {
	// where to store the scraped data
	var products []Product

	// initialize a Chrome browser instance on port 4444
	service, err := selenium.NewChromeDriverService("./chromedriver/chromedriver", 4444)
	if err != nil {
		log.Fatal("Error:", err)
	}

	defer service.Stop()

	// configure the browser options
	caps := selenium.Capabilities{}
	caps.AddChrome(chrome.Capabilities{Args: []string{
		"--headless", // comment out this line for testing
	}})

	// create a new remote client with the specified options
	driver, err := selenium.NewRemote(caps, "")
	if err != nil {
		log.Fatal("Error:", err)
	}

	// maximize the current window to avoid responsive rendering
	err = driver.MaximizeWindow("")
	if err != nil {
		log.Fatal("Error:", err)
	}

	// visit the target page
	err = driver.Get("https://scrapingclub.com/exercise/list_infinite_scroll/")
	if err != nil {
		log.Fatal("Error:", err)
	}

	// select the product elements
	productElements, err := driver.FindElements(selenium.ByCSSSelector, ".post")
	if err != nil {
		log.Fatal("Error:", err)
	}

	// iterate over the product elements
	// and extract data from them
	for _, productElement := range productElements {
		// select the name and price nodes
		nameElement, err := productElement.FindElement(selenium.ByCSSSelector, "h4")
		priceElement, err := productElement.FindElement(selenium.ByCSSSelector, "h5")
		// extract the data of interest
		name, err := nameElement.Text()
		price, err := priceElement.Text()
		if err != nil {
			log.Fatal("Error:", err)
		}

		// add the scraped data to the list
		product := Product{}
		product.name = name
		product.price = price
		products = append(products, product)
	}

	// export the scraped data to CSV
	file, err := os.Create("products.csv")
	if err != nil {
		log.Fatal("Error:", err)
	}

	defer file.Close()

	// initialize a file writer
	writer := csv.NewWriter(file)

	// define the CSV headers
	headers := []string{
		"name",
		"price",
	}

	// write the column headers
	writer.Write(headers)

	// adding each product to the CSV output file
	for _, product := range products {

		// converting a Product to an array of strings
		record := []string{
			product.name,
			product.price,
		}

		// writing a new CSV record
		writer.Write(record)
	}

	defer writer.Flush()
}
package main

import (
	"fmt"
	"github.com/PuerkitoBio/goquery"
	"log"
	"net/http"
	"os"
)

func getData() {
	// change the query t be a var
	// pass in single search term
	url := fmt.Sprintf("https://www.google.com/search?q=%s&gl=us&hl=en", os.Args[1])
	req, err := http.NewRequest("GET", url, nil)
	if err != nil {
		log.Fatal(err)
	}

	req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/101.0.4951.54 Safari/537.36")
	client := &http.Client{}
	res, err := client.Do(req)
	if err != nil {
		log.Fatal(err)
	}
	defer res.Body.Close()

	doc, err := goquery.NewDocumentFromReader(res.Body)
	if err != nil {
		log.Fatal(err)
	}
	c := 0
	doc.Find("div.g").Each(func(i int, result *goquery.Selection) {
		title := result.Find("h3").First().Text()
		link, _ := result.Find("a").First().Attr("href")
		snippet := result.Find(".VwiC3b").First().Text()

		fmt.Printf("Title: %s\n", title)
		fmt.Printf("Link: %s\n", link)
		fmt.Printf("Snippet: %s\n", snippet)
		fmt.Printf("Position: %d\n", c+1)
		fmt.Println()

		c++
	})
}

func main() {
	getData()
}

cd ../blog