Membuat Web Crawler menggunakan Golang
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.
1go mod init github.com/santekno/learn-golang-web-crawlerMembuat 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.
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 fungsivisit. - Fungsi terakhir
func Crawl(url string, depth int)digunakan untuk mendeteksi URL yang sama ditemukan itu agar tidak perlu di Crawl berulang. - Fungsi
maindigunakan 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.
1go run main.goProgram 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.
1found: https://www.santekno.com/jenis-jenis-name-server/
2found: https://www.santekno.com/tutorial/hardware/
3time taken: 3m7.149923291sBisa 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.
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.
1results := make(chan *result)Crawler ini akan kita tambahkan channel agar bisa menggunakan goroutine dan modifikasi fungsi Crawler menjadi seperti dibawah ini.
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.
1go run main.goSetelah selesai dijalankan maka akan terlihat berapa lama eksekusi proses untuk melakukan Crawler ini.
1found: https://www.santekno.com/tags/encoder
2found: https://www.santekno.com/categories/tutorial/page/2/
3time taken: 11.673643875sLuar 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.