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) []string
digunakan 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
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.