Lab 5: Reddit Data and Web Scraping

Introduction

Today we are going to shift gears slightly to consider alternative sources of textual data of political relevance. We will focus on two alternative sources of data – Reddit and Google News.

What is Reddit?

Reddit is a social news aggregation and discussion website jounded in 2005.

Today, the platform boasts the following statistics

  • 7th most visited site in the US, 29th most visited in the world
  • 8th most used social media platform in the US
  • Eleven million posts are submitted to Reddit each month, over 2 billion comments per year
  • Reddit is primarily young, 18-29 yo make up 64% of the user base and 30-49 make up another 29%
  • Something like 1 in 4 people between the age of 25 and 29 use Reddit actively
  • 222 million users live within the US; roughly half of the Reddit community.
  • 430 million monthly active users, 52 million daily active users
  • 42% of users get news from Reddit

Grabbing information from a subReddit

One of the nice things about Reddit is that they allow for non-authenticated calls to their API by simply adding “.json” to the appropriate part of a given URL. To get started let’s look at the main politics subreddit and then compare to the same URL but with “.json” appended. If we want to read this information into R we can do the following:

library(jsonlite)
library(tidyverse)
library(plyr)
library(dplyr)
library(httr)
library(xml2)
library(rvest)
library(RSelenium)
library(knitr)
url <- "https://www.reddit.com/r/politics/"
pol <- fromJSON(paste0(url,".json"))

Easy peasy. To access information which is relevant to our purposes, we need to parse through this list a little bit as follows, the relevant fields being defined here.

out <- pol$data$children$data
as_tibble(out)
## # A tibble: 27 x 109
##    approved_at_utc subreddit selftext     author_fullname saved mod_reason_title
##    <lgl>           <chr>     <chr>        <chr>           <lgl> <lgl>           
##  1 NA              politics  "Welcome to~ t2_d5h4t        FALSE NA              
##  2 NA              politics  "**Reportin~ t2_onl9u        FALSE NA              
##  3 NA              politics  ""           t2_4ot9563      FALSE NA              
##  4 NA              politics  ""           t2_deaxrt       FALSE NA              
##  5 NA              politics  ""           t2_gdfin        FALSE NA              
##  6 NA              politics  ""           t2_d1sxkm8i     FALSE NA              
##  7 NA              politics  ""           t2_tyyiguo      FALSE NA              
##  8 NA              politics  ""           t2_ba0snhoh     FALSE NA              
##  9 NA              politics  ""           t2_51aar45o     FALSE NA              
## 10 NA              politics  ""           t2_2eef87e      FALSE NA              
## # i 17 more rows
## # i 103 more variables: gilded <int>, clicked <lgl>, title <chr>,
## #   link_flair_richtext <list>, subreddit_name_prefixed <chr>, hidden <lgl>,
## #   pwls <int>, link_flair_css_class <lgl>, downs <int>,
## #   thumbnail_height <int>, top_awarded_type <lgl>, hide_score <lgl>,
## #   name <chr>, quarantine <lgl>, link_flair_text_color <chr>,
## #   upvote_ratio <dbl>, author_flair_background_color <chr>, ...

There are a few fields which may be of interest to us here. The first is the “title” field:

out$title
##  [1] "The \"What happened in your state last week?\" Megathread, Week 21"                                                                                                         
##  [2] "Discussion Thread: Ongoing Debt Ceiling and Budget Negotiations"                                                                                                            
##  [3] "AOC threatens to leave Twitter after Elon Musk promotes ‘disgusting’ account impersonating her"                                                                             
##  [4] "Ron DeSantis wants to \"make America Florida\": That's a dire threat"                                                                                                       
##  [5] "Ron DeSantis says he will ‘destroy leftism’ in US if elected president"                                                                                                     
##  [6] "The Press is Falling for Anti-Abortion “Fetal Heartbeat” Propaganda"                                                                                                        
##  [7] "Trump’s Lawyers Start to Wonder if One Could Be a Snitch"                                                                                                                   
##  [8] "The Right’s War on Brands Is Stupid and Terrifying: The anti-LGBTQ attacks of Bud Light and Target are no mere boycotts—the aim is to intimidate companies into submission."
##  [9] "Trump pledges to end birthright citizenship on first day in office"                                                                                                         
## [10] "Matt Gaetz joins growing rebellion over McCarthy's debt ceiling deal"                                                                                                       
## [11] "Trump campaign uses Pete Buttigieg’s picture to mock veterans over Memorial Day weekend"                                                                                    
## [12] "Jen Psaki: Despite being a laughingstock, don’t ignore what DeSantis is actually saying"                                                                                    
## [13] "Trump escalates attacks on judges amid increasing legal scrutiny"                                                                                                           
## [14] "With Gov Walz’s signature, Minnesota becomes 23rd state to legalize Marijuana"                                                                                              
## [15] "Don’t rule out a national abortion ban in 2025"                                                                                                                             
## [16] "DeSantis Hit With FEC Complaint Over 'Brazen' Violation of Campaign Finance Law"                                                                                            
## [17] "Oregon voters could decide on ranked-choice voting soon"                                                                                                                    
## [18] "Trump Team Wants to Get Rid of Feds Investigating Him"                                                                                                                      
## [19] "Trump lawyer said to have been waved off searching office for secret records"                                                                                               
## [20] "First Republican publicly supports ousting McCarthy as Speaker"                                                                                                             
## [21] "Far-right members, unhappy with debt deal, float threatening McCarthy's speakership"                                                                                        
## [22] "Uganda’s president signs horrific “Kill the Gays” law. The Biden administration is reevaluating aid."                                                                       
## [23] "Dianne Feinstein once became confused after seeing Vice President Kamala Harris preside over the Senate, report says"                                                       
## [24] "A Republican congressman says DeSantis threatened to primary some House reps if they didn't endorse him over Trump"                                                         
## [25] "Report: Threats against abortion providers have spiked, especially in states like Oregon, Washington"                                                                       
## [26] "‘Numbers Nobody Has Ever Seen’: How the GOP Lost Wisconsin"                                                                                                                 
## [27] "Is Student Debt Forgiveness Happening, or What?"

This all looks great, but there are only a few posts! What if we wanted to grab more of them? The trick is to use an older version of Reddit which did not feature the “never ending reddit” feature, and used pages instead! The technique is exactly the same:

url <- "https://old.reddit.com/r/politics/"
pol_old <- fromJSON(paste0(url,".json"))
out_old <- pol_old$data$children$data
out_old$title
##  [1] "The \"What happened in your state last week?\" Megathread, Week 21"                                                                                                         
##  [2] "Discussion Thread: Ongoing Debt Ceiling and Budget Negotiations"                                                                                                            
##  [3] "AOC threatens to leave Twitter after Elon Musk promotes ‘disgusting’ account impersonating her"                                                                             
##  [4] "Ron DeSantis wants to \"make America Florida\": That's a dire threat"                                                                                                       
##  [5] "Ron DeSantis says he will ‘destroy leftism’ in US if elected president"                                                                                                     
##  [6] "The Press is Falling for Anti-Abortion “Fetal Heartbeat” Propaganda"                                                                                                        
##  [7] "Trump’s Lawyers Start to Wonder if One Could Be a Snitch"                                                                                                                   
##  [8] "The Right’s War on Brands Is Stupid and Terrifying: The anti-LGBTQ attacks of Bud Light and Target are no mere boycotts—the aim is to intimidate companies into submission."
##  [9] "Trump pledges to end birthright citizenship on first day in office"                                                                                                         
## [10] "Matt Gaetz joins growing rebellion over McCarthy's debt ceiling deal"                                                                                                       
## [11] "Trump campaign uses Pete Buttigieg’s picture to mock veterans over Memorial Day weekend"                                                                                    
## [12] "Jen Psaki: Despite being a laughingstock, don’t ignore what DeSantis is actually saying"                                                                                    
## [13] "Trump escalates attacks on judges amid increasing legal scrutiny"                                                                                                           
## [14] "With Gov Walz’s signature, Minnesota becomes 23rd state to legalize Marijuana"                                                                                              
## [15] "Don’t rule out a national abortion ban in 2025"                                                                                                                             
## [16] "DeSantis Hit With FEC Complaint Over 'Brazen' Violation of Campaign Finance Law"                                                                                            
## [17] "Oregon voters could decide on ranked-choice voting soon"                                                                                                                    
## [18] "Trump Team Wants to Get Rid of Feds Investigating Him"                                                                                                                      
## [19] "Trump lawyer said to have been waved off searching office for secret records"                                                                                               
## [20] "First Republican publicly supports ousting McCarthy as Speaker"                                                                                                             
## [21] "Far-right members, unhappy with debt deal, float threatening McCarthy's speakership"                                                                                        
## [22] "Uganda’s president signs horrific “Kill the Gays” law. The Biden administration is reevaluating aid."                                                                       
## [23] "Dianne Feinstein once became confused after seeing Vice President Kamala Harris preside over the Senate, report says"                                                       
## [24] "A Republican congressman says DeSantis threatened to primary some House reps if they didn't endorse him over Trump"                                                         
## [25] "Report: Threats against abortion providers have spiked, especially in states like Oregon, Washington"                                                                       
## [26] "‘Numbers Nobody Has Ever Seen’: How the GOP Lost Wisconsin"                                                                                                                 
## [27] "Is Student Debt Forgiveness Happening, or What?"

To make this actionable, we need to make a few modifications after studying how the links work. After clicking “next” the url for me yields the following, “.json” being inserted into the appropriate place:

out_old$name
##  [1] "t3_13v4ga6" "t3_13vx2bi" "t3_13vycwl" "t3_13voghb" "t3_13vv2qt"
##  [6] "t3_13vm76t" "t3_13vsrdn" "t3_13vu0nz" "t3_13vwilw" "t3_13vp9xf"
## [11] "t3_13vulnw" "t3_13vxtpg" "t3_13vpmq7" "t3_13vwauy" "t3_13vs04d"
## [16] "t3_13vwom0" "t3_13vpb5s" "t3_13vqa7b" "t3_13vosy2" "t3_13vwtan"
## [21] "t3_13vu9z1" "t3_13vwso3" "t3_13vq1bt" "t3_13vxi88" "t3_13vr8s0"
## [26] "t3_13vvzq1" "t3_13vqcnn"
next_page <- paste0("https://old.reddit.com/r/politics/.json?count=25&after=",out_old$name[nrow(out_old)])
pol_next <- fromJSON(next_page)
out_next <- pol_next$data$children$data

After playing around a little bit, the following works for grabbing multiple pages:

moar_reddit <- function(subreddit,n_pages=1){
  
  base_url <- paste0("https://old.reddit.com/r/",subreddit,"/.json")
  
  out <- list()
  out[[1]] <- fromJSON(base_url,flatten=T)$data$children
  
  for(i in 2:n_pages){
    
    Sys.sleep(1)
    
    tmp <- out[[i-1]]
    last_id <- tmp[nrow(tmp),"data.name"]
    
    out[[i]] <- fromJSON(paste0(base_url,"?after=",last_id),flatten = T)$data$children
  }
  
  do.call("rbind.fill",out)
  
}

Let’s take it for a spin!

some_more_reddit <- moar_reddit("politics",5)
as_tibble(some_more_reddit)
## # A tibble: 127 x 111
##    kind  data.approved_at_utc data.subreddit data.selftext  data.author_fullname
##    <chr> <lgl>                <chr>          <chr>          <chr>               
##  1 t3    NA                   politics       "Welcome to t~ t2_d5h4t            
##  2 t3    NA                   politics       "**Reporting:~ t2_onl9u            
##  3 t3    NA                   politics       ""             t2_4ot9563          
##  4 t3    NA                   politics       ""             t2_deaxrt           
##  5 t3    NA                   politics       ""             t2_gdfin            
##  6 t3    NA                   politics       ""             t2_d1sxkm8i         
##  7 t3    NA                   politics       ""             t2_tyyiguo          
##  8 t3    NA                   politics       ""             t2_ba0snhoh         
##  9 t3    NA                   politics       ""             t2_51aar45o         
## 10 t3    NA                   politics       ""             t2_2eef87e          
## # i 117 more rows
## # i 106 more variables: data.saved <lgl>, data.mod_reason_title <lgl>,
## #   data.gilded <int>, data.clicked <lgl>, data.title <chr>,
## #   data.link_flair_richtext <list>, data.subreddit_name_prefixed <chr>,
## #   data.hidden <lgl>, data.pwls <int>, data.link_flair_css_class <lgl>,
## #   data.downs <int>, data.thumbnail_height <int>, data.top_awarded_type <lgl>,
## #   data.hide_score <lgl>, data.name <chr>, data.quarantine <lgl>, ...

Excellent! What sort of useful text can we grab from this data? Most immediately, we might grab the titles of the posts:

head(some_more_reddit$data.title)
## [1] "The \"What happened in your state last week?\" Megathread, Week 21"                            
## [2] "Discussion Thread: Ongoing Debt Ceiling and Budget Negotiations"                               
## [3] "AOC threatens to leave Twitter after Elon Musk promotes ‘disgusting’ account impersonating her"
## [4] "Ron DeSantis wants to \"make America Florida\": That's a dire threat"                          
## [5] "Ron DeSantis says he will ‘destroy leftism’ in US if elected president"                        
## [6] "The Press is Falling for Anti-Abortion “Fetal Heartbeat” Propaganda"

It would be straightforward to clean these up and process them similarly to how we treated Tweets. We might also be interested in the comments each post generated. To to this we can use the following field:

head(some_more_reddit$data.permalink)
## [1] "/r/politics/comments/13v4ga6/the_what_happened_in_your_state_last_week/"        
## [2] "/r/politics/comments/13vx2bi/discussion_thread_ongoing_debt_ceiling_and_budget/"
## [3] "/r/politics/comments/13vycwl/aoc_threatens_to_leave_twitter_after_elon_musk/"   
## [4] "/r/politics/comments/13voghb/ron_desantis_wants_to_make_america_florida_thats/" 
## [5] "/r/politics/comments/13vv2qt/ron_desantis_says_he_will_destroy_leftism_in_us/"  
## [6] "/r/politics/comments/13vm76t/the_press_is_falling_for_antiabortion_fetal/"

Remembering that we can append just about anything with “.json” to get what we want, let’s do the following.

expand_comments <- function(reddit_data){
  
  base <- "https://old.reddit.com"
  links <- paste0(base,reddit_data$data.permalink,".json")
  
  out <- list()
  
  for(i in 1:length(links)){
    
    Sys.sleep(1)
    
    out[[i]] <- fromJSON(links[i],flatten = TRUE)$data.children[[2]]
    
  }
  
  do.call("rbind.fill",out)
}

Let’s take it for a spin on a subset of the data!

some_comments <- expand_comments(some_more_reddit[5,])

The most interesting textual information is contained within the “body” field:

head(some_comments$data.body)
## [1] "\nAs a reminder, this subreddit [is for civil discussion.](/r/politics/wiki/index#wiki_be_civil)\n\nIn general, be courteous to others. Debate/discuss/argue the merits of ideas, don't attack people. Personal insults, shill or troll accusations, hate speech, any suggestion or support of harm, violence, or death, and other rule violations can result in a permanent ban. \n\nIf you see comments in violation of our rules, please report them.\n\n For those who have questions regarding any media outlets being posted on this subreddit, please click [here](https://www.reddit.com/r/politics/wiki/approveddomainslist) to review our details as to our approved domains list and outlet criteria.\n \n\n***\n\n\n*I am a bot, and this action was performed automatically. Please [contact the moderators of this subreddit](/message/compose/?to=/r/politics) if you have any questions or concerns.*"
## [2] "Is the leftism in the room with us right now?"                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        
## [3] "Ah yes, openly admitting to the destruction of democracy. Also known as a dictatorship.  \n\nVote wisely America. This could be your last chance."                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    
## [4] "Leftism to DeSantis is putting avocado on a BLT. It’s basically anything outside of his world view."                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  
## [5] "They hate us so much for caring about people and the environment. They hate the inclusivity. They hate the logic and reason. They hate the empathy. They want it gone and replaced with authoritarian white nationalism. They are the unapologetic past. They are the unapologetic slavery, the genocide, the segregation, the internment camps. They are the ugliness that doesn’t care, but also doesn’t want the children to know what they did. Every person voting Republican at this point is a traitor to the United States voting for its downfall."                                                                                                                                                                                                                                                                                                                                                          
## [6] "A kind of \"final solution\", if you will.  Hmmm..."

Again, we are in a position where we would easily be able to process this text!

Searching Reddit

Other than scraping subReddits, another useful task we can accomplish is utilizing their “search” capabilities. Let’s start by analyzing the results from a search for, say, Mitch McConnell. To get a JSON file, we just need to insert “.json” into the appropriate place. After navigating to the next page, we can see that the URL pattern is similar to the above and so we might write the following function:

search_reddit <- function(search,n_pages){
  
    url <- "https://old.reddit.com/search.json?"
    query <- gsub(" ","+",search)
    base <- paste0(url,"q=",tolower(query))
    
    out <- list()
    out[[1]] <- fromJSON(url(base),flatten=T)$data$children
    last_id <- out[[1]][nrow(out[[1]])-3,"data.name"]
    
    for(i in 2:n_pages){
  
      Sys.sleep(1)
    
      out[[i]] <- fromJSON(url(paste0(base,"&after=",last_id)),flatten = T)$data$children
      
      last_id <- out[[i]][nrow(out[[i]]),"data.name"]
  }
  
  do.call("rbind.fill",out)
    
}

Let’s take it for a spin:

search_out <- search_reddit("Mitch McConnell", 3)

From here we can do a few things. First, note that a number of subReddits are identified which are relevant to the search:

table(search_out$data.subreddit)
## 
##          AIVoiceMemes      AngryObservation              antimeme 
##                     1                     1                     1 
##           AskALiberal          AskOldPeople             AskReddit 
##                     1                     2                     2 
##    AustralianPolitics   bestconspiracymemes   BikiniBottomTwitter 
##                     1                     1                     1 
## conservativeterrorism         CringetopiaRM      DecodingTheGurus 
##                     1                     1                     1 
##               Destiny               economy                 esist 
##                     1                     1                     1 
##              facepalm               Fauxmoi       Fuckthealtright 
##                     1                     1                     1 
##           FunnyandSad                   gay       huntingtonbeach 
##                     1                     1                     2 
##     interestingasfuck             inthenews                kansas 
##                     1                     1                     1 
##              Kentucky     LeopardsAteMyFace             lexington 
##                     1                     1                     1 
##            Louisville                 memes                   nba 
##                     1                     2                     1 
##                  news       NewsOfTheStupid               NoRules 
##                     1                     1                     1 
##          philadelphia                  pics PoliticalCompassMemes 
##                     1                     2                     1 
##        PoliticalHumor              politics        PublicFreakout 
##                     1                     8                     1 
##     Qult_Headquarters     SaintMeghanMarkle       shittyaskreddit 
##                     1                     1                     1 
##     ShittyLifeProTips             Spiderman      TheBidenshitshow 
##                     1                     1                     1 
##     TheMajorityReport     therewasanattempt                tumblr 
##                     1                     2                     1 
##     UkrainianConflict               VoteDEM          washingtondc 
##                     1                     1                     1 
##            weedstocks                 Weird      whatisthisanimal 
##                     1                     1                     1 
##    WhitePeopleTwitter     WorkersStrikeBack            WorkReform 
##                     5                     1                     1 
##        WouldYouRather 
##                     1

We also have access to the same sort of permalinks as before which lead to post comments:

head(search_out$data.permalink)
## [1] "/r/pics/comments/12wjh4n/august_2_2022_jon_stewart_as_mitch_mcconnell/"                 
## [2] "/r/AskReddit/comments/13mzli7/who_do_you_believe_is_literally_evil/"                    
## [3] "/r/interestingasfuck/comments/13d69cg/this_tortoises_strange_reaction_to_the_darker/"   
## [4] "/r/FunnyandSad/comments/13ondoy/rich_old_white_nut_jobs_screwing_the_world/"            
## [5] "/r/LeopardsAteMyFace/comments/13fk5po/joe_manchin_sided_with_the_republicans_to_secure/"
## [6] "/r/PublicFreakout/comments/13ky6iw/victoria_secret_meltdown/"

With this sort of information, we can use the above functions to either collect additional posts from those subReddits or expand the comments for posts of interest!

Google News

Google News is a news aggregation service developed by Google and launched in 2002. Boasting over 500 million visits per month, the site aggregates content from more than 20,000 publishers and 50,000 news sources worldwide.

Although not “social media,” this sort of “traditional media” may be helpful for your research projects as it will allow you to systematically collect other (recent) information about your politician of choice. These two media realms also frequently interact, with traditional media citing social media and vice versa. For our purposes it also allows us to practice the skills we have been developing while giving a (soft) tutorial into web-scraping!

To get started, we need a few new packages, xml2 and rvest. The first of these packages will allow us to work with XML files which you can roughly think of as the older and more general cousin of JSON files. The second is a package of wrappers for the xml2 and httr packages which make it incredibly easly to download and manipulate both HTML and XML. You will also need the SelectorGadget JavaScript bookmarklet, an interactive css selector finder, to get the most out of the tutorial!

Let’s start by taking a quick look at the anatomy of a Google News Query. There are three main components: the link to Google News, the query definition, and the language definition. So, as a first step towards building up to a workable scraping function, we can build up queries as follows:

google_news <- function(search_terms){
  base <- "https://news.google.com/search?q="
  query <- gsub(" ","%20",search_terms)
  lang <- "&hl=en-US&gl=US&ceid=US%3Aen"
  
  url <- paste0(base,query,lang)
  
  url
}


browseURL(google_news("ukraine war"))

browseURL(google_news("Chuck Schumer"))

Great! Next we need to think about how to grab content from these pages. To do this, let’s first grab the underlying HTML for the page. Abusing our (work in progress) function for a moment…

google_news("ukraine war") %>% 
  read_html() -> a_page

a_page
## {html_document}
## <html lang="en-US" dir="ltr">
## [1] <head>\n<meta http-equiv="Content-Type" content="text/html; charset=UTF-8 ...
## [2] <body id="yDmH0d" jscontroller="pjICDe" jsaction="rcuQ6b:npT2md; click:FA ...

This is more or less exactly what you get when you take a look at the appropriate page source. From here, we just need to know what to look for.

There are two broad approaches to figuring out what we need. For simple websites, SelectorGadget will give us exactly what we need. For more complex websites, we can inspect the source itself. For example, a search result which comes up for “Ukraine War” is the article “U.S. unveils $700 million military aid package for Ukraine; fighting rages in Donbas.” Navigating to the page source and searching for this yields the class corresponding to article titles, which we select using the “.” operator:

a_page %>% 
  html_nodes(".DY5T1d") %>% 
  html_text() %>% 
  head()
## [1] "Russia-Ukraine War: Live Updates: Drone Strikes Damage Buildings in Moscow as Kyiv Is Hit Again"
## [2] "Russia's war in Ukraine: Live updates"                                                          
## [3] "Ukraine war comes to Moscow as drones strike both capitals"                                     
## [4] "The American military veterans who've fallen in Ukraine"                                        
## [5] "Beyond Ukraine's Offensive: The West Needs to Prepare the Country's Military for a Long War"    
## [6] "Russia-Ukraine war: List of key events, day 456"

Reading a little further over, we can see the at this has an “href” attribute which links to the article

a_page %>% 
  html_nodes(".DY5T1d") %>% 
  html_attr("href") %>% 
  head()
## [1] "./articles/CBMiSGh0dHBzOi8vd3d3Lm55dGltZXMuY29tL2xpdmUvMjAyMy8wNS8zMC93b3JsZC9ydXNzaWEtdWtyYWluZS1kcm9uZXMtbmV3c9IBAA?hl=en-US&gl=US&ceid=US%3Aen"                                                                                                                                          
## [2] "./articles/CBMiUGh0dHBzOi8vd3d3LmNubi5jb20vZXVyb3BlL2xpdmUtbmV3cy9ydXNzaWEtdWtyYWluZS13YXItbmV3cy0wNS0zMC0yMy9pbmRleC5odG1s0gFUaHR0cHM6Ly9hbXAuY25uLmNvbS9jbm4vZXVyb3BlL2xpdmUtbmV3cy9ydXNzaWEtdWtyYWluZS13YXItbmV3cy0wNS0zMC0yMy9pbmRleC5odG1s?hl=en-US&gl=US&ceid=US%3Aen"                
## [3] "./articles/CBMiZ2h0dHBzOi8vd3d3LnJldXRlcnMuY29tL3dvcmxkL2V1cm9wZS91a3JhaW5lLWFpci1kZWZlbmNlcy1iYXR0bGUtZnJlc2gtd2F2ZS1ydXNzaWFuLWF0dGFja3MtMjAyMy0wNS0zMC_SAQA?hl=en-US&gl=US&ceid=US%3Aen"                                                                                                 
## [4] "./articles/CBMibWh0dHBzOi8vd3d3Lndhc2hpbmd0b25wb3N0LmNvbS9uYXRpb25hbC1zZWN1cml0eS8yMDIzLzA1LzI5L21lbW9yaWFsLWRheS1hbWVyaWNhbnMta2lsbGVkLXVrcmFpbmUtcnVzc2lhLXdhci_SAQA?hl=en-US&gl=US&ceid=US%3Aen"                                                                                         
## [5] "./articles/CBMiS2h0dHBzOi8vd3d3LmZvcmVpZ25hZmZhaXJzLmNvbS91a3JhaW5lL3J1c3NpYS13YXItYmV5b25kLXVrcmFpbmVzLW9mZmVuc2l2ZdIBAA?hl=en-US&gl=US&ceid=US%3Aen"                                                                                                                                      
## [6] "./articles/CBMiVmh0dHBzOi8vd3d3LmFsamF6ZWVyYS5jb20vbmV3cy8yMDIzLzUvMjUvcnVzc2lhLXVrcmFpbmUtd2FyLWxpc3Qtb2Yta2V5LWV2ZW50cy1kYXktNDU20gFaaHR0cHM6Ly93d3cuYWxqYXplZXJhLmNvbS9hbXAvbmV3cy8yMDIzLzUvMjUvcnVzc2lhLXVrcmFpbmUtd2FyLWxpc3Qtb2Yta2V5LWV2ZW50cy1kYXktNDU2?hl=en-US&gl=US&ceid=US%3Aen"

Scrolling to the right, we can also find the place were the publication date is kept:

a_page %>% 
  html_nodes(".WW6dff") %>% 
  html_attr("datetime") %>% 
  head()
## [1] "2023-05-30T20:40:16Z" "2023-05-30T18:50:00Z" "2023-05-30T18:33:00Z"
## [4] "2023-05-29T08:00:00Z" "2023-05-10T07:00:00Z" "2023-05-25T02:22:57Z"

Likewise, we can find the “vehicle” or “publisher” information in the following class:

a_page %>% 
  html_nodes(".wEwyrc") %>% 
  html_text()
##   [1] "The New York Times"          "CNN"                        
##   [3] "Reuters"                     "The Washington Post"        
##   [5] "Foreign Affairs Magazine"    "Al Jazeera English"         
##   [7] "The Guardian"                "The Independent"            
##   [9] "Air & Space Forces Magazine" "BBC"                        
##  [11] "The Guardian"                "Al Jazeera English"         
##  [13] "BBC"                         "NDTV"                       
##  [15] "PBS NewsHour"                "The Independent"            
##  [17] "The Guardian"                "Sky News"                   
##  [19] "Al Jazeera English"          "Euronews"                   
##  [21] "CNBC"                        "The Washington Post"        
##  [23] "Al Jazeera English"          "BBC"                        
##  [25] "Euronews"                    "The Guardian"               
##  [27] "Sky News"                    "The Associated Press"       
##  [29] "Business Insider"            "Al Jazeera English"         
##  [31] "CBS News"                    "The Independent"            
##  [33] "Breaking Defense"            "The Guardian"               
##  [35] "USA TODAY"                   "CBS News"                   
##  [37] "The New York Times"          "CNBC"                       
##  [39] "BBC"                         "CNN"                        
##  [41] "NBC News"                    "The Guardian"               
##  [43] "BBC"                         "The Guardian"               
##  [45] "Slate"                       "USA TODAY"                  
##  [47] "Reuters"                     "BBC"                        
##  [49] "CNN International"           "Al Jazeera English"         
##  [51] "BBC"                         "The Guardian"               
##  [53] "National Post"               "The Washington Post"        
##  [55] "Vox.com"                     "Reuters.com"                
##  [57] "The Washington Post"         "CNN"                        
##  [59] "Al Jazeera English"          "Reuters"                    
##  [61] "Al Jazeera English"          "Reuters.com"                
##  [63] "The Guardian"                "CNBC"                       
##  [65] "Al Jazeera English"          "CNN"                        
##  [67] "Euronews"                    "Reuters"                    
##  [69] "The Guardian"                "The Washington Post"        
##  [71] "The Washington Post"         "BBC"                        
##  [73] "The Washington Post"         "USA TODAY"                  
##  [75] "Yahoo News"                  "Newsweek"                   
##  [77] "NBC News"                    "HuffPost UK"                
##  [79] "Al Jazeera English"          "The Independent"            
##  [81] "Times of India"              "Al Jazeera English"         
##  [83] "The Globe and Mail"          "Al Jazeera English"         
##  [85] "Reuters"                     "Financial Times"            
##  [87] "Al Jazeera English"          "The Guardian"               
##  [89] "Sky News"                    "Forbes"                     
##  [91] "The Independent"             "Euronews"                   
##  [93] "The Independent"             "South China Morning Post"   
##  [95] "The New York Times"          "Fox News"                   
##  [97] "Al Jazeera English"          "Kyiv Independent"           
##  [99] "The Independent"             "Al Jazeera English"         
## [101] "The Washington Post"         "EURACTIV"                   
## [103] "YouTube"                     "The Strategist"             
## [105] "YouTube"

Now let’s throw everything together into an updated version of our function!

google_news <- function(search_terms){
  
  base <- "https://news.google.com/search?q="
  query <- gsub(" ","%20",search_terms)
  lang <- "&hl=en-US&gl=US&ceid=US%3Aen"
  
  url <- paste0(base,query,lang)
  
  result <- read_html(url)
  
  result %>% 
    html_nodes(".DY5T1d") %>% 
    html_text() -> title
  
  result %>% 
    html_nodes(".DY5T1d") %>% 
    html_attr("href") -> link
  
  sapply(link,function(x)paste0("https://news.google.com",substring(x,2))) -> link
  
  result %>% 
    html_nodes(".WW6dff") %>% 
    html_attr("datetime") -> date
  
  result %>% 
    html_nodes(".wEwyrc") %>% 
    html_text() -> publisher
  
  captured <- min(length(publisher),length(title),length(link),length(date))
  
  out <- data.frame(publisher = publisher[1:captured],
                    title = title[1:captured],
                    link = link[1:captured],
                    date = date[1:captured])
  
  rownames(out) <- NULL
  
  out
}

Let’s take it for a spin!

biden <- google_news("Joe Biden")
as_tibble(biden)
## # A tibble: 112 x 4
##    publisher          title                                          link  date 
##    <fct>              <fct>                                          <fct> <fct>
##  1 The Daily Beast    Biden Accuser Tara Reade Claims She Fled to R~ http~ 2023~
##  2 MSNBC              Biden (literally) laughs off question about p~ http~ 2023~
##  3 Yahoo News         Biden Has Priceless Response To Fox News Ques~ http~ 2023~
##  4 Newsweek           Video of Joe Biden's Response to Potential Tr~ http~ 2023~
##  5 Fox News           Republicans to hold FBI Director Wray in cont~ http~ 2023~
##  6 The New York Times Opinion | Biden’s Age, the Economy and Trump:~ http~ 2023~
##  7 MSNBC              This poll of Black adults looks bad for Biden~ http~ 2023~
##  8 Newsweek           Joe Biden Rewards Donors With Admin Positions~ http~ 2023~
##  9 CNN                Debt ceiling deal exposes Biden and McCarthy’~ http~ 2023~
## 10 NBC News           Debt ceiling deal details: What does the Bide~ http~ 2023~
## # i 102 more rows

Sweet! Two other tasks before we finish. First, we would prefer to have the actual URL of the news article rather than the Google News re-direct link. Second, we would like to have more information about the article – a description rather than just the title. To do this, we need to introduce one additional package, RSelenium and slightly modify the code.

NOTE: this will require the most recent version of Chrome to work. There may be some driver issues with Chrome, but these can be fixed by following the instructions to delete the LICENSE.chromedriver here and setting the appropriate “chromever,” the options for which are available via binman::list_versions(appname = "chromedriver").

expand_results <- function(data_frame){

  system("taskkill /im java.exe /f", intern=FALSE, ignore.stdout=FALSE)
  
  google_link <- data_frame$link
  
  prefs = list("profile.managed_default_content_settings.images" = 2L,
               "profile.default_content_setting_values.notifications" = 2,
               "profile.managed_default_content_settings.stylesheets" = 2,
               "profile.managed_default_content_settings.cookies" = 2,
               "profile.managed_default_content_settings.javascript" = 1,
               "profile.managed_default_content_settings.plugins" = 1,
               "profile.managed_default_content_settings.popups" = 2,
               "profile.managed_default_content_settings.geolocation" = 2,
               "profile.managed_default_content_settings.media_stream" = 2)
  
  cprof <- list(chromeOptions = list(prefs = prefs))
  rD <- rsDriver(browser = "chrome",
                extraCapabilities = cprof,
                 chromever = "113.0.5672.63",
                 verbose = F)
  remDr <- rD[["client"]]
  
  tmp <- list()
  for(i in 1:length(google_link)){

    url <- google_link[i]
    remDr$navigate(url)

    true_link <- unlist(remDr$getCurrentUrl())

    remDr$getPageSource()[[1]] %>%
      read_html() %>%
      html_node(xpath = '//meta[@name="description"]') %>%
      html_attr("content") -> description

    tmp[[i]] <- data.frame(true_link = true_link,
                           description = description)

  }
  
  tmp <- do.call("rbind",tmp)
  
  system("taskkill /im java.exe /f", intern=FALSE, ignore.stdout=FALSE)
  
  cbind(data_frame,tmp)
  
}

Let’s see what we get!

extra <- expand_results(biden[1:10,])
as_tibble(extra)
## # A tibble: 10 x 6
##    publisher          title                    link  date  true_link description
##    <fct>              <fct>                    <fct> <fct> <fct>     <fct>      
##  1 The Daily Beast    Biden Accuser Tara Read~ http~ 2023~ https://~ "In a Russ~
##  2 MSNBC              Biden (literally) laugh~ http~ 2023~ https://~ "If Donald~
##  3 Yahoo News         Biden Has Priceless Res~ http~ 2023~ https://~ "The presi~
##  4 Newsweek           Video of Joe Biden's Re~ http~ 2023~ https://~ "\"Where a~
##  5 Fox News           Republicans to hold FBI~ http~ 2023~ https://~ "House Ove~
##  6 The New York Times Opinion | Biden’s Age, ~ http~ 2023~ https://~ "The group~
##  7 MSNBC              This poll of Black adul~ http~ 2023~ https://~ "Bidens' p~
##  8 Newsweek           Joe Biden Rewards Donor~ http~ 2023~ https://~ "Biden onc~
##  9 CNN                Debt ceiling deal expos~ http~ 2023~ https://~ "President~
## 10 NBC News           Debt ceiling deal detai~ http~ 2023~ https://~ "Joe Biden~

Success! From these principles you can (somewhat) easily expand into scraping the news articles themselves, the links they contain, etc!

Exercises

Before we get ahead of ourselves, we want to make sure that you have fundamentals in order. Do the following:

Write a script which…

  1. Collect five pages from the subReddit of your choice, clean the “title” field, and creates a word cloud. Conduct PCA on the appropriate document-term matrix; are there any notable clusters?
  2. Collect five pages from the Reddit search of your choice, clean the “title” field, and create a word cloud. Conduct PCA on the appropriate document-term matrix; are there any notable clusters?
  3. Collect Google News results for the search term of your choice.
    • Clean the “title” field, create a word cloud, and conduct PCA as above.
    • Now “expand” the data.frame by scraping the resulting links. Clean the “description” field, creade a word cloud, and conduct PCA as above.

Save and submit your working R script to the Exercise/Quiz Submission Link by the end of the day (ideally, end of lab session!).

LS0tDQp0aXRsZTogIiINCmF1dGhvcjogIkNocmlzdG9waGVyIFNjaHdhcnoiDQpwYWdlczoNCiAgZXh0cmE6IHRydWUNCm91dHB1dDogDQogIGh0bWxfZG9jdW1lbnQ6DQogICAgdG9jOiB0cnVlDQogICAgDQogICAgdG9jX2RlcHRoOiAzDQogICAgdG9jX2Zsb2F0OiB0cnVlDQogICAgY29kZV9kb3dubG9hZDogdHJ1ZQ0KLS0tDQoNCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQ0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGVjaG8gPSBUUlVFKQ0KYGBgDQoNCiMgTGFiIDU6IFJlZGRpdCBEYXRhIGFuZCBXZWIgU2NyYXBpbmcNCg0KIyMgSW50cm9kdWN0aW9uDQoNClRvZGF5IHdlIGFyZSBnb2luZyB0byBzaGlmdCBnZWFycyBzbGlnaHRseSB0byBjb25zaWRlciBhbHRlcm5hdGl2ZSBzb3VyY2VzIG9mIHRleHR1YWwgZGF0YSBvZiBwb2xpdGljYWwgcmVsZXZhbmNlLiAgV2Ugd2lsbCBmb2N1cyBvbiB0d28gYWx0ZXJuYXRpdmUgc291cmNlcyBvZiBkYXRhIC0tIFJlZGRpdCBhbmQgR29vZ2xlIE5ld3MuDQoNCiMjIFdoYXQgaXMgUmVkZGl0Pw0KDQpSZWRkaXQgaXMgYSBzb2NpYWwgbmV3cyBhZ2dyZWdhdGlvbiBhbmQgZGlzY3Vzc2lvbiB3ZWJzaXRlIGpvdW5kZWQgaW4gMjAwNS4NCg0KVG9kYXksIHRoZSBwbGF0Zm9ybSBib2FzdHMgdGhlIGZvbGxvd2luZyBbc3RhdGlzdGljc10oaHR0cHM6Ly90aHJpdmVteXdheS5jb20vcmVkZGl0LXN0YXRpc3RpY3MvKQ0KDQorIDd0aCBtb3N0IHZpc2l0ZWQgc2l0ZSBpbiB0aGUgVVMsIDI5dGggbW9zdCB2aXNpdGVkIGluIHRoZSB3b3JsZA0KKyA4dGggbW9zdCB1c2VkIHNvY2lhbCBtZWRpYSBwbGF0Zm9ybSBpbiB0aGUgVVMNCisgRWxldmVuIG1pbGxpb24gcG9zdHMgYXJlIHN1Ym1pdHRlZCB0byBSZWRkaXQgZWFjaCBtb250aCwgb3ZlciAyIGJpbGxpb24gY29tbWVudHMgcGVyIHllYXINCisgUmVkZGl0IGlzIHByaW1hcmlseSB5b3VuZywgMTgtMjkgeW8gbWFrZSB1cCA2NCUgb2YgdGhlIHVzZXIgYmFzZSBhbmQgMzAtNDkgbWFrZSB1cCBhbm90aGVyIDI5JQ0KKyBTb21ldGhpbmcgbGlrZSAxIGluIDQgcGVvcGxlIGJldHdlZW4gdGhlIGFnZSBvZiAyNSBhbmQgMjkgdXNlIFJlZGRpdCBhY3RpdmVseQ0KKyAyMjIgbWlsbGlvbiB1c2VycyBsaXZlIHdpdGhpbiB0aGUgVVM7IHJvdWdobHkgaGFsZiBvZiB0aGUgUmVkZGl0IGNvbW11bml0eS4NCisgNDMwIG1pbGxpb24gbW9udGhseSBhY3RpdmUgdXNlcnMsIDUyIG1pbGxpb24gZGFpbHkgYWN0aXZlIHVzZXJzDQorIDQyJSBvZiB1c2VycyBnZXQgbmV3cyBmcm9tIFJlZGRpdA0KDQojIyBHcmFiYmluZyBpbmZvcm1hdGlvbiBmcm9tIGEgc3ViUmVkZGl0DQoNCk9uZSBvZiB0aGUgbmljZSB0aGluZ3MgYWJvdXQgUmVkZGl0IGlzIHRoYXQgdGhleSBhbGxvdyBmb3Igbm9uLWF1dGhlbnRpY2F0ZWQgY2FsbHMgdG8gdGhlaXIgQVBJIGJ5IHNpbXBseSBhZGRpbmcgIi5qc29uIiB0byB0aGUgYXBwcm9wcmlhdGUgcGFydCBvZiBhIGdpdmVuIFVSTC4gIFRvIGdldCBzdGFydGVkIGxldCdzIGxvb2sgYXQgdGhlIG1haW4gW3BvbGl0aWNzIHN1YnJlZGRpdF0oaHR0cHM6Ly93d3cucmVkZGl0LmNvbS9yL3BvbGl0aWNzLykgYW5kIHRoZW4gY29tcGFyZSB0byB0aGUgc2FtZSBVUkwgYnV0IFt3aXRoICIuanNvbiIgYXBwZW5kZWRdKGh0dHBzOi8vd3d3LnJlZGRpdC5jb20vci9wb2xpdGljcy8uanNvbikuICBJZiB3ZSB3YW50IHRvIHJlYWQgdGhpcyBpbmZvcm1hdGlvbiBpbnRvIFIgd2UgY2FuIGRvIHRoZSBmb2xsb3dpbmc6DQoNCmBgYHtyIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0V9DQpsaWJyYXJ5KGpzb25saXRlKQ0KbGlicmFyeSh0aWR5dmVyc2UpDQpsaWJyYXJ5KHBseXIpDQpsaWJyYXJ5KGRwbHlyKQ0KbGlicmFyeShodHRyKQ0KbGlicmFyeSh4bWwyKQ0KbGlicmFyeShydmVzdCkNCmxpYnJhcnkoUlNlbGVuaXVtKQ0KbGlicmFyeShrbml0cikNCmBgYA0KDQpgYGB7cn0NCnVybCA8LSAiaHR0cHM6Ly93d3cucmVkZGl0LmNvbS9yL3BvbGl0aWNzLyINCnBvbCA8LSBmcm9tSlNPTihwYXN0ZTAodXJsLCIuanNvbiIpKQ0KYGBgDQoNCkVhc3kgcGVhc3kuICBUbyBhY2Nlc3MgaW5mb3JtYXRpb24gd2hpY2ggaXMgcmVsZXZhbnQgdG8gb3VyIHB1cnBvc2VzLCB3ZSBuZWVkIHRvIHBhcnNlIHRocm91Z2ggdGhpcyBsaXN0IGEgbGl0dGxlIGJpdCBhcyBmb2xsb3dzLCB0aGUgcmVsZXZhbnQgZmllbGRzIGJlaW5nIGRlZmluZWQgW2hlcmVdKGh0dHBzOi8vZ2l0aHViLmNvbS9yZWRkaXQtYXJjaGl2ZS9yZWRkaXQvd2lraS9KU09OKS4NCg0KYGBge3J9DQpvdXQgPC0gcG9sJGRhdGEkY2hpbGRyZW4kZGF0YQ0KYXNfdGliYmxlKG91dCkNCmBgYA0KDQpUaGVyZSBhcmUgYSBmZXcgZmllbGRzIHdoaWNoIG1heSBiZSBvZiBpbnRlcmVzdCB0byB1cyBoZXJlLiAgVGhlIGZpcnN0IGlzIHRoZSAidGl0bGUiIGZpZWxkOg0KDQpgYGB7cn0NCm91dCR0aXRsZQ0KYGBgDQoNClRoaXMgYWxsIGxvb2tzIGdyZWF0LCBidXQgdGhlcmUgYXJlIG9ubHkgYSBmZXcgcG9zdHMhICBXaGF0IGlmIHdlIHdhbnRlZCB0byBncmFiIG1vcmUgb2YgdGhlbT8gIFRoZSB0cmljayBpcyB0byB1c2UgYW4gW29sZGVyIHZlcnNpb24gb2YgUmVkZGl0XShodHRwczovL29sZC5yZWRkaXQuY29tL3IvcG9saXRpY3MvKSB3aGljaCBkaWQgbm90IGZlYXR1cmUgdGhlICJuZXZlciBlbmRpbmcgcmVkZGl0IiBmZWF0dXJlLCBhbmQgdXNlZCBwYWdlcyBpbnN0ZWFkISAgVGhlIHRlY2huaXF1ZSBpcyBleGFjdGx5IHRoZSBzYW1lOg0KDQpgYGB7cn0NCnVybCA8LSAiaHR0cHM6Ly9vbGQucmVkZGl0LmNvbS9yL3BvbGl0aWNzLyINCnBvbF9vbGQgPC0gZnJvbUpTT04ocGFzdGUwKHVybCwiLmpzb24iKSkNCm91dF9vbGQgPC0gcG9sX29sZCRkYXRhJGNoaWxkcmVuJGRhdGENCmBgYA0KDQpgYGB7cn0NCm91dF9vbGQkdGl0bGUNCmBgYA0KDQpUbyBtYWtlIHRoaXMgYWN0aW9uYWJsZSwgd2UgbmVlZCB0byBtYWtlIGEgZmV3IG1vZGlmaWNhdGlvbnMgYWZ0ZXIgc3R1ZHlpbmcgaG93IHRoZSBsaW5rcyB3b3JrLiAgQWZ0ZXIgY2xpY2tpbmcgIm5leHQiIHRoZSB1cmwgZm9yIG1lIHlpZWxkcyB0aGUgZm9sbG93aW5nLCAiLmpzb24iIGJlaW5nIGluc2VydGVkIGludG8gdGhlIGFwcHJvcHJpYXRlIHBsYWNlOg0KDQpgYGB7cn0NCm91dF9vbGQkbmFtZQ0KbmV4dF9wYWdlIDwtIHBhc3RlMCgiaHR0cHM6Ly9vbGQucmVkZGl0LmNvbS9yL3BvbGl0aWNzLy5qc29uP2NvdW50PTI1JmFmdGVyPSIsb3V0X29sZCRuYW1lW25yb3cob3V0X29sZCldKQ0KcG9sX25leHQgPC0gZnJvbUpTT04obmV4dF9wYWdlKQ0Kb3V0X25leHQgPC0gcG9sX25leHQkZGF0YSRjaGlsZHJlbiRkYXRhDQpgYGANCg0KQWZ0ZXIgcGxheWluZyBhcm91bmQgYSBsaXR0bGUgYml0LCB0aGUgZm9sbG93aW5nIHdvcmtzIGZvciBncmFiYmluZyBtdWx0aXBsZSBwYWdlczoNCg0KYGBge3J9DQptb2FyX3JlZGRpdCA8LSBmdW5jdGlvbihzdWJyZWRkaXQsbl9wYWdlcz0xKXsNCiAgDQogIGJhc2VfdXJsIDwtIHBhc3RlMCgiaHR0cHM6Ly9vbGQucmVkZGl0LmNvbS9yLyIsc3VicmVkZGl0LCIvLmpzb24iKQ0KICANCiAgb3V0IDwtIGxpc3QoKQ0KICBvdXRbWzFdXSA8LSBmcm9tSlNPTihiYXNlX3VybCxmbGF0dGVuPVQpJGRhdGEkY2hpbGRyZW4NCiAgDQogIGZvcihpIGluIDI6bl9wYWdlcyl7DQogICAgDQogICAgU3lzLnNsZWVwKDEpDQogICAgDQogICAgdG1wIDwtIG91dFtbaS0xXV0NCiAgICBsYXN0X2lkIDwtIHRtcFtucm93KHRtcCksImRhdGEubmFtZSJdDQogICAgDQogICAgb3V0W1tpXV0gPC0gZnJvbUpTT04ocGFzdGUwKGJhc2VfdXJsLCI/YWZ0ZXI9IixsYXN0X2lkKSxmbGF0dGVuID0gVCkkZGF0YSRjaGlsZHJlbg0KICB9DQogIA0KICBkby5jYWxsKCJyYmluZC5maWxsIixvdXQpDQogIA0KfQ0KDQpgYGANCg0KTGV0J3MgdGFrZSBpdCBmb3IgYSBzcGluIQ0KDQpgYGB7cn0NCnNvbWVfbW9yZV9yZWRkaXQgPC0gbW9hcl9yZWRkaXQoInBvbGl0aWNzIiw1KQ0KYXNfdGliYmxlKHNvbWVfbW9yZV9yZWRkaXQpDQpgYGANCg0KRXhjZWxsZW50ISAgV2hhdCBzb3J0IG9mIHVzZWZ1bCB0ZXh0IGNhbiB3ZSBncmFiIGZyb20gdGhpcyBkYXRhPyAgTW9zdCBpbW1lZGlhdGVseSwgd2UgbWlnaHQgZ3JhYiB0aGUgdGl0bGVzIG9mIHRoZSBwb3N0czoNCg0KYGBge3J9DQpoZWFkKHNvbWVfbW9yZV9yZWRkaXQkZGF0YS50aXRsZSkNCmBgYA0KDQpJdCB3b3VsZCBiZSBzdHJhaWdodGZvcndhcmQgdG8gY2xlYW4gdGhlc2UgdXAgYW5kIHByb2Nlc3MgdGhlbSBzaW1pbGFybHkgdG8gaG93IHdlIHRyZWF0ZWQgVHdlZXRzLiAgV2UgbWlnaHQgYWxzbyBiZSBpbnRlcmVzdGVkIGluIHRoZSBjb21tZW50cyBlYWNoIHBvc3QgZ2VuZXJhdGVkLiAgVG8gdG8gdGhpcyB3ZSBjYW4gdXNlIHRoZSBmb2xsb3dpbmcgZmllbGQ6DQpgYGB7cn0NCmhlYWQoc29tZV9tb3JlX3JlZGRpdCRkYXRhLnBlcm1hbGluaykNCmBgYA0KDQpSZW1lbWJlcmluZyB0aGF0IHdlIGNhbiBhcHBlbmQganVzdCBhYm91dCBhbnl0aGluZyB3aXRoICIuanNvbiIgdG8gZ2V0IHdoYXQgd2Ugd2FudCwgbGV0J3MgZG8gdGhlIGZvbGxvd2luZy4NCg0KYGBge3J9DQpleHBhbmRfY29tbWVudHMgPC0gZnVuY3Rpb24ocmVkZGl0X2RhdGEpew0KICANCiAgYmFzZSA8LSAiaHR0cHM6Ly9vbGQucmVkZGl0LmNvbSINCiAgbGlua3MgPC0gcGFzdGUwKGJhc2UscmVkZGl0X2RhdGEkZGF0YS5wZXJtYWxpbmssIi5qc29uIikNCiAgDQogIG91dCA8LSBsaXN0KCkNCiAgDQogIGZvcihpIGluIDE6bGVuZ3RoKGxpbmtzKSl7DQogICAgDQogICAgU3lzLnNsZWVwKDEpDQogICAgDQogICAgb3V0W1tpXV0gPC0gZnJvbUpTT04obGlua3NbaV0sZmxhdHRlbiA9IFRSVUUpJGRhdGEuY2hpbGRyZW5bWzJdXQ0KICAgIA0KICB9DQogIA0KICBkby5jYWxsKCJyYmluZC5maWxsIixvdXQpDQp9DQpgYGANCg0KTGV0J3MgdGFrZSBpdCBmb3IgYSBzcGluIG9uIGEgc3Vic2V0IG9mIHRoZSBkYXRhIQ0KDQpgYGB7cn0NCnNvbWVfY29tbWVudHMgPC0gZXhwYW5kX2NvbW1lbnRzKHNvbWVfbW9yZV9yZWRkaXRbNSxdKQ0KYGBgDQoNClRoZSBtb3N0IGludGVyZXN0aW5nIHRleHR1YWwgaW5mb3JtYXRpb24gaXMgY29udGFpbmVkIHdpdGhpbiB0aGUgImJvZHkiIGZpZWxkOg0KDQpgYGB7cn0NCmhlYWQoc29tZV9jb21tZW50cyRkYXRhLmJvZHkpDQpgYGANCg0KQWdhaW4sIHdlIGFyZSBpbiBhIHBvc2l0aW9uIHdoZXJlIHdlIHdvdWxkIGVhc2lseSBiZSBhYmxlIHRvIHByb2Nlc3MgdGhpcyB0ZXh0IQ0KDQoNCiMjIFNlYXJjaGluZyBSZWRkaXQNCg0KT3RoZXIgdGhhbiBzY3JhcGluZyBzdWJSZWRkaXRzLCBhbm90aGVyIHVzZWZ1bCB0YXNrIHdlIGNhbiBhY2NvbXBsaXNoIGlzIHV0aWxpemluZyB0aGVpciAic2VhcmNoIiBjYXBhYmlsaXRpZXMuICBMZXQncyBzdGFydCBieSBhbmFseXppbmcgdGhlIHJlc3VsdHMgZnJvbSBhIHNlYXJjaCBmb3IsIHNheSwgW01pdGNoIE1jQ29ubmVsbF0oaHR0cHM6Ly9vbGQucmVkZGl0LmNvbS9zZWFyY2g/cT1taXRjaCttY2Nvbm5lbGwpLiAgVG8gZ2V0IGEgSlNPTiBmaWxlLCB3ZSBqdXN0IG5lZWQgdG8gaW5zZXJ0ICIuanNvbiIgaW50byB0aGUgW2FwcHJvcHJpYXRlIHBsYWNlXShodHRwczovL29sZC5yZWRkaXQuY29tL3NlYXJjaC5qc29uP3E9bWl0Y2grbWNjb25uZWxsKS4gIEFmdGVyIG5hdmlnYXRpbmcgdG8gdGhlIFtuZXh0IHBhZ2VdKGh0dHBzOi8vb2xkLnJlZGRpdC5jb20vc2VhcmNoP3E9bWl0Y2grbWNjb25uZWxsJmNvdW50PTIyJmFmdGVyPXQzX3Ywc2dmMyksIHdlIGNhbiBzZWUgdGhhdCB0aGUgVVJMIHBhdHRlcm4gaXMgc2ltaWxhciB0byB0aGUgYWJvdmUgYW5kIHNvIHdlIG1pZ2h0IHdyaXRlIHRoZSBmb2xsb3dpbmcgZnVuY3Rpb246DQoNCmBgYHtyfQ0Kc2VhcmNoX3JlZGRpdCA8LSBmdW5jdGlvbihzZWFyY2gsbl9wYWdlcyl7DQogIA0KICAgIHVybCA8LSAiaHR0cHM6Ly9vbGQucmVkZGl0LmNvbS9zZWFyY2guanNvbj8iDQogICAgcXVlcnkgPC0gZ3N1YigiICIsIisiLHNlYXJjaCkNCiAgICBiYXNlIDwtIHBhc3RlMCh1cmwsInE9Iix0b2xvd2VyKHF1ZXJ5KSkNCiAgICANCiAgICBvdXQgPC0gbGlzdCgpDQogICAgb3V0W1sxXV0gPC0gZnJvbUpTT04odXJsKGJhc2UpLGZsYXR0ZW49VCkkZGF0YSRjaGlsZHJlbg0KICAgIGxhc3RfaWQgPC0gb3V0W1sxXV1bbnJvdyhvdXRbWzFdXSktMywiZGF0YS5uYW1lIl0NCiAgICANCiAgICBmb3IoaSBpbiAyOm5fcGFnZXMpew0KICANCiAgICAgIFN5cy5zbGVlcCgxKQ0KICAgIA0KICAgICAgb3V0W1tpXV0gPC0gZnJvbUpTT04odXJsKHBhc3RlMChiYXNlLCImYWZ0ZXI9IixsYXN0X2lkKSksZmxhdHRlbiA9IFQpJGRhdGEkY2hpbGRyZW4NCiAgICAgIA0KICAgICAgbGFzdF9pZCA8LSBvdXRbW2ldXVtucm93KG91dFtbaV1dKSwiZGF0YS5uYW1lIl0NCiAgfQ0KICANCiAgZG8uY2FsbCgicmJpbmQuZmlsbCIsb3V0KQ0KICAgIA0KfQ0KDQpgYGANCg0KTGV0J3MgdGFrZSBpdCBmb3IgYSBzcGluOg0KDQpgYGB7cn0NCnNlYXJjaF9vdXQgPC0gc2VhcmNoX3JlZGRpdCgiTWl0Y2ggTWNDb25uZWxsIiwgMykNCmBgYA0KDQpGcm9tIGhlcmUgd2UgY2FuIGRvIGEgZmV3IHRoaW5ncy4gIEZpcnN0LCBub3RlIHRoYXQgYSBudW1iZXIgb2Ygc3ViUmVkZGl0cyBhcmUgaWRlbnRpZmllZCB3aGljaCBhcmUgcmVsZXZhbnQgdG8gdGhlIHNlYXJjaDoNCg0KYGBge3J9DQp0YWJsZShzZWFyY2hfb3V0JGRhdGEuc3VicmVkZGl0KQ0KYGBgDQoNCldlIGFsc28gaGF2ZSBhY2Nlc3MgdG8gdGhlIHNhbWUgc29ydCBvZiBwZXJtYWxpbmtzIGFzIGJlZm9yZSB3aGljaCBsZWFkIHRvIHBvc3QgY29tbWVudHM6DQoNCmBgYHtyfQ0KaGVhZChzZWFyY2hfb3V0JGRhdGEucGVybWFsaW5rKQ0KYGBgDQoNCldpdGggdGhpcyBzb3J0IG9mIGluZm9ybWF0aW9uLCB3ZSBjYW4gdXNlIHRoZSBhYm92ZSBmdW5jdGlvbnMgdG8gZWl0aGVyIGNvbGxlY3QgYWRkaXRpb25hbCBwb3N0cyBmcm9tIHRob3NlIHN1YlJlZGRpdHMgb3IgZXhwYW5kIHRoZSBjb21tZW50cyBmb3IgcG9zdHMgb2YgaW50ZXJlc3QhDQoNCiMjIEdvb2dsZSBOZXdzDQoNCkdvb2dsZSBOZXdzIGlzIGEgbmV3cyBhZ2dyZWdhdGlvbiBzZXJ2aWNlIGRldmVsb3BlZCBieSBHb29nbGUgYW5kIGxhdW5jaGVkIGluIDIwMDIuICBCb2FzdGluZyBvdmVyIDUwMCBtaWxsaW9uIHZpc2l0cyBwZXIgbW9udGgsIHRoZSBzaXRlIGFnZ3JlZ2F0ZXMgY29udGVudCBmcm9tIG1vcmUgdGhhbiAyMCwwMDAgcHVibGlzaGVycyBhbmQgNTAsMDAwIG5ld3Mgc291cmNlcyB3b3JsZHdpZGUuDQoNCkFsdGhvdWdoIG5vdCAic29jaWFsIG1lZGlhLCIgdGhpcyBzb3J0IG9mICJ0cmFkaXRpb25hbCBtZWRpYSIgbWF5IGJlIGhlbHBmdWwgZm9yIHlvdXIgcmVzZWFyY2ggcHJvamVjdHMgYXMgaXQgd2lsbCBhbGxvdyB5b3UgdG8gc3lzdGVtYXRpY2FsbHkgY29sbGVjdCBvdGhlciAocmVjZW50KSBpbmZvcm1hdGlvbiBhYm91dCB5b3VyIHBvbGl0aWNpYW4gb2YgY2hvaWNlLiAgVGhlc2UgdHdvIG1lZGlhIHJlYWxtcyBhbHNvIGZyZXF1ZW50bHkgaW50ZXJhY3QsIHdpdGggdHJhZGl0aW9uYWwgbWVkaWEgY2l0aW5nIHNvY2lhbCBtZWRpYSBhbmQgdmljZSB2ZXJzYS4gIEZvciBvdXIgcHVycG9zZXMgaXQgYWxzbyBhbGxvd3MgdXMgdG8gcHJhY3RpY2UgdGhlIHNraWxscyB3ZSBoYXZlIGJlZW4gZGV2ZWxvcGluZyB3aGlsZSBnaXZpbmcgYSAoc29mdCkgdHV0b3JpYWwgaW50byB3ZWItc2NyYXBpbmchDQoNClRvIGdldCBzdGFydGVkLCB3ZSBuZWVkIGEgZmV3IG5ldyBwYWNrYWdlcywgKip4bWwyKiogYW5kICoqcnZlc3QqKi4gIFRoZSBmaXJzdCBvZiB0aGVzZSBwYWNrYWdlcyB3aWxsIGFsbG93IHVzIHRvIHdvcmsgd2l0aCBYTUwgZmlsZXMgd2hpY2ggeW91IGNhbiByb3VnaGx5IHRoaW5rIG9mIGFzIHRoZSBvbGRlciBhbmQgbW9yZSBnZW5lcmFsIGNvdXNpbiBvZiBKU09OIGZpbGVzLiAgVGhlIHNlY29uZCBpcyBhIHBhY2thZ2Ugb2Ygd3JhcHBlcnMgZm9yIHRoZSB4bWwyIGFuZCBodHRyIHBhY2thZ2VzIHdoaWNoIG1ha2UgaXQgaW5jcmVkaWJseSBlYXNseSB0byBkb3dubG9hZCBhbmQgbWFuaXB1bGF0ZSBib3RoIEhUTUwgYW5kIFhNTC4gIFlvdSB3aWxsIGFsc28gbmVlZCB0aGUgW1NlbGVjdG9yR2FkZ2V0XShodHRwczovL3J2ZXN0LnRpZHl2ZXJzZS5vcmcvYXJ0aWNsZXMvc2VsZWN0b3JnYWRnZXQuaHRtbCkgSmF2YVNjcmlwdCBib29rbWFya2xldCwgYW4gaW50ZXJhY3RpdmUgY3NzIHNlbGVjdG9yIGZpbmRlciwgdG8gZ2V0IHRoZSBtb3N0IG91dCBvZiB0aGUgdHV0b3JpYWwhDQoNCg0KTGV0J3Mgc3RhcnQgYnkgdGFraW5nIGEgcXVpY2sgbG9vayBhdCB0aGUgYW5hdG9teSBvZiBhIFtHb29nbGUgTmV3cyBRdWVyeV0oaHR0cHM6Ly9uZXdzLmdvb2dsZS5jb20vc2VhcmNoP3E9dWtyYWluZSZobD1lbi1VUyZnbD1VUyZjZWlkPVVTJTNBZW4pLiAgVGhlcmUgYXJlIHRocmVlIG1haW4gY29tcG9uZW50czogdGhlIGxpbmsgdG8gR29vZ2xlIE5ld3MsIHRoZSBxdWVyeSBkZWZpbml0aW9uLCBhbmQgdGhlIGxhbmd1YWdlIGRlZmluaXRpb24uICBTbywgYXMgYSBmaXJzdCBzdGVwIHRvd2FyZHMgYnVpbGRpbmcgdXAgdG8gYSB3b3JrYWJsZSBzY3JhcGluZyBmdW5jdGlvbiwgd2UgY2FuIGJ1aWxkIHVwIHF1ZXJpZXMgYXMgZm9sbG93czoNCg0KYGBge3J9DQpnb29nbGVfbmV3cyA8LSBmdW5jdGlvbihzZWFyY2hfdGVybXMpew0KICBiYXNlIDwtICJodHRwczovL25ld3MuZ29vZ2xlLmNvbS9zZWFyY2g/cT0iDQogIHF1ZXJ5IDwtIGdzdWIoIiAiLCIlMjAiLHNlYXJjaF90ZXJtcykNCiAgbGFuZyA8LSAiJmhsPWVuLVVTJmdsPVVTJmNlaWQ9VVMlM0FlbiINCiAgDQogIHVybCA8LSBwYXN0ZTAoYmFzZSxxdWVyeSxsYW5nKQ0KICANCiAgdXJsDQp9DQoNCg0KYnJvd3NlVVJMKGdvb2dsZV9uZXdzKCJ1a3JhaW5lIHdhciIpKQ0KDQpicm93c2VVUkwoZ29vZ2xlX25ld3MoIkNodWNrIFNjaHVtZXIiKSkNCg0KYGBgDQoNCkdyZWF0ISAgTmV4dCB3ZSBuZWVkIHRvIHRoaW5rIGFib3V0IGhvdyB0byBncmFiIGNvbnRlbnQgZnJvbSB0aGVzZSBwYWdlcy4gIFRvIGRvIHRoaXMsIGxldCdzIGZpcnN0IGdyYWIgdGhlIHVuZGVybHlpbmcgSFRNTCBmb3IgdGhlIHBhZ2UuICBBYnVzaW5nIG91ciAod29yayBpbiBwcm9ncmVzcykgZnVuY3Rpb24gZm9yIGEgbW9tZW50Li4uDQoNCmBgYHtyfQ0KZ29vZ2xlX25ld3MoInVrcmFpbmUgd2FyIikgJT4lIA0KICByZWFkX2h0bWwoKSAtPiBhX3BhZ2UNCg0KYV9wYWdlDQpgYGANCg0KVGhpcyBpcyBtb3JlIG9yIGxlc3MgZXhhY3RseSB3aGF0IHlvdSBnZXQgd2hlbiB5b3UgdGFrZSBhIGxvb2sgYXQgdGhlIGFwcHJvcHJpYXRlIFtwYWdlIHNvdXJjZV0odmlldy1zb3VyY2U6aHR0cHM6Ly9uZXdzLmdvb2dsZS5jb20vc2VhcmNoP3E9dWtyYWluZSUyMHdhciZobD1lbi1VUyZnbD1VUyZjZWlkPVVTJTNBZW4pLiAgRnJvbSBoZXJlLCB3ZSBqdXN0IG5lZWQgdG8ga25vdyB3aGF0IHRvIGxvb2sgZm9yLiAgDQoNClRoZXJlIGFyZSB0d28gYnJvYWQgYXBwcm9hY2hlcyB0byBmaWd1cmluZyBvdXQgd2hhdCB3ZSBuZWVkLiAgRm9yIHNpbXBsZSB3ZWJzaXRlcywgU2VsZWN0b3JHYWRnZXQgd2lsbCBnaXZlIHVzIGV4YWN0bHkgd2hhdCB3ZSBuZWVkLiAgRm9yIG1vcmUgY29tcGxleCB3ZWJzaXRlcywgd2UgY2FuIGluc3BlY3QgdGhlIHNvdXJjZSBpdHNlbGYuICBGb3IgZXhhbXBsZSwgYSBzZWFyY2ggcmVzdWx0IHdoaWNoIGNvbWVzIHVwIGZvciAiVWtyYWluZSBXYXIiIGlzIHRoZSBhcnRpY2xlICJVLlMuIHVudmVpbHMgJDcwMCBtaWxsaW9uIG1pbGl0YXJ5IGFpZCBwYWNrYWdlIGZvciBVa3JhaW5lOyBmaWdodGluZyByYWdlcyBpbiBEb25iYXMuIiAgTmF2aWdhdGluZyB0byB0aGUgcGFnZSBzb3VyY2UgYW5kIHNlYXJjaGluZyBmb3IgdGhpcyB5aWVsZHMgdGhlIGNsYXNzIGNvcnJlc3BvbmRpbmcgdG8gYXJ0aWNsZSB0aXRsZXMsIHdoaWNoIHdlIHNlbGVjdCB1c2luZyB0aGUgIi4iIG9wZXJhdG9yOg0KDQpgYGB7cn0NCmFfcGFnZSAlPiUgDQogIGh0bWxfbm9kZXMoIi5EWTVUMWQiKSAlPiUgDQogIGh0bWxfdGV4dCgpICU+JSANCiAgaGVhZCgpDQpgYGANClJlYWRpbmcgYSBsaXR0bGUgZnVydGhlciBvdmVyLCB3ZSBjYW4gc2VlIHRoZSBhdCB0aGlzIGhhcyBhbiAiaHJlZiIgYXR0cmlidXRlIHdoaWNoIGxpbmtzIHRvIHRoZSBhcnRpY2xlDQoNCmBgYHtyfQ0KYV9wYWdlICU+JSANCiAgaHRtbF9ub2RlcygiLkRZNVQxZCIpICU+JSANCiAgaHRtbF9hdHRyKCJocmVmIikgJT4lIA0KICBoZWFkKCkNCmBgYA0KDQpTY3JvbGxpbmcgdG8gdGhlIHJpZ2h0LCB3ZSBjYW4gYWxzbyBmaW5kIHRoZSBwbGFjZSB3ZXJlIHRoZSBwdWJsaWNhdGlvbiBkYXRlIGlzIGtlcHQ6DQoNCmBgYHtyfQ0KYV9wYWdlICU+JSANCiAgaHRtbF9ub2RlcygiLldXNmRmZiIpICU+JSANCiAgaHRtbF9hdHRyKCJkYXRldGltZSIpICU+JSANCiAgaGVhZCgpDQpgYGANCkxpa2V3aXNlLCB3ZSBjYW4gZmluZCB0aGUgInZlaGljbGUiIG9yICJwdWJsaXNoZXIiIGluZm9ybWF0aW9uIGluIHRoZSBmb2xsb3dpbmcgY2xhc3M6DQoNCmBgYHtyfQ0KYV9wYWdlICU+JSANCiAgaHRtbF9ub2RlcygiLndFd3lyYyIpICU+JSANCiAgaHRtbF90ZXh0KCkNCmBgYA0KDQpOb3cgbGV0J3MgdGhyb3cgZXZlcnl0aGluZyB0b2dldGhlciBpbnRvIGFuIHVwZGF0ZWQgdmVyc2lvbiBvZiBvdXIgZnVuY3Rpb24hDQoNCmBgYHtyfQ0KZ29vZ2xlX25ld3MgPC0gZnVuY3Rpb24oc2VhcmNoX3Rlcm1zKXsNCiAgDQogIGJhc2UgPC0gImh0dHBzOi8vbmV3cy5nb29nbGUuY29tL3NlYXJjaD9xPSINCiAgcXVlcnkgPC0gZ3N1YigiICIsIiUyMCIsc2VhcmNoX3Rlcm1zKQ0KICBsYW5nIDwtICImaGw9ZW4tVVMmZ2w9VVMmY2VpZD1VUyUzQWVuIg0KICANCiAgdXJsIDwtIHBhc3RlMChiYXNlLHF1ZXJ5LGxhbmcpDQogIA0KICByZXN1bHQgPC0gcmVhZF9odG1sKHVybCkNCiAgDQogIHJlc3VsdCAlPiUgDQogICAgaHRtbF9ub2RlcygiLkRZNVQxZCIpICU+JSANCiAgICBodG1sX3RleHQoKSAtPiB0aXRsZQ0KICANCiAgcmVzdWx0ICU+JSANCiAgICBodG1sX25vZGVzKCIuRFk1VDFkIikgJT4lIA0KICAgIGh0bWxfYXR0cigiaHJlZiIpIC0+IGxpbmsNCiAgDQogIHNhcHBseShsaW5rLGZ1bmN0aW9uKHgpcGFzdGUwKCJodHRwczovL25ld3MuZ29vZ2xlLmNvbSIsc3Vic3RyaW5nKHgsMikpKSAtPiBsaW5rDQogIA0KICByZXN1bHQgJT4lIA0KICAgIGh0bWxfbm9kZXMoIi5XVzZkZmYiKSAlPiUgDQogICAgaHRtbF9hdHRyKCJkYXRldGltZSIpIC0+IGRhdGUNCiAgDQogIHJlc3VsdCAlPiUgDQogICAgaHRtbF9ub2RlcygiLndFd3lyYyIpICU+JSANCiAgICBodG1sX3RleHQoKSAtPiBwdWJsaXNoZXINCiAgDQogIGNhcHR1cmVkIDwtIG1pbihsZW5ndGgocHVibGlzaGVyKSxsZW5ndGgodGl0bGUpLGxlbmd0aChsaW5rKSxsZW5ndGgoZGF0ZSkpDQogIA0KICBvdXQgPC0gZGF0YS5mcmFtZShwdWJsaXNoZXIgPSBwdWJsaXNoZXJbMTpjYXB0dXJlZF0sDQogICAgICAgICAgICAgICAgICAgIHRpdGxlID0gdGl0bGVbMTpjYXB0dXJlZF0sDQogICAgICAgICAgICAgICAgICAgIGxpbmsgPSBsaW5rWzE6Y2FwdHVyZWRdLA0KICAgICAgICAgICAgICAgICAgICBkYXRlID0gZGF0ZVsxOmNhcHR1cmVkXSkNCiAgDQogIHJvd25hbWVzKG91dCkgPC0gTlVMTA0KICANCiAgb3V0DQp9DQpgYGANCg0KTGV0J3MgdGFrZSBpdCBmb3IgYSBzcGluIQ0KDQpgYGB7cn0NCmJpZGVuIDwtIGdvb2dsZV9uZXdzKCJKb2UgQmlkZW4iKQ0KYXNfdGliYmxlKGJpZGVuKQ0KYGBgDQoNClN3ZWV0ISAgVHdvIG90aGVyIHRhc2tzIGJlZm9yZSB3ZSBmaW5pc2guICBGaXJzdCwgd2Ugd291bGQgcHJlZmVyIHRvIGhhdmUgdGhlIGFjdHVhbCBVUkwgb2YgdGhlIG5ld3MgYXJ0aWNsZSByYXRoZXIgdGhhbiB0aGUgR29vZ2xlIE5ld3MgcmUtZGlyZWN0IGxpbmsuICBTZWNvbmQsIHdlIHdvdWxkIGxpa2UgdG8gaGF2ZSBtb3JlIGluZm9ybWF0aW9uIGFib3V0IHRoZSBhcnRpY2xlIC0tIGEgZGVzY3JpcHRpb24gcmF0aGVyIHRoYW4ganVzdCB0aGUgdGl0bGUuICBUbyBkbyB0aGlzLCB3ZSBuZWVkIHRvIGludHJvZHVjZSBvbmUgYWRkaXRpb25hbCBwYWNrYWdlLCAqKlJTZWxlbml1bSoqIGFuZCBzbGlnaHRseSBtb2RpZnkgdGhlIGNvZGUuICANCg0KTk9URTogdGhpcyB3aWxsIHJlcXVpcmUgdGhlIG1vc3QgcmVjZW50IHZlcnNpb24gb2YgQ2hyb21lIHRvIHdvcmsuICBUaGVyZSBtYXkgYmUgc29tZSBkcml2ZXIgaXNzdWVzIHdpdGggQ2hyb21lLCBidXQgdGhlc2UgY2FuIGJlIGZpeGVkIGJ5IGZvbGxvd2luZyB0aGUgaW5zdHJ1Y3Rpb25zIHRvIGRlbGV0ZSB0aGUgTElDRU5TRS5jaHJvbWVkcml2ZXIgW2hlcmVdKGh0dHBzOi8vc3RhY2tvdmVyZmxvdy5jb20vcXVlc3Rpb25zLzc1MDkyNTM1L3JzZWxlbml1bS1mYWlscy10by1vcGVuLWJyb3dzZXIpIGFuZCBzZXR0aW5nIHRoZSBhcHByb3ByaWF0ZSAiY2hyb21ldmVyLCIgdGhlIG9wdGlvbnMgZm9yIHdoaWNoIGFyZSBhdmFpbGFibGUgdmlhIGBiaW5tYW46Omxpc3RfdmVyc2lvbnMoYXBwbmFtZSA9ICJjaHJvbWVkcml2ZXIiKWAuDQoNCmBgYHtyIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0V9DQpleHBhbmRfcmVzdWx0cyA8LSBmdW5jdGlvbihkYXRhX2ZyYW1lKXsNCg0KICBzeXN0ZW0oInRhc2traWxsIC9pbSBqYXZhLmV4ZSAvZiIsIGludGVybj1GQUxTRSwgaWdub3JlLnN0ZG91dD1GQUxTRSkNCiAgDQogIGdvb2dsZV9saW5rIDwtIGRhdGFfZnJhbWUkbGluaw0KICANCiAgcHJlZnMgPSBsaXN0KCJwcm9maWxlLm1hbmFnZWRfZGVmYXVsdF9jb250ZW50X3NldHRpbmdzLmltYWdlcyIgPSAyTCwNCiAgICAgICAgICAgICAgICJwcm9maWxlLmRlZmF1bHRfY29udGVudF9zZXR0aW5nX3ZhbHVlcy5ub3RpZmljYXRpb25zIiA9IDIsDQogICAgICAgICAgICAgICAicHJvZmlsZS5tYW5hZ2VkX2RlZmF1bHRfY29udGVudF9zZXR0aW5ncy5zdHlsZXNoZWV0cyIgPSAyLA0KICAgICAgICAgICAgICAgInByb2ZpbGUubWFuYWdlZF9kZWZhdWx0X2NvbnRlbnRfc2V0dGluZ3MuY29va2llcyIgPSAyLA0KICAgICAgICAgICAgICAgInByb2ZpbGUubWFuYWdlZF9kZWZhdWx0X2NvbnRlbnRfc2V0dGluZ3MuamF2YXNjcmlwdCIgPSAxLA0KICAgICAgICAgICAgICAgInByb2ZpbGUubWFuYWdlZF9kZWZhdWx0X2NvbnRlbnRfc2V0dGluZ3MucGx1Z2lucyIgPSAxLA0KICAgICAgICAgICAgICAgInByb2ZpbGUubWFuYWdlZF9kZWZhdWx0X2NvbnRlbnRfc2V0dGluZ3MucG9wdXBzIiA9IDIsDQogICAgICAgICAgICAgICAicHJvZmlsZS5tYW5hZ2VkX2RlZmF1bHRfY29udGVudF9zZXR0aW5ncy5nZW9sb2NhdGlvbiIgPSAyLA0KICAgICAgICAgICAgICAgInByb2ZpbGUubWFuYWdlZF9kZWZhdWx0X2NvbnRlbnRfc2V0dGluZ3MubWVkaWFfc3RyZWFtIiA9IDIpDQogIA0KICBjcHJvZiA8LSBsaXN0KGNocm9tZU9wdGlvbnMgPSBsaXN0KHByZWZzID0gcHJlZnMpKQ0KICByRCA8LSByc0RyaXZlcihicm93c2VyID0gImNocm9tZSIsDQogICAgICAgICAgICAgICAgZXh0cmFDYXBhYmlsaXRpZXMgPSBjcHJvZiwNCiAgICAgICAgICAgICAgICAgY2hyb21ldmVyID0gIjExMy4wLjU2NzIuNjMiLA0KICAgICAgICAgICAgICAgICB2ZXJib3NlID0gRikNCiAgcmVtRHIgPC0gckRbWyJjbGllbnQiXV0NCiAgDQogIHRtcCA8LSBsaXN0KCkNCiAgZm9yKGkgaW4gMTpsZW5ndGgoZ29vZ2xlX2xpbmspKXsNCg0KICAgIHVybCA8LSBnb29nbGVfbGlua1tpXQ0KICAgIHJlbURyJG5hdmlnYXRlKHVybCkNCg0KICAgIHRydWVfbGluayA8LSB1bmxpc3QocmVtRHIkZ2V0Q3VycmVudFVybCgpKQ0KDQogICAgcmVtRHIkZ2V0UGFnZVNvdXJjZSgpW1sxXV0gJT4lDQogICAgICByZWFkX2h0bWwoKSAlPiUNCiAgICAgIGh0bWxfbm9kZSh4cGF0aCA9ICcvL21ldGFbQG5hbWU9ImRlc2NyaXB0aW9uIl0nKSAlPiUNCiAgICAgIGh0bWxfYXR0cigiY29udGVudCIpIC0+IGRlc2NyaXB0aW9uDQoNCiAgICB0bXBbW2ldXSA8LSBkYXRhLmZyYW1lKHRydWVfbGluayA9IHRydWVfbGluaywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGRlc2NyaXB0aW9uID0gZGVzY3JpcHRpb24pDQoNCiAgfQ0KICANCiAgdG1wIDwtIGRvLmNhbGwoInJiaW5kIix0bXApDQogIA0KICBzeXN0ZW0oInRhc2traWxsIC9pbSBqYXZhLmV4ZSAvZiIsIGludGVybj1GQUxTRSwgaWdub3JlLnN0ZG91dD1GQUxTRSkNCiAgDQogIGNiaW5kKGRhdGFfZnJhbWUsdG1wKQ0KICANCn0NCg0KYGBgDQoNCkxldCdzIHNlZSB3aGF0IHdlIGdldCENCg0KYGBge3J9DQpleHRyYSA8LSBleHBhbmRfcmVzdWx0cyhiaWRlblsxOjEwLF0pDQphc190aWJibGUoZXh0cmEpDQpgYGANCg0KU3VjY2VzcyEgIEZyb20gdGhlc2UgcHJpbmNpcGxlcyB5b3UgY2FuIChzb21ld2hhdCkgZWFzaWx5IGV4cGFuZCBpbnRvIHNjcmFwaW5nIHRoZSBuZXdzIGFydGljbGVzIHRoZW1zZWx2ZXMsIHRoZSBsaW5rcyB0aGV5IGNvbnRhaW4sIGV0YyENCg0KIyMgRXhlcmNpc2VzDQoNCkJlZm9yZSB3ZSBnZXQgYWhlYWQgb2Ygb3Vyc2VsdmVzLCB3ZSB3YW50IHRvIG1ha2Ugc3VyZSB0aGF0IHlvdSBoYXZlIGZ1bmRhbWVudGFscyBpbiBvcmRlci4gIERvIHRoZSBmb2xsb3dpbmc6DQoNCldyaXRlIGEgc2NyaXB0IHdoaWNoLi4uDQoNCjEuIENvbGxlY3QgZml2ZSBwYWdlcyBmcm9tIHRoZSBzdWJSZWRkaXQgb2YgeW91ciBjaG9pY2UsIGNsZWFuIHRoZSAidGl0bGUiIGZpZWxkLCBhbmQgY3JlYXRlcyBhIHdvcmQgY2xvdWQuICBDb25kdWN0IFBDQSBvbiB0aGUgYXBwcm9wcmlhdGUgZG9jdW1lbnQtdGVybSBtYXRyaXg7IGFyZSB0aGVyZSBhbnkgbm90YWJsZSBjbHVzdGVycz8NCjIuIENvbGxlY3QgZml2ZSBwYWdlcyBmcm9tIHRoZSBSZWRkaXQgc2VhcmNoIG9mIHlvdXIgY2hvaWNlLCBjbGVhbiB0aGUgInRpdGxlIiBmaWVsZCwgYW5kIGNyZWF0ZSBhIHdvcmQgY2xvdWQuICBDb25kdWN0IFBDQSBvbiB0aGUgYXBwcm9wcmlhdGUgZG9jdW1lbnQtdGVybSBtYXRyaXg7IGFyZSB0aGVyZSBhbnkgbm90YWJsZSBjbHVzdGVycz8NCjMuIENvbGxlY3QgR29vZ2xlIE5ld3MgcmVzdWx0cyBmb3IgdGhlIHNlYXJjaCB0ZXJtIG9mIHlvdXIgY2hvaWNlLg0KICAgICsgQ2xlYW4gdGhlICJ0aXRsZSIgZmllbGQsIGNyZWF0ZSBhIHdvcmQgY2xvdWQsIGFuZCBjb25kdWN0IFBDQSBhcyBhYm92ZS4NCiAgICArIE5vdyAiZXhwYW5kIiB0aGUgZGF0YS5mcmFtZSBieSBzY3JhcGluZyB0aGUgcmVzdWx0aW5nIGxpbmtzLiAgQ2xlYW4gdGhlICJkZXNjcmlwdGlvbiIgZmllbGQsIGNyZWFkZSBhIHdvcmQgY2xvdWQsIGFuZCBjb25kdWN0IFBDQSBhcyBhYm92ZS4NCg0KU2F2ZSBhbmQgc3VibWl0IHlvdXIgd29ya2luZyBSIHNjcmlwdCB0byB0aGUgW0V4ZXJjaXNlL1F1aXogU3VibWlzc2lvbiBMaW5rXShodHRwczovL2Zvcm1zLmdsZS96VHprNllLajdxdmVMUkJNNikgYnkgdGhlIGVuZCBvZiB0aGUgZGF5IChpZGVhbGx5LCBlbmQgb2YgbGFiIHNlc3Npb24hKS4NCg==