Monday, June 8, 2020

Extracting information from a dynamic webpage with RSelenium - I


The COVID-19 Contact Tracing and Reporting webpage of the Ministry of Health and Sports (MOHS). Myanmar is available here.
knitr::include_graphics("caseSelection.png")
Here, information on the last reported case-151 is displayed by default, and case-150 is displayed by clicking the tab in the middle. To get other cases you can open the dropdown list of case numbers at “View More” and make a selection. The dropdown list is quite long and you can see that it would be inconvenient to scroll down the list and click “Case-1” at the very end. But a blinking cursor invites you to enter case number, for example, “120” and you get case highlighted in black. Now you click on it and information for case-120 is displayed. But entering “1” and it highlights “Case 151” for you. If you try " 1" or “001” you get the message “No results found”.
In the context of learning to do NLP on Myanmar language, my last three posts reported on the results of exploring the COVID-19 surveillance reports of MOHS. In this post, I’ll try to scrape the contact tracing information on the confirmed Covid-19 infected cases.

Requirements for scraping a dynamic webpage

Web scraping/web mining/web harvesting means extracting data from websites. “… websites contain wealth of useful data but designed for human consumption and not data analysis. The goal of web scraping is to take advantage of the pattern or structure of web pages to extract and store data in a format suitable for data analysis.”
The webpages like the MOHS COVID-19 Contact Tracing and Reporting webpage are dynamically generated webpages. They require user interaction to display results. I learned that in such contexts web scraping couldn’t be handled by a tool like rvest. The reason is that such webpages typically use Java scripts to produce dynamic content and rvest is not designed to handle them. Looking for a solution, I found that I could use RSelenium package, among others.
The RSelenium package “… can be used to automate those interactions and extract page contents. It provides a set of bindings for the Selenium 2.0 webdriver using the JsonWireProtocol. It can also aid in automated application testing, load testing, and web scraping.”

Using RSelenium

I’ve installed the RSelenium ver. 1.7.7 package which also took care of installing the required Selenium Server software. The recommended practice is to run RSelenium in a Docker container and I’ve also installed the docker desktop ver. 2.3.0.3(45519). I am using a laptop with Intel i7 processor, 16GB RAM and running Windows 10. This post has been created with R Notebook in RStudio ver. 1.2.5042.

Start the Selenium server and chrome browser

With windows startup, the Doker Desktop is also started. In the RStudio Terminal you would start Selenium server in docker container with: “docker run -d -p 4445:4444 selenium/standalone-chrome”. Then you start the server and browser and navigate to the MOHS webpage:
# Load the Library
library(RSelenium)
# start the server and browser(you can use other browsers here)
rD <- remoteDriver(remoteServerAddr = "localhost",
                      port = 4445L,
                      browserName = "chrome")
rD$open()
[1] "Connecting to remote server"
$acceptInsecureCerts
[1] FALSE

$browserName
[1] "chrome"

$browserVersion
[1] "81.0.4044.138"

$chrome
$chrome$chromedriverVersion
[1] "81.0.4044.138 (8c6c7ba89cc9453625af54f11fd83179e23450fa-refs/branch-heads/4044@{#999})"

$chrome$userDataDir
[1] "/tmp/.com.google.Chrome.nFtAQN"


$`goog:chromeOptions`
$`goog:chromeOptions`$debuggerAddress
[1] "localhost:46211"


$networkConnectionEnabled
[1] FALSE

$pageLoadStrategy
[1] "normal"

$platformName
[1] "linux"

$proxy
named list()

$setWindowRect
[1] TRUE

$strictFileInteractability
[1] FALSE

$timeouts
$timeouts$implicit
[1] 0

$timeouts$pageLoad
[1] 300000

$timeouts$script
[1] 30000


$unhandledPromptBehavior
[1] "dismiss and notify"

$`webauthn:virtualAuthenticators`
[1] TRUE

$webdriver.remote.sessionid
[1] "0782f983bf5db88192ae2e4fb29cc3cf"

$id
[1] "0782f983bf5db88192ae2e4fb29cc3cf"
# navigate to URL for MOHS contact tracing and reporting
rD$navigate("https://mohs.gov.mm/page/8509")
You can view the screen shot of the web page. It is shown in the RStudio viewer:
rD$screenshot(display = TRUE)

Retrieving information on case-151 and 150

On the webpage, Case-151, case-150, and all other cases are represented dynamically by three separate elements. I learnt later that they are displayed via three aria-controls. You could retrieve Case-151 and case-150 like this:
caseTexts <- list()
case151 <- rD$findElement(using="css selector", ".nav-tabs > li:nth-child(1) > a:nth-child(1)")
case151$clickElement()
ct151 <- rD$findElement(using="css selector", "#Post_10137")
caseTexts <- append(caseTexts,ct151$getElementText())
cat(caseTexts[[1]])
C - 151 ဓာတ်ခွဲအတည်ပြုလူနာ
အသက် - (၂၅) နှစ်၊ မ
နေရပ်လိပ်စာ - အင်းစိန်မြို့နယ်၊ ရန်ကုန်တိုင်းဒေသကြီး
ရောဂါလက္ခဏာဖြစ်ပွားခြင်း - နှာစေးခြင်း၊ အနံ့ပျောက်ခြင်း
ရောဂါလက္ခဏာစတင်ဖြစ်ပွားသည့်ရက် - ၂၄-၄-၂၀၂၀
ပြသခဲ့သည့်ဆေးရုံ - Fever clinic မှတဆင့် အင်းစိန်အထွေထွေရောဂါကုဆေးရုံကြီး
ဓာတ်ခွဲအတည်ပြုလူနာနှင့်ထိတွေ့ခြင်း - မရှိ
ဓာတ်ခွဲအတည်ပြုသည့်ရက်စွဲ - ၁-၅-၂၀၂၀
ပြည်ပခရီးသွားရာဇဝင် - မရှိ
အတူနေမိသားစုဝင် - (၆) ဦး
ဓာတ်ခွဲအတည်ပြုစစ်ဆေးတွေ့ရှိပြီးချိန်မှစ၍ အဆိုပါလူနာအား တောင်ဥက္ကလာပအထူးကုဆေးရုံကြီးတွင် သီးခြားထားရှိကုသမှုပေးလျက်ရှိပါသည်။
လူနာနှင့်အနီးကပ်ထိတွေ့သူများအား အသွားအလာတားမြစ်ပိတ်ပင်၍ ဓာတ်ခွဲနမူနာ ရယူစစ်ဆေးပြီးဖြစ်ပါသည်။
ခရီးသွားရာဇဝင်
ရောဂါလက္ခဏာာမပေါ်မီ (၂) ရက်နှင့် ရောဂါလက္ခဏာပေါ်ပြီးနောက် Fever clinic (အင်းစိန်) သို့သွားခြင်းမှအပ ပြင်ပသို့ သွားရောက်ခဲ့ခြင်းမရှိကြောင်းသိရှိရပါသည်။
 
case150 <- rD$findElement(using="css selector", ".nav-tabs > li:nth-child(2) > a:nth-child(1)")
case150$clickElement()
ct150 <- rD$findElement(using="css selector", "#Post_10135")
caseTexts <- append(caseTexts,ct150$getElementText())
cat(caseTexts[[2]])
C - 150 ဓာတ်ခွဲအတည်ပြုလူနာ
အသက် - (၁၈) နှစ်၊ မ
နေရပ်လိပ်စာ - ဟားခါးမြို့နယ်၊ ချင်းပြည်နယ်  
ရောဂါလက္ခဏာဖြစ်ပွားခြင်း - မရှိ
ပြသခဲ့သည့်ဆေးရုံ - မရှိ
ဓာတ်ခွဲအတည်ပြုလူနာနှင့်ထိတွေ့ခြင်း - မရှိ
အသွားအလာကန့်သတ်သည့်ရက်စွဲ - ၁၆-၄-၂၀၂၀ (ဟားခါးမြို့)
ဓာတ်ခွဲအတည်ပြုသည့်ရက်စွဲ - ၂၉-၄-၂၀၂၀
ပြည်ပခရီးသွားရာဇဝင် - ရှိ (တရုတ်နိုင်ငံ)
ဓာတ်ခွဲအတည်ပြုစစ်ဆေးတွေ့ရှိပြီးချိန်မှစ၍ အဆိုပါလူနာအား ဟားခါးပြည်သူ့ဆေးရုံကြီးတွင် သီးခြားထားရှိကုသမှုပေးလျက်ရှိပါသည်။
အနီးကပ်ထိတွေ့ခဲ့သူများအား အသွားအလာတားမြစ် ပိတ်ပင်၍ ဓာတ်ခွဲနမူနာ ရယူစစ်ဆေးပြီးဖြစ်ပါသည်။
ခရီးသွားရာဇဝင်
ရက်စွဲ သွားရောက်ခဲ့သည့်နေရာ အသုံးပြုသည့်ယာဉ် မှတ်ချက်
၂၀-၃-၂၀၂၀ တရုတ်နိုင်ငံမှ ရှမ်းပြည်နယ်၊ မူဆယ်မြို့ မူဆယ်မြို့ရှိတည်းခိုးခန်း တစ်ခုတွင် တည်းခိုခဲ့ပါသည်။
၂၁-၃-၂၀၂၀ မနက် (၈:၀၀) နာရီ မန္တလေး ရွှေမန္တလာ အဝေးပြေးယာဉ် မန္တလေးမြို့သို့ (၂၃-၃-၂၀၂၀) ရက်နေ့ မနက် (၁:၀၀) နာရီတွင် ရောက်ရှိခဲ့ပါသည်။
၂၄-၃-၂၀၂၀ မနက် (၈:၀၀) နာရီ ကလေးမြို့ ရွှေမန္တလာ အဝေးပြေးယာဉ် (၂၅-၃-၂၀၂၀) ရက်နေ့ မနက် (၁:၀၀) နာရီတွင် ကလေးမြို့ သို့ရောက်ရှိခဲ့ပါသည်။
၂၄-၃-၂၀၂၀ မှ ၅-၄-၂၀၂၀ ထိ ကလေးမြို့ သူငယ်ချင်းဖြစ်သူ၏နေအိမ်တွင် တည်းခိုနေထိုင်ခဲ့ပါသည်။
၅-၄-၂၀၂၀ ညနေ (၄:၀၀) နာရီ ဟားခါးမြို့ တောင်ဇလပ်ယာဉ်(အသေး) ဟားခါးမြို့ကားဂိတ်သို့ (၆-၄-၂၀၂၀) ရက်နေ့ နေ့လည် (၁:၃၀) နာရီတွင်ရောက်ရှိခဲ့ပါသည်။
(၆-၄-၂၀၂၀) ရက်နေ့ နေ့လည်(၁:၄၅) နာရီ သီဖူးလ်ကျေးရွာ၊ ဟာခါးမြို့နယ် တောင်ဇလပ်ယာဉ်(အသေး) (၆-၄-၂၀၂၀) ရက်နေ့ နေ့လည် (၃:၃၀) နာရီတွင် ၎င်း၏ဖခင် အိမ်သို့ ရောက်ရှိခဲ့ပါသည်။ (၁၃-၄-၂၀၂၀) ရက်နေ့ထိ နေထိုင်ခဲ့ပါသည်။
၁၃-၄-၂၀၂၀ ဟားခါးမြို့ သီဖူးလ်ကျေးရွာမှ ဟာခါးမြို့သို့ ပြန်လည်ထွက်ခွာခဲ့ပါသည်။
                   

Retrieving information on case-1

To try retrieving information for other cases, I have to locate the “View More” tab, and click on it to open the select case dropdown list.
# find element view more
casey <- rD$findElement(using="css selector", "#recentcase li:nth-child(3) a")
rD$screenshot(display = TRUE)
# click view more and "select the case box" is opened
casey$clickElement()
rD$screenshot(display = TRUE)
# after clicking  view more  find "#select2-slcase-container"
caseM <- rD$findElement(using="css selector","#select2-slcase-container") 
caseM$clickElement()      # need to click caseM to open
rD$screenshot(display = TRUE)
Up to the previous step it was fine. Then I was stuck with entering the case number at the cursor and then to find the highlighted case number, which when clicked would retrieve the information for the case in question. The problem was with locating the highlighted text of selected case-number. I tried using the Selector Gadget as well as the “Inspect” option on the page source to find the right locator for that purpose.
I tried with Chrome as well as Firefox browser and also looked and tried out solutions from Selenium tutorials and the StackOverflow Q/A site. I struggled with that for more than a week. Finally, I got the clue that I have to look for some solution to find the web element associated with some dropdown list. Also, from the inspection of the page-source of the MOHS webpage I found how a given case-number is associated with an appropriate case-code:
knitr::include_graphics("caseOption.png")
Then I was lucky to hit upon the right xpath specification, for example, to find the web element for case-1, whose code is “528”. When this element is located and clicked, information on case-1 should be displayed on the webpage at the web element with id = “OtherCase_Info”.
case_1 <- rD$findElement(using = 'xpath', "//*[@value = '528']")
# Sys.sleep(2)
case_1$clickElement()
To able to see that information we may have to scroll down the page once and view the screen shot:
# find the webpage body
bodelement <- rD$findElement("css", "body")
#scroll down once
bodelement$sendKeysToElement(list(key = "page_down"))
rD$screenshot(display = TRUE)
You can locate the element on the webpage where this information is displayed and view the information with cat() function:
case1_text <- rD$findElement(using = 'css selector', "#OtherCase_Info")
cat(paste0(case1_text$getElementText()))
C-1 ဓာတ်ခွဲအတည်ပြုလူနာ
  အဆိုပါလူနာသည် ၎င်း၏မိခင်နှင့်အတူ အမေရိကန်နိုင်ငံ၊ ဝါရှင်တန်မြို့မှ (၁၃-၃-၂၀၂၀) ရက်နေ့မှ American Airways (AA 3269) ဖြင့် Chicago သို့လည်းကောင်း၊ Chicago မှ Doha သို့ Qatar Airways (Qatar 726) ဖြင့်လည်းကောင်းထွက်ခွာခဲ့ပါသည်။
(၁၄-၃-၂၀၂၀) ရက်နေ့တွင် ဒိုဟာမြို့မှ Qatar Airways (QR-916) ဖြင့် ထွက်ခွာလာခဲ့ရာ ရန်ကုန်အပြည်ပြည်ဆိုင်ရာလေဆိပ်သို့ (၁၅-၃-၂၀၂၀) ရက်နေ့ နံနက် (၅:၃၅) တွင် ရောက်ရှိခဲ့ပါသည်။
ရန်ကုန်အပြည်ပြည်ဆိုင်ရာလေဆိပ်မှ စစ်ကိုင်းတိုင်းဒေသကြီး၊ ကလေးမြို့လေဆိပ်သို့ မြန်မာအမျိုးသားလေကြောင်းလိုင်း (UB – 503) ဖြင့် ထွက်ခွာလာခဲ့ရာ (၁၅-၃-၂၀၂၀) ရက်နေ့ နေ့လည် (၂:၃၀) နာရီအချိန်တွင် ရောက်ရှိခဲ့ပါသည်။
ကလေးမြို့တွင် (၁၅-၃-၂၀၂၀) ရက်နေ့မှ (၁၇-၃-၂၀၂၀) ရက်နေ့ထိ တပ်ဦးသီတာ၊ ရတနာပုံလမ်း၌ တည်းခိုနေထိုင်ခဲ့ပြီး (၁၅-၃-၂၀၂၀) ရက်နေ့ ညနေပိုင်းတွင် လမ်းဆုံရှိ ရွှေဆိုင်သို့ သွားရောက်ခဲ့ကြောင်းနှင့် (၁၆-၃-၂၀၂၀) ရက်နေ့တွင် Cherry Photo Studio တွင် Pre-wedding photo ရိုက်ကူးခဲ့ကြောင်း သိရှိရပါသည်။
(၁၇-၃-၂၀၂၀) ရက်နေ့တွင် ကလေးမြို့မှ တီးတိန်မြို့သို့ ပြည်ချစ်သားလိုင်းကားဖြင့် ထွက်ခွာ လာခဲ့ကြောင်းနှင့် ကားပေါ်တွင် လူ (၁၂) ဦးပါရှိကြောင်းသိရှိရပါသည်။
တီးတိန်မြို့တွင် လွယ်ဘွားရပ်ကွက်တွင် တစ်ညအိပ် တည်းခိုနေထိုင်ခဲ့ပြီး (၁၈-၃-၂၀၂၀) ရက်နေ့တွင် ကပ်တဲလ်ကျေးရွာသို့ AG အသင်းတော်ကားဖြင့် ထွက်ခွာခဲ့ကြောင်းသိရှိရပါ သည်။
(၁၉-၃-၂၀၂၀) ရက်နေ့တွင် နံနက်ပိုင်းတွင် ဖျားခြင်းစတင်ခံစားခဲ့ရသဖြင့် ကပ်တဲလ် ကျေးရွာရှိ PSI ဆေးခန်းသို့ နံနက် (၈:၀၀) နာရီတွင် သွားရောက်ပြသခဲ့ကြောင်း သိရှိရပါသည်။
PSI ဆေးခန်းမှ ဆရာဝန်သည် တီးတိန်မြို့နယ်၊ ပြည်သူ့ဆေးရုံမှ တာဝန်ရှိသူများနှင့် ဆက်သွယ်တိုင်ပင်၍ ပြည်သူဆေးရုံဝင်းအတွင်းရှိ (၈) ခန်းတွဲ၊ (၂) ထပ် ဆောင်သစ်၊ အပေါ် ထပ်တွင် စနစ်တကျ အသွားအလာ ကန့်သတ်ခြင်း (Quarantine) ပြုလုပ်ခဲ့ပါသည်။

Getting to extract information for all the remaining cases

You’ll have to find way to automate the task of extracting those cases like I’ve done for case-1. Vectorized calculation is the strength of R. But RSelenium doesn’t support it. So you need to use for loops or some other method to get it done. I’ll try to do that with my next post.

No comments:

Post a Comment