Skip to content
Santekno.com | Level Up Your Engineering Skills
ID
📖 0%
20 Dec 2023 · 6 mnt baca ·Artikel 113 / 119
Go

Membuat Web Crawler menggunakan Golang

IH
Ihsan Arif
Penulis di Santekno · Backend Engineer

Web Crawler sering kita gunakan untuk mengambil sesuatu pada suatu website sehingga kita mendapatkan konten yang dibutuhkan. Hal ini biasanya digunakan untuk kebutuhan konten. Dalam hal ini kita akan coba menggunakan Golang untuk membuat Web Crawler sederhana dan akan mengambil beberapa konten seperti URL yang ada pada halaman suatu website tersebut.

Persiapan Projek

Sekarang kita akan buat projek baru dengan membuat folder learn-golang-web-crawler. Setelah itu buat inisialisasi projek module dengan perintah ini.

bash
1go mod init github.com/santekno/learn-golang-web-crawler

Membuat Web Crawler menggunakan sequential

Pertama kita akan coba terlebih dahulu menggunakan metode sequential yang mana kita hanya melakukan perulangan biasa untuk melakukan crawler ke website tersebut.

Buat file main.go lalu isi file tersebut dengan kode dibawah ini.

go
 1package main
 2
 3import (
 4	"fmt"
 5	"net/http"
 6	"time"
 7
 8	"golang.org/x/net/html"
 9)
10
11var fetched map[string]bool
12
13func Crawl(url string, depth int) {
14	if depth < 0 {
15		return
16	}
17	urls, err := findLinks(url)
18	if err != nil {
19		fmt.Println(err)
20		return
21	}
22	fmt.Printf("found: %s\n", url)
23	fetched[url] = true
24	for _, u := range urls {
25		if !fetched[u] {
26			Crawl(u, depth-1)
27		}
28	}
29}
30
31func findLinks(url string) ([]string, error) {
32	resp, err := http.Get(url)
33	if err != nil {
34		return nil, err
35	}
36	if resp.StatusCode != http.StatusOK {
37		resp.Body.Close()
38		return nil, fmt.Errorf("getting %s: %s", url, resp.Status)
39	}
40	doc, err := html.Parse(resp.Body)
41	resp.Body.Close()
42	if err != nil {
43		return nil, fmt.Errorf("parsing %s as HTML: %v", url, err)
44	}
45	return visit(nil, doc), nil
46}
47
48func visit(links []string, n *html.Node) []string {
49	if n.Type == html.ElementNode && n.Data == "a" {
50		for _, a := range n.Attr {
51			if a.Key == "href" {
52				links = append(links, a.Val)
53			}
54		}
55	}
56	for c := n.FirstChild; c != nil; c = c.NextSibling {
57		links = visit(links, c)
58	}
59	return links
60}
61
62func main() {
63	fetched = make(map[string]bool)
64	now := time.Now()
65	Crawl("http://santekno.com", 2)
66	fmt.Println("time taken:", time.Since(now))
67}

Beberapa penjelasan agar teman-teman paham setiap fungsi yang dibuat digunakan untuk apa berikut ini penjelasannya.

  • Fungsi visit(links []string, n *html.Node) []stringdigunakan untuk menelusuri pada satu halaman web terdapat URL apa saja dan jika URL tersebut pernah diakses maka akan melakukan akses ulang ke URL yang berbeda nantinya semua URL pada website URL pertama akan dikembalikan sebagai hasil.
  • Fungsi findLinks(url string) ([]string, error) digunakan untuk menemukan URL yang akan di Crawl dengan melakukan pengecekan apakah website tersebut tersedia atau tidak dan mengambil semua halaman HTML-nya untuk dikirim ke fungsi visit.
  • Fungsi terakhir func Crawl(url string, depth int) digunakan untuk mendeteksi URL yang sama ditemukan itu agar tidak perlu di Crawl berulang.
  • Fungsi main digunakan untuk mendefinisikan URL yang akan di crawl dan sebagai fungsi utama dari program ini.

Apakah teman-teman sudah memahami fungsinya satu persatu? Jika sudah kita akan coba langsung menjalankan program ini dengan perintah dibawah ini.

bash
1go run main.go

Program akan berjalan dan melakukan akses ke URL yang sudah kita definisikan di dalam fungsi main. Jangan lupa Pastikan internet pada komputer atau laptop kamu berjalan dengan lancar agar proses-nya pun tidak akan terlalu lama.

Jika sudah selesai dijalankan maka akan keluar pada terminal seperti dibawah ini.

bash
1found: https://www.santekno.com/jenis-jenis-name-server/
2found: https://www.santekno.com/tutorial/hardware/
3time taken: 3m7.149923291s

Bisa kita lihat berarti untuk melakukan penelusuran atau Crawler Website santekno.com ini membutuhkan sekitar 3 menit 7 detik. Lumayan lama juga dan ini juga dikondisikan dengan internet yang ada pada laptop kalian.

Jika hanya 1 URL saja mungkin ini lebih cepat dan bagaimana kalau misalkan kita ingin melakukan Crawler ke 100 URL/Website maka jika sequential kita perlu membutuhkan minimal 100 kali dari yang pertama yaitu 300 menit dan ini sangat membutuhkan waktu yang lama sekali.

Lalu bagaimana nih agar prosesnya lebih cepat lagi untuk melakukan Crawler Web? Pada proses selanjutnya kita akan coba mengubah proses Crawler tersebut menggunakan Concurrent yang sudah pernah kita pelajari sebelumnya.

Mengubah Crawler Web menggunakan Concurrent

Kita akan memodifikasi Crawler Web sebelumnya dengan menambahkan beberapa improvement yaitu dengan menggunakan channel. Buat struct terlebih dahulu seperti ini.

go
1type result struct {
2	url   string
3	urls  []string
4	err   error
5	depth int
6}

Struct ini digunakan untuk menyimpan URL yang akan kita Crawler. Tambahkan channel pada fungsi Crawler diawal fungsi.

go
1results := make(chan *result)

Crawler ini akan kita tambahkan channel agar bisa menggunakan goroutine dan modifikasi fungsi Crawler menjadi seperti dibawah ini.

go
 1func Crawl(url string, depth int) {
 2	results := make(chan *result)
 3
 4	fetch := func(url string, depth int) {
 5		urls, err := findLinks(url)
 6		results <- &result{url, urls, err, depth}
 7	}
 8
 9	go fetch(url, depth)
10	fetched[url] = true
11
12	for fetching := 1; fetching > 0; fetching-- {
13		res := <-results
14		if res.err != nil {
15			fmt.Println(res.err)
16			continue
17		}
18
19		fmt.Printf("found: %s\n", res.url)
20		if res.depth > 0 {
21			for _, u := range res.urls {
22				if !fetched[u] {
23					fetching++
24					go fetch(u, res.depth-1)
25					fetched[u] = true
26				}
27			}
28		}
29	}
30	close(results)
31}

Bisa kita lihat kita membuat suatu fungsi fetch yang mana didalamnya akan memanggil fungsi findLinks dan menyimpan hasilnya ke dalam channel results. Perlu diketahui setelah itu fungsi fetch tersebut akan kita jalankan dengan menggunakan goroutine seperti pada awal penjelasan disinggung.

Lihat kode selanjutnya yaitu melakukan perulangan. Pada kode ini kita akan mengambil semua data URL yang ada pada channel results. Kapan kode perulangan tersebut selesai? Perulangan ini akan selesai jika fetching valuenya sudah menjadi 0.

Baiklah, langsung saja kita jalankan modifikasi yang terakhir ini dengan perintah yang sama seperti diatas.

bash
1go run main.go

Setelah selesai dijalankan maka akan terlihat berapa lama eksekusi proses untuk melakukan Crawler ini.

bash
1found: https://www.santekno.com/tags/encoder
2found: https://www.santekno.com/categories/tutorial/page/2/
3time taken: 11.673643875s

Luar biasa sekali prosesnya pun menjadi lebih cepat yang awal membutuhkan sekitar 3 menit tetapi setelah kita modifikasi menggunakan concurrent kita meringkas waktu dan proses hanya 11 detik.

Kesimpulan

Web Crawler ini sering kita gunakan untuk kebutuhan-kebutuhan tertentu terutama jika kita ingin menganalisis data yang sudah ada pada suatu website tertentu. Maka jika kita ingin membuat Crawler Web menggunakan Golang coba bisa perhatikan dan gunakan concurrent agar bisa lebih efisien dalam mengerjakannya sehingga prosesnya lebih singkat dan tidak perlu membutuhkan waktu yang lebih lama apalagi jika kita melakukan Crawler Web bukan hanya satu saja.

Artikel Terkait

💬 Komentar