Assalamualaikum Sahabat Whitecyber semua …
Introduction to Web Scraping
Web scraping adalah suatu teknik yang bertujuan untuk mengotomasi ekstraksi data dari berbagai sumber daring, mulai dari wikipedia, artikel, website, e-commerce, dan lainnya. Lalu, kenapa teknik ini penting untuk kita pelajari? Apa manfaat yang bisa diambil dengan web scraping di dunia kerja nanti?
Pemahaman Bisnis
Dari segi bisnis, suatu perusahaan mungkin membutuhkan sebuah scraper untuk:
- Mengumpulkan data dari kompetitor, seperti harga untuk suatu produk yang mirip, untuk menyesuaikan harga yang masih bersaing
- Mengumpulkan ulasan, komentar, dari pengguna melalui media sosial untuk mengetahui opini terhadap perusahaan atau produk secara umum
- Dan lainnya
Personal Project
Jika kamu masih belum tergugah, coba bayangkan ini sebagai personal project yang bisa kamu pakai pamerkan. Selain itu, kamu bisa membuat scraper sebebas mungkin, misalkan:
- Mencari meme favorit kamu
- Mencari topik-topik berita yang sedang populer sekarang
- Mengumpulkan resep-resep masakan favorit kamu, sehingga saat memasak tidak perlu repot browsing
- Dan lainnya
Warning
Beberapa hal yang perlu diperhatikan dalam mengumpulkan data menggunakan scraper adalah apakah situs yang di-scrape mengizinkan scraper untuk mengakses secara bebas, beberapa halaman tertentu, atau bahkan tidak diizinkan sama sekali. Informasi ini bisa kita lihat dalam robots.txt
dalam setiap situs.
Untuk melakukan web scraping, kita gunakan library Scrapy. Scrapy adalah sebuah framework yang digunakan untuk crawl sebuah web dan mengekstrak data dari halaman web tersebut.
Perbedaan framework dan library terletak pada kontrol penggunaannya dalam sebuah program [Woz19].
- Framework meminta kita untuk menyediakan apa saja yang diperlukan untuk menggunakannya.
- Library mengizinkan kita untuk menggunakannya di manapun dan kapanpun.
Creating A Spider
Scrapy menggunakan istilah “spider” yang merupakan crawler utama untuk “merayapi” halaman sebuah web. Untuk itu, kita perlu membuat spider yang didefinisikan dalam sebuah class. Secara umum, struktur kode yang akan kita buat adalah seperti berikut.
Basic Code
import scrapy from scrapy.crawler import CrawlerProcess class DSJobCrawler(scrapy.Spider): name = "ds_job_crawler" # instructions for spiders to follow ... # initiate CrawlerProcess crawler = CrawlerProcess() # assign which spider to use crawler.crawl(DSJobCrawler) # start crawling crawler.start()
.
Yuk kita jelasin satu persatu BOT ini …
Imports
Pertama, kita lakukan impor framework scrapy
dan class CrawlerProcess
yang akan kita gunakan
import csv import scrapy from scrapy.crawler import CrawlerProcess
.
Kita hanya memerlukan 2 library saja, yaitu scrapy dan csv. Sebelum kita impor, kita harus pastikan bahwa scrapy
sudah terpasang atau belum. Jika belum, kita bisa install terlebih dahulu.
Supaya lebih sederhana, kita hanya akan mengumpulkan data lowongan pekerjaan data scientist dari Indeed. Data lowongan itu akan kita simpan dalam sebuah file berformat CSV, data_scientist_job.csv
. Kita akan coba mengekstrak beberapa informasi berikut:
job_title
→ Judul pekerjaanjob_link
→ Tautan lowongan pekerjaan di Indeedcompany
→ Nama perusahaancompany_rating
→ Penilaian perusahaan berdasarkan pengguna Indeedcompany_reviews
→ Jumlah ulasan perusahaan oleh pengguna Indeedapply_link
→ Tautan untuk melamar pekerjaan
Oleh karena itu, terlebih dahulu kita buat file tersebut, data_roles_job.csv
, yang hanya berisi informasi-informasi di atas menggunakan kode di bawah ini.
job_storage_file = "data_roles_job.csv" with open(job_storage_file, "w") as out_file: writer = csv.DictWriter( out_file, fieldnames=[ "job_title", "job_link", "company", "company_rating", "company_reviews", "job_type", "job_location", "salary", "job_desc", "apply_link" ] ) writer.writeheader()
.
Spider Class
Selanjutnya, kita buat class DSJobCrawler
yang akan mengekstrak informasi yang disebutkan sebelumnya. Dalam mendefinisikan class tersebut, kita akan bagi ke dalam beberapa komponen:
- Konfigurasi awal spider
- Instruksi gateaway
- Instruksi ekstraksi informasi
1. Konfigurasi Awal Spider
Pertama, kita gunakan kode di bawah ini.
class DSJobCrawler(scrapy.Spider): name = "ds_job_crawler" start_urls = [ "https://id.indeed.com/lowongan-kerja-data-scientist-di-Indonesia", "https://id.indeed.com/jobs?q=Data+Science&l=Indonesia" ] custom_settings = {"DOWNLOAD_DELAY": .25}
.
class DSJobCrawler(scrapy.Spider)
→ class yang digunakan kita namai denganDSJobCrawler
yang mewarisi classscrapy.Spider
. Pewarisan (inheritance) ini wajib hukumnya agar scrapy bisa berjalan.
name
→ nama spiderstart_urls
→ URL yang akan kita ekstrak informasinyaURL yang akan kita ekstrak informasinyacustom_settings
→ konfigurasi pada spider yang nilainya kita tentukan sendirikonfigurasi pada spider yang nilainya kita tentukan sendiri
2. Instruksi Gateaway
Selanjutnya, kita akan mendefinisikan metode parse
pada class DSJobCrawler
yang pada kasus ini berfungsi sebagai sebuah gateaway. Maksud dari gateaway di sini adalah parse
akan berfungsi sebagai gerbang masuk utama yang membantu mengarahkan tautan halaman yang akan diproses. Ini biasanya digunakan ketika kita tertarik untuk mengekstrak informasi dari beberapa halaman.
Sebagai contoh, jika kita lihat contoh tampilan halaman pada Indeed berikut, setiap halaman mempunyai daftar lowongan pekerjaan. Kita tertarik untuk mengekstrak informasi dari setiap lowongan yang ada di halaman 1, 2, dan seterusnya.
.
Kita akan menggunakan potongan kode berikut.
... def parse(self, response): current_page = response.css("ul.pagination-list").xpath( ".//*[contains(@aria-current, 'true')]" ).attrib.get("aria-label") self.logger.debug("current page: %s", current_page) try: next_page = response.css("ul.pagination-list").xpath( ".//*[contains(@aria-label, '{}')]".format(int(current_page) + 1) ).attrib["href"] except: self.logger.info("Couldn't find next page. Done extracting") else: if current_page: current_page += 1 yield response.follow(next_page, callback=self.parse, meta={"dont_redirect": True}) for job_link in response.css("a.tapItem::attr(href)").getall(): yield response.follow(job_link, callback=self.parse_detail) ...
.
3. Instruksi Ekstraksi Informasi
Jika kita perhatikan pada metode parse
di atas, ada bagian perulangan for job_link in response...
yang menggunakan response.follow
untuk meneruskan setiap tautan detail lowongan ke fungsi callback parse_detail
. Metode inilah yang akan digunakan untuk mengekstrak semua informasi yang ada pada halaman detail lowongan pekerjaan.
Perlu diperhatikan juga bahwa setiap detail informasi pada lowongan pekerjaan pasti berbeda satu dengan yang lainnya, baik secara format penulisan dan kelengkapan informasi. Oleh karena itu, sangat wajar ketika informasi yang kita ekstrak tidak sempurna.
.
Untuk itu, kita gunakan kode di bawah ini.
.
def parse_detail(self, response): company_info = response.xpath("//div[contains(@class, 'InlineCompanyRating')]") list_job_desc = [ text.strip() for text in response.xpath("//div[contains(@class, 'jobDescriptionText')]").css("::text").getall() ] job_subtitle = [subtitle.get() for subtitle in response.xpath("//div[contains(@class, 'JobInfoHeader-subtitle')]/*/text()")] job_detail = { "job_title": response.css("h1::text").get(), "job_link": response.url, "company": company_info.xpath(".//a/text()").get() or company_info.xpath(".//div/text()").get(), "company_rating": company_info.xpath(".//meta[contains(@itemprop, 'ratingValue')]").attrib.get("content"), "company_reviews": company_info.xpath(".//meta[contains(@itemprop, 'ratingCount')]").attrib.get("content"), "job_location": job_subtitle[0], "job_type": job_subtitle[1] if len(job_subtitle) > 1 else "", "salary": response.xpath("//div[contains(@class, 'JobMetadataHeader')]/span/text()").get(), "job_desc": " ".join(list_job_desc), "apply_link": response.xpath("//div[contains(@id, 'applyButtonLinkContainer')]").css("a::attr(href)").get() } if not job_detail.get("company"): self.logger.debug("Got no company info: %s", response.url) with open(job_storage_file, "a") as out_file: writer = csv.DictWriter(out_file, fieldnames=job_detail.keys()) writer.writerow(job_detail)
.
.
Run The Crawler
Setelah semua komponen sudah siap dan dijadikan dalam satu class. Kita bisa langsung mengekstrak semua lowongan pekerjaan di Indeed menggunakan kode berikut ini.
.
crawler = CrawlerProcess() crawler.crawl(DSJobCrawler) crawler.start()
.
Contoh Hasil
Jika proses ekstraksi kita berhasil, kita akan mendapatkan data lowongan pekerjaan beserta informasi yang kita ingingkan seperti ilustrasi di bawah ini.