I took a look at that new ChatGPT and I can officially say – the future is now. Take a look at what it does, after writing it a simple nice request:
The first result did not work, as it had unused variable and golang does not allow it, but then it started giving working results:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
package main import ( "fmt" "net/http" "github.com/PuerkitoBio/goquery" ) func main() { title, err := getTitle("https://www.vitoshacademy.com") if err != nil { fmt.Println(err) } else { fmt.Println(title) } } func getTitle(url string) (string, error) { response, err := http.Get(url) if err != nil { return "", err } defer response.Body.Close() doc, err := goquery.NewDocumentFromReader(response.Body) if err != nil { return "", err } return doc.Find("title").First().Text(), nil } |
Then I asked it to try to do it without the github libraries and after only 8 wrong tries, it returned this working beauty:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 |
package main import ( "fmt" "net/http" "golang.org/x/net/html" "io/ioutil" "strings" ) func main() { title, err := getTitle("https://www.google.com") if err != nil { fmt.Println(err) } else { fmt.Println(title) } } func getTitle(url string) (string, error) { response, err := http.Get(url) if err != nil { return "", err } defer response.Body.Close() bytes, err := ioutil.ReadAll(response.Body) if err != nil { return "", err } reader := strings.NewReader(string(bytes)) doc, err := html.Parse(reader) if err != nil { return "", err } var title string var f func(*html.Node) f = func(n *html.Node) { if n.Type == html.ElementNode && n.Data == "title" { title = n.FirstChild.Data } for c := n.FirstChild; c != nil; c = c.NextSibling { f(c) } } f(doc) return title, nil } |
Then I asked it to do it with less libraries and with less lines and both times it produced a working code. I asked it to do it without a loop, and it gave a working solution as well (after a few corrections from my site):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
package main import ( "fmt" "net/http" "golang.org/x/net/html" ) func main() { title, err := getTitle("https://www.google.com") if err != nil { fmt.Println(err) } else { fmt.Println(title) } } func getTitle(url string) (string, error) { resp, err := http.Get(url) if err != nil { return "", err } defer resp.Body.Close() doc, err := html.Parse(resp.Body) if err != nil { return "", err } var title string var f func(*html.Node) f = func(n *html.Node) { if n.Type == html.ElementNode && n.Data == "title" { title = n.FirstChild.Data } for c := n.FirstChild; c != nil; c = c.NextSibling { f(c) } } f(doc) return title, nil } |
Asked for meaningful names of the variables and it agreed that meaningful names was a good idea:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
package main import ( "fmt" "net/http" "golang.org/x/net/html" ) func main() { title, err := GetTitle("https://www.google.com") if err != nil { fmt.Println(err) } else { fmt.Println(title) } } func GetTitle(url string) (string, error) { resp, err := http.Get(url) if err != nil { return "", err } defer resp.Body.Close() doc, err := html.Parse(resp.Body) if err != nil { return "", err } var title string var findTitle func(*html.Node) findTitle = func(node *html.Node) { if node.Type == html.ElementNode && node.Data == "title" { title = node.FirstChild.Data } for child := node.FirstChild; child != nil; child = child.NextSibling { findTitle(child) } } findTitle(doc) return title, nil } |
I asked it a few times to optimize it, and once the optimization was even passing the website argument like this:
1 |
title, err := GetTitle("<html><head><title>This is the title</title></head></html>") |
Really, this is no doubt faster! 🙂 And some kind of strange creativity, for not needing to access the site at all.
That’s not all!
I asked it to make a library with useful webfunctions, and it created one with the GetTitle() function and then proposed to add two more functions. These are its words:
You can expand this library by adding more functions that are commonly used in web development, such as functions to make HTTP requests, parse HTML or JSON, and so on. Please let me know if you have any other questions or concerns.
And ChatGPT was correct! So, long story short, this is the small library it created, named web:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 |
package web import ( "bytes" "encoding/json" "io/ioutil" "net/http" "golang.org/x/net/html" ) // GetTitle returns the title of a web page func GetTitle(url string) (string, error) { resp, err := http.Get(url) if err != nil { return "", err } defer resp.Body.Close() doc, err := html.Parse(resp.Body) if err != nil { return "", err } var title string var findTitle func(*html.Node) findTitle = func(node *html.Node) { if node.Type == html.ElementNode && node.Data == "title" { title = node.FirstChild.Data } for child := node.FirstChild; child != nil; child = child.NextSibling { findTitle(child) } } findTitle(doc) return title, nil } // GetJSON makes an HTTP GET request to the specified URL and returns the response as a map func GetJSON(url string) (map[string]interface{}, error) { resp, err := http.Get(url) if err != nil { return nil, err } defer resp.Body.Close() body, err := ioutil.ReadAll(resp.Body) if err != nil { return nil, err } var data map[string]interface{} err = json.Unmarshal(body, &data) if err != nil { return nil, err } return data, nil } // PostJSON makes an HTTP POST request to the specified URL with the specified data and returns the response as a map func PostJSON(url string, data map[string]interface{}) (map[string]interface{}, error) { jsonData, err := json.Marshal(data) if err != nil { return nil, err } resp, err := http.Post(url, "application/json", bytes.NewBuffer(jsonData)) if err != nil { return nil, err } defer resp.Body.Close() body, err := ioutil.ReadAll(resp.Body) if err != nil { return nil, err } var responseData map[string]interface{} err = json.Unmarshal(body, &responseData) if err != nil { return nil, err } return responseData, nil } |
And these are the tests it wrote:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
package web import ( "testing" ) func TestGetTitle(t *testing.T) { title, err := GetTitle("https://www.google.com") if err != nil { t.Errorf("GetTitle returned an error: %v", err) } if title != "Google" { t.Errorf("GetTitle returned the wrong title. Expected: Google, got: %s", title) } } func TestPostJSON(t *testing.T) { data := map[string]interface{}{ "key1": "value1", "key2": "value2", } _, err := PostJSON("https://jsonplaceholder.typicode.com/posts", data) if err != nil { t.Errorf("PostJSON returned an error: %v", err) } } |
The future is now!