Programming

Data Harvest: POM.GO.ID

Awalnya sederhana aja, abis baca “Kolom” di detik.com langsung inget, di Indonesia ada BPOM (Badan Pengawas Obat dan Makanan) dan dari websitenya kita bisa tau apakah sebuah produk obat dan makanan benar-benar aman untuk dikonsumsi. Entri pertama dari penelusuran Google dengan keyword “bpom” langsung tepat, dan disana ada sub-result menuju web yang saya mau: Cek Produk BPOM.

Saya langsung cari nama produk berdasarkan kata kunci air kemasan yang ada di meja saya: Pristine. Hasilnya ada 9 data dan semuanya gak ada sangkut pautnya sama air mineral keluaran grup sinarmas. Saya coba ulang dengan nomor registrasinya: MD 265210006138, baru deh nongol datanya. Ternyata “Pristine” itu merk, sedangkan nama produknya sendiri adalah “Air Minum pH Tinggi”.

Abis baca keterangan sekilas, langsung aja reflek: klik-kanan – View Source. Keliatan deh raw html-nya, ternyata halaman pencarian dibuat dengan table standar, data diload oleh script php (tidak pakai ajax) dan paging sederhana. Semua parameter ada di address bar browser, dan diantara parameter yang ada, si programmer nyisipin session php. Mungkin niatnya supaya datanya gak bisa di lihat tanpa melalui browser, tapi downside-nya orang gak bisa ngasih tautan referensi ke entri tertentu. Oh iya, satu lagi, baris data hasil pencarian bisa di klik untuk menampilkan detail entrinya, dan ini diload dengan ajax.

Data detail muncul setelah row di-klik.

Karena liat langsung raw html agak susah, jadi Ctrl+Shift+I untuk ngeluarin DevTools dari browser. Jaman dulu yg begini susah banget, satu2nya tools yang lumayan mumpuni addon dari Firefox yang namanya Firebug, tapi berhubung browser2 modern udah meng-embed fungsi ini jadi Firebug di-mati-in deh.

Inspeksi raw html tabel hasil pencarian

Dari inspeksi elemen, bisa dilihat dalam tiap <tr> produk ada custom attribute bernama urldetil yang digunakan untuk me-load data detail dari produk dengan ajax call.

Request AJAX untuk mengambil data detail.
Response berupa halaman html yang siap ditampilkan.

Nah, sebenernya data detail ini yang harusnya di harvest, dan data dari table hasil search dibutuhkan untuk mendapatkan ID dari itemnya. Jadi ini langkah-langkah untuk harvest datanya:

  • Buka https://cekbpom.pom.go.id dan simpan cookie, ambil session_id php.
  • Request ke url untuk search https://cekbpom.pom.go.id/index.php/home/produk/ {PHP_SESSION_ID}/all/row/{ROW_PER_PAGE}/page/{PAGE_NUMBER}/order/4/DESC/search/{KATEGORI}/{KATA-KUNCI-PENCARIAN}
  • Parse response html-nya, ambil tiap urldetil dan request lagi ke
    https://cekbpom.pom.go.id/index.php/home/detil/{PHP_SESSION_ID}/produk/{URLDETIL}
  • Parse lagi response-nya untuk mendapatkan data detail.

Cara paling gampang dan cepat yang terpikir oleh saya adalah dengan menggunakan PHP. Script yang saya gunakan bukan untuk dibuka oleh browser, tapi script php dipanggil lewat terminal php -f namafile.php [parameter]. Setelah beberapa kali mencoba, saya dapat draft kasarnya, beberapa parameter masih di hardcode dan hasil berupa json disimpan ke dalam satu file (klik READ MORE untuk source-code php-nya).

<?php
/* CEK PARAMETER, JIKA TIDAK ADA: KELUAR DARI PROGRAM */
if(count($argv)<2){
    echo 'Usage: php -f bpom.php [product]';
    echo "\n\n";
    die();
}

/* GLOBAL VARIABEL */
$cookies = Array();
$last = null;
$ckfile = tempnam (".", 'pudding');

/* PARSING HEADER HTML UNTUK AMBIL COOKIE */
function curlResponseHeaderCallback($ch, $headerLine) {
    global $cookies;
    global $last;
    if (preg_match('/^Set-Cookie:\s*([^;]*)/mi', $headerLine, $cookie) == 1)
    {
        $cookies[] = $cookie;
        $last = $cookie;
    }
    return strlen($headerLine);
}

/* AMBIL ISI HTML DARI TAG */
function DOMinnerHTML(DOMNode $element) 
{ 
    $innerHTML = ""; 
    $children  = $element->childNodes;
    foreach ($children as $child) 
    { 
        $innerHTML .= $element->ownerDocument->saveHTML($child);
    }
    return $innerHTML; 
}

/* AMBIL DATA DETIL BPOM */
function get_data($uri, $pro) {
    global $ckfile;
    $ch = curl_init($uri);
    curl_setopt($ch, CURLOPT_COOKIEJAR, $ckfile);
    curl_setopt($ch, CURLOPT_COOKIEFILE, $ckfile);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
    curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
    $isi = curl_exec($ch);

    $hasil = array();
    $attr = array();

    $co_reg = $pro;
    $no_reg = "";
    $na_reg = "";

    $dom = new DOMDocument;
    $dom->loadHTML($isi);
    $sub = true;
    $tds = $dom->getElementsByTagName('td');
    foreach($tds as $td){
        if ($sub) {
            foreach($td->attributes as $a){
                if ($a->nodeName == "class") {
                    if ($a->nodeValue=="subs") {
                        $attr = array(trim($td->nodeValue));
                        $sub = false;
                    }
                }
            }
        } else {
            $x = trim($td->nodeValue);
            $y = trim(DOMinnerHTML($td));
            if ($attr[0]=="Nama Produk") $na_reg = $x;
            if ($attr[0]=="Nomor Registrasi") $no_reg = $x;
            $attr[] = $x;
            if ($x != $y) {
                $t = new DOMDocument;
                $t->loadHTML($y);
                $a = $t->getElementsByTagName('a')->item(0);
                foreach($a->attributes as $r){
                    if ($r->nodeName=="href") $attr[] = trim($r->nodeValue);
                }
            }
            $hasil[] = $attr;
            $sub = true;
        }
    }

    $res = new stdClass;
    $res->kode = $co_reg;
    $res->nomor = $no_reg;
    $res->nama = $na_reg;
    $res->data = $hasil;

    return $res;
}

/* ---------- MAIN PROGRAM ---------- */

// STEP 1 : AMBIL SESSION_ID DARI COOKIE
$ch = curl_init('http://cekbpom.pom.go.id/');
curl_setopt($ch, CURLOPT_HEADERFUNCTION, "curlResponseHeaderCallback");
curl_setopt($ch, CURLOPT_COOKIEJAR, $ckfile);
curl_setopt($ch, CURLOPT_COOKIEFILE, $ckfile);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
$result = curl_exec($ch);

$sesid = (explode("=",$last[1]))[1];
$keyword = $argv[1]; // get parameter 

// STEP 2 : REQUEST KE URL SEARCH
$uri = "http://cekbpom.pom.go.id/index.php/home/produk/{$sesid}/all/row/1000/page/1/order/4/DESC/search/1/".$keyword;
$ch = curl_init($uri);
curl_setopt($ch, CURLOPT_COOKIEJAR, $ckfile);
curl_setopt($ch, CURLOPT_COOKIEFILE, $ckfile);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
$result = curl_exec($ch);

// STEP 3 : PARSE RESPONSE HTML, AMBIL DATA URLDETIL DI TIAP TAG TR
$hasil = array();
$prods = array();

$doc = new DOMDocument;
$doc->loadHTML($result);
$trs = $doc->getElementsByTagName('tr');
foreach($trs as $tr){
    if (!$tr->hasAttributes()) break;
    foreach($tr->attributes as $a){
        if ($a->nodeName == "urldetil") $prods[] = $a->nodeValue;
    }
}

// STEP 4 : ITERATE TIAP DATA DETIL, DOWNLOAD, PARSE DATA DAN SIMPAN DALAM ARRAY
foreach($prods as $prod){
    $pro = substr($prod,1);
    $uri = "https://cekbpom.pom.go.id/index.php/home/detil/{$sesid}/produk".$prod;
    $hasil[] = get_data($uri, $pro);
}

// LAST : SAVE ARRAY OF OBJECT DATA KE FILE
$str_json = str_replace("\\u00a0","",json_encode($hasil, JSON_PRETTY_PRINT));
$str_json = str_replace($sesid,'{SESSION_ID}',$str_json);
file_put_contents($keyword.'.json',$str_json);
echo "That's all folks.\n\n";
?>

Data detil sengaja disimpan dalam array bukan dalam field tertentu, karena dari hasil pengamatan daftar field yang ditampilkan berbeda-beda. Karena masih draft dan program ini hanya untuk POC extract data BPOM, maka data dalam array sudah cukup. Dan ini contoh data hasil pencarian dengan keyword “pristine” ( php -f bpom.php pristine ).

[
    {
        "kode": "286839.01",
        "nomor": "NA18180105247",
        "nama": "Pristine Acne Gel With Anti - Shine",
        "data": [
            [
                "Nomor Registrasi",
                "NA18180105247"
            ],
            [
                "Tanggal Terbit",
                "01-10-2018"
            ],
            [
                "Diterbitkan Oleh",
                "Notifikasi Kosmetika"
            ],
            [
                "",
                "Direktorat Registrasi Obat Tradisional, Suplemen Kesehatan, dan Kosmetik"
            ],
            [
                "Produk",
                "Kosmetika"
            ],
            [
                "Nama Produk",
                "Pristine Acne Gel With Anti - Shine"
            ],
            [
                "Bentuk Sediaan",
                "Gel"
            ],
            [
                "Merk",
                "AMARANTHINE"
            ],
            [
                "Kemasan",
                "Tube - Dus 100 g"
            ],
            [
                "Pendaftar",
                "Immortal Cosmedika Indonesia,PT - Depok",
                "https:\/\/cekbpom.pom.go.id\/index.php\/home\/sarana\/{SESSION_ID}\/id\/001.T59.01"
            ],
            [
                "Diproduksi Oleh",
                "Immortal Cosmedika Indonesia, PT - Depok",
                "https:\/\/cekbpom.pom.go.id\/index.php\/home\/sarana\/{SESSION_ID}\/id\/002.M1579.01"
            ]
        ]
    },
    {
        "kode": "286840.01",
        "nomor": "NA18181206788",
        "nama": "Pristine Clean Refreshing Toner With Anti - Shine",
        "data": [
            [
                "Nomor Registrasi",
                "NA18181206788"
            ],
            [
                "Tanggal Terbit",
                "01-10-2018"
            ],
            [
                "Diterbitkan Oleh",
                "Notifikasi Kosmetika"
            ],
            [
                "",
                "Direktorat Registrasi Obat Tradisional, Suplemen Kesehatan, dan Kosmetik"
            ],
            [
                "Produk",
                "Kosmetika"
            ],
            [
                "Nama Produk",
                "Pristine Clean Refreshing Toner With Anti - Shine"
            ],
            [
                "Bentuk Sediaan",
                "Cair"
            ],
            [
                "Merk",
                "AMARANTHINE"
            ],
            [
                "Kemasan",
                "Botol - Dus 180 ml"
            ],
            [
                "Pendaftar",
                "Immortal Cosmedika Indonesia,PT - Depok",
                "https:\/\/cekbpom.pom.go.id\/index.php\/home\/sarana\/{SESSION_ID}\/id\/001.T59.01"
            ],
            [
                "Diproduksi Oleh",
                "Immortal Cosmedika Indonesia, PT - Depok",
                "https:\/\/cekbpom.pom.go.id\/index.php\/home\/sarana\/{SESSION_ID}\/id\/002.M1579.01"
            ]
        ]
    },
    {
        "kode": "286838.01",
        "nomor": "NA18180105244",
        "nama": "Pristine Sebum Reducer With Anti - Shine",
        "data": [
            [
                "Nomor Registrasi",
                "NA18180105244"
            ],
            [
                "Tanggal Terbit",
                "01-10-2018"
            ],
            [
                "Diterbitkan Oleh",
                "Notifikasi Kosmetika"
            ],
            [
                "",
                "Direktorat Registrasi Obat Tradisional, Suplemen Kesehatan, dan Kosmetik"
            ],
            [
                "Produk",
                "Kosmetika"
            ],
            [
                "Nama Produk",
                "Pristine Sebum Reducer With Anti - Shine"
            ],
            [
                "Bentuk Sediaan",
                "Krim"
            ],
            [
                "Merk",
                "AMARANTHINE"
            ],
            [
                "Kemasan",
                "Botol - Dus 60 ml"
            ],
            [
                "Pendaftar",
                "Immortal Cosmedika Indonesia,PT - Depok",
                "https:\/\/cekbpom.pom.go.id\/index.php\/home\/sarana\/{SESSION_ID}\/id\/001.T59.01"
            ],
            [
                "Diproduksi Oleh",
                "Immortal Cosmedika Indonesia, PT - Depok",
                "https:\/\/cekbpom.pom.go.id\/index.php\/home\/sarana\/{SESSION_ID}\/id\/002.M1579.01"
            ]
        ]
    },
    {
        "kode": "263270.01",
        "nomor": "NA18181202037",
        "nama": "Pristine Clean Milk Cleanser With Anti - Shine",
        "data": [
            [
                "Nomor Registrasi",
                "NA18181202037"
            ],
            [
                "Tanggal Terbit",
                "01-04-2018"
            ],
            [
                "Diterbitkan Oleh",
                "Notifikasi Kosmetika"
            ],
            [
                "",
                "Direktorat Registrasi Obat Tradisional, Suplemen Kesehatan, dan Kosmetik"
            ],
            [
                "Produk",
                "Kosmetika"
            ],
            [
                "Nama Produk",
                "Pristine Clean Milk Cleanser With Anti - Shine"
            ],
            [
                "Bentuk Sediaan",
                "Krim"
            ],
            [
                "Merk",
                "AMARANTHINE"
            ],
            [
                "Kemasan",
                "Botol - Dus 180 ml"
            ],
            [
                "Pendaftar",
                "Immortal Cosmedika Indonesia,PT - Depok",
                "https:\/\/cekbpom.pom.go.id\/index.php\/home\/sarana\/{SESSION_ID}\/id\/001.T59.01"
            ],
            [
                "Diproduksi Oleh",
                "Immortal Cosmedika Indonesia, PT - Depok",
                "https:\/\/cekbpom.pom.go.id\/index.php\/home\/sarana\/{SESSION_ID}\/id\/002.M1579.01"
            ]
        ]
    },
    {
        "kode": "161794.01",
        "nomor": "NA18170100338",
        "nama": "Pristine Exquisite Anti-Acne Cons",
        "data": [
            [
                "Nomor Registrasi",
                "NA18170100338"
            ],
            [
                "Tanggal Terbit",
                "06-02-2017"
            ],
            [
                "Diterbitkan Oleh",
                "Notifikasi Kosmetika"
            ],
            [
                "",
                "Direktorat Registrasi Obat Tradisional, Suplemen Kesehatan, dan Kosmetik"
            ],
            [
                "Produk",
                "Kosmetika"
            ],
            [
                "Nama Produk",
                "Pristine Exquisite Anti-Acne Cons"
            ],
            [
                "Bentuk Sediaan",
                "Cair"
            ],
            [
                "Merk",
                "AMARANTHINE"
            ],
            [
                "Kemasan",
                "Botol Pump - Dus 15 ml"
            ],
            [
                "Pendaftar",
                "Pesona Amaranthine Cosmetiques, PT - Depok",
                "https:\/\/cekbpom.pom.go.id\/index.php\/home\/sarana\/{SESSION_ID}\/id\/001.T1436.01"
            ],
            [
                "Diproduksi Oleh",
                "Immortal Cosmedika Indonesia, PT - Depok",
                "https:\/\/cekbpom.pom.go.id\/index.php\/home\/sarana\/{SESSION_ID}\/id\/002.M1579.01"
            ]
        ]
    },
    {
        "kode": "189234.01",
        "nomor": "NA18170100123",
        "nama": "Pristine T-zone Sebum Treatment Cream",
        "data": [
            [
                "Nomor Registrasi",
                "NA18170100123"
            ],
            [
                "Tanggal Terbit",
                "16-01-2017"
            ],
            [
                "Diterbitkan Oleh",
                "Notifikasi Kosmetika"
            ],
            [
                "",
                "Direktorat Registrasi Obat Tradisional, Suplemen Kesehatan, dan Kosmetik"
            ],
            [
                "Produk",
                "Kosmetika"
            ],
            [
                "Nama Produk",
                "Pristine T-zone Sebum Treatment Cream"
            ],
            [
                "Bentuk Sediaan",
                "Krim"
            ],
            [
                "Merk",
                "AMARANTHINE"
            ],
            [
                "Kemasan",
                "Pot - Dus 30 g"
            ],
            [
                "Pendaftar",
                "Pesona Amaranthine Cosmetiques, PT - Depok",
                "https:\/\/cekbpom.pom.go.id\/index.php\/home\/sarana\/{SESSION_ID}\/id\/001.T1436.01"
            ],
            [
                "Diproduksi Oleh",
                "Immortal Cosmedika Indonesia, PT - Depok",
                "https:\/\/cekbpom.pom.go.id\/index.php\/home\/sarana\/{SESSION_ID}\/id\/002.M1579.01"
            ]
        ]
    },
    {
        "kode": "133539.01",
        "nomor": "NA18160100196",
        "nama": "Pristine Sebum Reducer With Anti - shine",
        "data": [
            [
                "Nomor Registrasi",
                "NA18160100196"
            ],
            [
                "Tanggal Terbit",
                "20-01-2016"
            ],
            [
                "Diterbitkan Oleh",
                "Notifikasi Kosmetika"
            ],
            [
                "",
                "Direktorat Registrasi Obat Tradisional, Suplemen Kesehatan, dan Kosmetik"
            ],
            [
                "Produk",
                "Kosmetika"
            ],
            [
                "Nama Produk",
                "Pristine Sebum Reducer With Anti - shine"
            ],
            [
                "Bentuk Sediaan",
                "Krim"
            ],
            [
                "Merk",
                "AMARANTHINE"
            ],
            [
                "Kemasan",
                "Botol - Dus 60 ml"
            ],
            [
                "Pendaftar",
                "Pesona Amaranthine Cosmetiques, PT - Depok",
                "https:\/\/cekbpom.pom.go.id\/index.php\/home\/sarana\/{SESSION_ID}\/id\/001.T1436.01"
            ],
            [
                "Diproduksi Oleh",
                "Immortal Cosmedika Indonesia, PT - Depok",
                "https:\/\/cekbpom.pom.go.id\/index.php\/home\/sarana\/{SESSION_ID}\/id\/002.M1579.01"
            ]
        ]
    },
    {
        "kode": "160104.01",
        "nomor": "NA18151205656",
        "nama": "Pristine Clean Facial Wash",
        "data": [
            [
                "Nomor Registrasi",
                "NA18151205656"
            ],
            [
                "Tanggal Terbit",
                "16-12-2015"
            ],
            [
                "Diterbitkan Oleh",
                "Notifikasi Kosmetika"
            ],
            [
                "",
                "Direktorat Registrasi Obat Tradisional, Suplemen Kesehatan, dan Kosmetik"
            ],
            [
                "Produk",
                "Kosmetika"
            ],
            [
                "Nama Produk",
                "Pristine Clean Facial Wash"
            ],
            [
                "Bentuk Sediaan",
                "Cairan Kental"
            ],
            [
                "Merk",
                "AMARANTHINE"
            ],
            [
                "Kemasan",
                "Botol - Dus 180 ml"
            ],
            [
                "Pendaftar",
                "Pesona Amaranthine Cosmetiques, PT - Depok",
                "https:\/\/cekbpom.pom.go.id\/index.php\/home\/sarana\/{SESSION_ID}\/id\/001.T1436.01"
            ],
            [
                "Diproduksi Oleh",
                "Immortal Cosmedika Indonesia, PT - Depok",
                "https:\/\/cekbpom.pom.go.id\/index.php\/home\/sarana\/{SESSION_ID}\/id\/002.M1579.01"
            ]
        ]
    },
    {
        "kode": "134292.01",
        "nomor": "NA18150103378",
        "nama": "Pristine Acne Gel With Anti - shine",
        "data": [
            [
                "Nomor Registrasi",
                "NA18150103378"
            ],
            [
                "Tanggal Terbit",
                "03-11-2015"
            ],
            [
                "Diterbitkan Oleh",
                "Notifikasi Kosmetika"
            ],
            [
                "",
                "Direktorat Registrasi Obat Tradisional, Suplemen Kesehatan, dan Kosmetik"
            ],
            [
                "Produk",
                "Kosmetika"
            ],
            [
                "Nama Produk",
                "Pristine Acne Gel With Anti - shine"
            ],
            [
                "Bentuk Sediaan",
                "Gel"
            ],
            [
                "Merk",
                "AMARANTHINE"
            ],
            [
                "Kemasan",
                "Tube - Dus 100 g"
            ],
            [
                "Pendaftar",
                "Pesona Amaranthine Cosmetiques, PT - Depok",
                "https:\/\/cekbpom.pom.go.id\/index.php\/home\/sarana\/{SESSION_ID}\/id\/001.T1436.01"
            ],
            [
                "Diproduksi Oleh",
                "Immortal Cosmedika Indonesia, PT - Depok",
                "https:\/\/cekbpom.pom.go.id\/index.php\/home\/sarana\/{SESSION_ID}\/id\/002.M1579.01"
            ]
        ]
    }
]

That’s all folks.