pemrograman

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.

go 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.

package main

import (
	"fmt"
	"net/http"
	"time"

	"golang.org/x/net/html"
)

var fetched map[string]bool

func Crawl(url string, depth int) {
	if depth < 0 {
		return
	}
	urls, err := findLinks(url)
	if err != nil {
		fmt.Println(err)
		return
	}
	fmt.Printf("found: %s\n", url)
	fetched[url] = true
	for _, u := range urls {
		if !fetched[u] {
			Crawl(u, depth-1)
		}
	}
}

func findLinks(url string) ([]string, error) {
	resp, err := http.Get(url)
	if err != nil {
		return nil, err
	}
	if resp.StatusCode != http.StatusOK {
		resp.Body.Close()
		return nil, fmt.Errorf("getting %s: %s", url, resp.Status)
	}
	doc, err := html.Parse(resp.Body)
	resp.Body.Close()
	if err != nil {
		return nil, fmt.Errorf("parsing %s as HTML: %v", url, err)
	}
	return visit(nil, doc), nil
}

func visit(links []string, n *html.Node) []string {
	if n.Type == html.ElementNode && n.Data == "a" {
		for _, a := range n.Attr {
			if a.Key == "href" {
				links = append(links, a.Val)
			}
		}
	}
	for c := n.FirstChild; c != nil; c = c.NextSibling {
		links = visit(links, c)
	}
	return links
}

func main() {
	fetched = make(map[string]bool)
	now := time.Now()
	Crawl("http://santekno.com", 2)
	fmt.Println("time taken:", time.Since(now))
}

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.

go 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.

found: https://www.santekno.com/jenis-jenis-name-server/
found: https://www.santekno.com/tutorial/hardware/
time 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.

type result struct {
	url   string
	urls  []string
	err   error
	depth int
}

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

results := make(chan *result)

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

func Crawl(url string, depth int) {
	results := make(chan *result)

	fetch := func(url string, depth int) {
		urls, err := findLinks(url)
		results <- &result{url, urls, err, depth}
	}

	go fetch(url, depth)
	fetched[url] = true

	for fetching := 1; fetching > 0; fetching-- {
		res := <-results
		if res.err != nil {
			fmt.Println(res.err)
			continue
		}

		fmt.Printf("found: %s\n", res.url)
		if res.depth > 0 {
			for _, u := range res.urls {
				if !fetched[u] {
					fetching++
					go fetch(u, res.depth-1)
					fetched[u] = true
				}
			}
		}
	}
	close(results)
}

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.

go run main.go

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

found: https://www.santekno.com/tags/encoder
found: https://www.santekno.com/categories/tutorial/page/2/
time 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.

comments powered by Disqus