SOLUTIONS
Tutorial 2, Advanced Crime Analysis, BSc Security and Crime Science, UCL
Aim of this tutorial
This tutorial will help you consolidate some techniques presented and used earlier in this module about APIs and web-scraping. You will also be able to start working on your own webscraping programme that might be useful for your final project.
Task 2: Using YouTube’s API to analyse highly controversial content
Recently, the razor manufacturer Gilette released a video called We Believe: The Best Men Can Be. That video has been extremely controversial and has evoked a number considerably opinioted responses.
Use YouTube’s API to gether information about that video. Note: retrieving all comments will exceed your quota and will take a very long time.
#your code comes here
Task 3: Retrieving the public opinion on that controversial topic.
You might not that some comments below that video are on the boundary of hate speech or even in full-blown aggressive language.
Let’s look at a different source of opinion about that video. This recent opinion article in the Guardian discusses the video.
Try to access the comments made to that video and store them in a dataframe along with a unique identifier.
#your code comes here
Task 4: Recreate the lyrics scraping example with your own artist selection
In this blog you can see stepwise how to scrape data from popular music artists and then access the lyrics of their songs.
(Re-)use the code from the above blog post and re-do their scraping process using your own artist (charts) selection.
#NOTE: Differences in html node names - CORRECTED
#NOTE: Found that artists included featuring... which meant I got 404 errors - CORRECTED
#Load Packages
library(tidyverse)
library(rvest)
#Identify the url from where you want to extract data
URLBase<-"https://www.billboard.com/charts/hot-100-60th-anniversary"
#Read HTML of that page
pageBase<-read_html(URLBase)
#Get the artist name
artist<-html_nodes(pageBase, ".chart-list-item__artist")
#Convert to char
artist<-as.character(html_text(artist))
#Get the artist rank
artistRank<-html_nodes(pageBase, ".chart-list-item__rank ")
#Convert to numeric
artistRank<-as.numeric(html_text(artistRank))
# Save it to a tibble df
#Note how we replace the '\n' character (which means newline) with nothing
#Then we filter for top 10
artistsTop<-tibble('Artist' = gsub("\n", "", artist),
'Rank' = artistRank) %>%
filter(artistRank <= 10)
#Format the link to navigate to the artists genius webpage
URLGenius<-paste0("https://genius.com/artists/",artistsTop$Artist)
#Issues aries when songs in the chart are from one artist featuring another
#Because these collaborations do not have their own artist page
#We therefore consider the main artist only
#Edit song link
#Remove everything after the word Featuring
URLGenius<-lapply(URLGenius, function(x) {sub('Featuring.*', '', x)})
#Unlist so in same format
URLGenius<-unlist(URLGenius)
#Initialize a tibble df to store the results
artistLyrics<-tibble()
#Retrieve song links for each artist
for (i in 1:10) {
pageGenius<-read_html(URLGenius[i])
URLSongs <- html_nodes(pageGenius, ".mini_card_grid-song a") %>%
html_attr("href")
#Inner loop to get the Song Name and Lyrics from the Song Link
for (j in 1:10) {
# Get lyrics
songLyrics <- read_html(URLSongs[j]) %>%
html_nodes("div.lyrics p") %>%
html_text()
# Get song name
songName <- read_html(URLSongs[j]) %>%
html_nodes("h1.header_with_cover_art-primary_info-title") %>%
html_text()
# Save the details to a tibble df
artistLyrics <- rbind(artistLyrics, tibble(Rank = artistsTop$Rank[i],
Artist = artistsTop$Artist[i],
Song = songName,
Lyrics = songLyrics ))
# Insert time delay
Sys.sleep(10)
}
}
#View results
artistLyrics
Task 5: Creating a local database of missing persons
The problem of missing persons in the UK is increasingly recognised in academic research. However, to date not curated database exists that researchers can easily download to query the data of people reported missing.
Note that some of the images are from dead persons and might be confrontational to look at.
Your task is to create a local database on your computer of missing females. Use this urlc (https://www.missingpersons.police.uk/en-gb/case-search/9444442) as a starting point and retrieve the (1) gender, (2) age, (3) ethnicity and (4) circumstances details of the first three pages of search results.
#your code comes here
#Takes the base URL and pastes with 1,2,3 to get a list of pages to search
#Each page is then searched for the 'View Case Details' buttons and the hyperlinks noted
URLMissingPersons <- lapply(paste0('https://www.missingpersons.police.uk/en-gb/case-search/9444442?orderBy=dateDesc&page=', 1:3),
function(url){
url %>% read_html() %>%
html_nodes(".Case") %>%
html_nodes(".btn-default") %>%
html_attr('href')
})
#View Output
URLMissingPersons
#Unlist output
#Simplify as we have nensted lists: one for each page
URLMissingPersons<-unlist(URLMissingPersons)
#View Output
URLMissingPersons
#Add base URL to get complete URL
URLMissingPersons[] <- lapply(URLMissingPersons, function(x) paste("https://www.missingpersons.police.uk/en-gb", x, sep=""))
#Unlist
URLMissingPersons<-unlist(URLMissingPersons)
#ViewOutput
URLMissingPersons
#Extract Case Details Section
#Uses lapply to apply the following function to every URL in our case list
#Extract a list of the case detail field titles
#Extract a list of the case details field details
#Bind the two together
#Return a dataframe
dbMissingPersons<- lapply(URLMissingPersons,
function(url){
caseDetailsTitle<- url %>% read_html() %>%
html_nodes('dl.CaseDetails dt') %>%
html_text()
caseDetailsDescription<-url %>% read_html() %>%
html_nodes('dl.CaseDetails dd') %>%
html_text()
caseDetails<-data.frame(caseDetailsTitle, caseDetailsDescription)
return(caseDetails)
})
#ViewOutput
View(dbMissingPersons)
Task 6: Web-scraping for the detection of potentially suspicious items
You can use web-scraping to look for suspicious items on online market places. Let’s take the example of clothing from the “Supreme” brand on gumtree.
You can access the search results for supreme hoodie
here: https://www.gumtree.com/search?featured_filter=false&urgent_filter=false&sort=date&search_scope=false&photos_filter=false&search_category=all&q=supreme+hoodie&tq=%7B%22i%22%3A%22supreme%22%2C%22s%22%3A%22supreme+hoodie%22%2C%22p%22%3A9%2C%22t%22%3A14%7D&search_location=
Your task now is to scrape the (1) price, (2) title, and (3) location of each ad.
#your code comes here
#acces gumtree page
target_url = "https://www.gumtree.com/search?featured_filter=false&urgent_filter=false&sort=date&search_scope=false&photos_filter=false&search_category=all&q=supreme+hoodie&tq=%7B%22i%22%3A%22supreme%22%2C%22s%22%3A%22supreme+hoodie%22%2C%22p%22%3A9%2C%22t%22%3A14%7D&search_location="
target_page = read_html(target_url)
# look for page structure and find products"
ProductListings = target_page %>% html_nodes(xpath = '//*[@id="srp-results"]') %>% html_nodes("div.srp-results") %>% html_nodes("div.grid-row") %>% html_nodes("div.space-mbxs:nth-child(3)") %>% html_nodes(".list-listing-mini") %>% html_nodes("li")
# contents for products
SingleProds = ProductListings %>% html_nodes("article") %>% html_nodes("a.listing-link") %>% html_nodes("div.listing-content")
# title, price, location of products
ProductsTitle = SingleProds %>% html_node("h2.listing-title") %>% html_text()
ProductsPrice = SingleProds %>% html_node("span.listing-price") %>% html_text()
ProductsLocation = SingleProds %>% html_node("div.listing-location") %>% html_text()
# save data in data frame
AllData = data.frame(ProductsTitle, ProductsPrice, ProductsLocation)
Task 7: Start with your own web-scraping
Choose a website that you want to scrape (e.g. for your final project for this module). In this task, try to understand the structure of that website and how you can retrieve the content you wish to access.
Write a stepwise plan on what you need to do to obtain that data.
#write your plan here
#step1:
#step2:
#step3:
#step4:
#step5:
Now start by creating a my_target_url
variable and do the initial accessing of that url with the read_html
function:
#your code comes here
Task 8: Your custom web-scraper
Start building your own webscraper here:
#your code comes here
LS0tCnRpdGxlOiAiU29sdXRpb25zOiBXZWJzY3JhcGluZyBpbiBSIgphdXRob3I6ICJCIEtsZWluYmVyZywgRiBTb2xkbmVyLCBEIEhhbW1vY2tzIgpkYXRlOiAyMiBKYW51YXJ5IDIwMTkKc3VidGl0bGU6IERlcHQgb2YgU2VjdXJpdHkgYW5kIENyaW1lIFNjaWVuY2UsIFVDTApvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKLS0tCgoqKlNPTFVUSU9OUyoqCgotLS0KClR1dG9yaWFsIDIsIEFkdmFuY2VkIENyaW1lIEFuYWx5c2lzLCBCU2MgU2VjdXJpdHkgYW5kIENyaW1lIFNjaWVuY2UsIFVDTAoKLS0tCgojIyBBaW0gb2YgdGhpcyB0dXRvcmlhbAoKVGhpcyB0dXRvcmlhbCB3aWxsIGhlbHAgeW91IGNvbnNvbGlkYXRlIHNvbWUgdGVjaG5pcXVlcyBwcmVzZW50ZWQgYW5kIHVzZWQgZWFybGllciBpbiB0aGlzIG1vZHVsZSBhYm91dCBBUElzIGFuZCB3ZWItc2NyYXBpbmcuIFlvdSB3aWxsIGFsc28gYmUgYWJsZSB0byBzdGFydCB3b3JraW5nIG9uIHlvdXIgb3duIHdlYnNjcmFwaW5nIHByb2dyYW1tZSB0aGF0IG1pZ2h0IGJlIHVzZWZ1bCBmb3IgeW91ciBmaW5hbCBwcm9qZWN0LgoKCiMjIFRhc2sgMTogR2VvZ3JhcGhpY2FsIHZhcmlhdGlvbiBvZiBwdWJsaWMgcGVyY2VwdGlvbiBvbiBUd2l0dGVyCgpVc2UgVHdpdHRlcidzIEFQSSB0byByZXRyaWV2ZSBUd2VldHMgYWJvdXQgImNyaW1lIiBpbiB0aGVzZSBjaXRpZXM6ICgxKSBOZXcgWW9yayBDaXR5LiAoMikgTG9uZG9uLCAoMykgTG9zIEFuZ2VsZXMsICg0KSBBdXN0aW4sIFRleGFzLCBhbmQgKDUpIER1Ymxpbi4KClN0b3JlIGFsbCB0d2VldHMgaW4gYSBzaW5nbGUgZGF0YWZyYW1lIHdpdGggYSBjb2x1bW4gaWRlbnRpZnlpbmcgdGhlIGNpdHkuCgpgYGB7cn0KI3lvdXIgY29kZSBjb21lcyBoZXJlCmBgYAoKCiMjIFRhc2sgMjogVXNpbmcgWW91VHViZSdzIEFQSSB0byBhbmFseXNlIGhpZ2hseSBjb250cm92ZXJzaWFsIGNvbnRlbnQKClJlY2VudGx5LCB0aGUgcmF6b3IgbWFudWZhY3R1cmVyIEdpbGV0dGUgcmVsZWFzZWQgYSB2aWRlbyBjYWxsZWQgW1dlIEJlbGlldmU6IFRoZSBCZXN0IE1lbiBDYW4gQmVdKGh0dHBzOi8vd3d3LnlvdXR1YmUuY29tL3dhdGNoP3Y9a29QbXVFeVAzYTApLiBUaGF0IHZpZGVvIGhhcyBiZWVuIGV4dHJlbWVseSBjb250cm92ZXJzaWFsIGFuZCBoYXMgZXZva2VkIGEgbnVtYmVyIGNvbnNpZGVyYWJseSBvcGluaW90ZWQgcmVzcG9uc2VzLgoKVXNlIFlvdVR1YmUncyBBUEkgdG8gZ2V0aGVyIGluZm9ybWF0aW9uIGFib3V0IHRoYXQgdmlkZW8uIF9Ob3RlOiByZXRyaWV2aW5nIGFsbCBjb21tZW50cyB3aWxsIGV4Y2VlZCB5b3VyIHF1b3RhIGFuZCB3aWxsIHRha2UgYSB2ZXJ5IGxvbmcgdGltZS5fCgpgYGB7cn0KI3lvdXIgY29kZSBjb21lcyBoZXJlCmBgYAoKCiMjIFRhc2sgMzogUmV0cmlldmluZyB0aGUgcHVibGljIG9waW5pb24gb24gdGhhdCBjb250cm92ZXJzaWFsIHRvcGljLgoKWW91IG1pZ2h0IG5vdCB0aGF0IHNvbWUgY29tbWVudHMgYmVsb3cgdGhhdCB2aWRlbyBhcmUgb24gdGhlIGJvdW5kYXJ5IG9mIGhhdGUgc3BlZWNoIG9yIGV2ZW4gaW4gZnVsbC1ibG93biBhZ2dyZXNzaXZlIGxhbmd1YWdlLgoKTGV0J3MgbG9vayBhdCBhIGRpZmZlcmVudCBzb3VyY2Ugb2Ygb3BpbmlvbiBhYm91dCB0aGF0IHZpZGVvLiBUaGlzIFtyZWNlbnQgb3BpbmlvbiBhcnRpY2xlXShodHRwczovL3d3dy50aGVndWFyZGlhbi5jb20vY29tbWVudGlzZnJlZS8yMDE5L2phbi8xNi9tZW4tbWFzY3VsaW5pdHktZ2lsbGV0dGUtYWR2ZXJ0aXNlbWVudCkgaW4gdGhlIEd1YXJkaWFuIGRpc2N1c3NlcyB0aGUgdmlkZW8uCgpUcnkgdG8gYWNjZXNzIHRoZSBjb21tZW50cyBtYWRlIHRvIHRoYXQgdmlkZW8gYW5kIHN0b3JlIHRoZW0gaW4gYSBkYXRhZnJhbWUgYWxvbmcgd2l0aCBhIHVuaXF1ZSBpZGVudGlmaWVyLgoKYGBge3J9CiN5b3VyIGNvZGUgY29tZXMgaGVyZQpgYGAKCgojIyBUYXNrIDQ6IFJlY3JlYXRlIHRoZSBseXJpY3Mgc2NyYXBpbmcgZXhhbXBsZSB3aXRoIHlvdXIgb3duIGFydGlzdCBzZWxlY3Rpb24KCkluIFt0aGlzIGJsb2ddKGh0dHBzOi8vdG93YXJkc2RhdGFzY2llbmNlLmNvbS9sZWFybi10by1jcmVhdGUteW91ci1vd24tZGF0YXNldHMtd2ViLXNjcmFwaW5nLWluLXItZjkzNGEzMTc0OGE1KSB5b3UgY2FuIHNlZSBzdGVwd2lzZSBob3cgdG8gc2NyYXBlIGRhdGEgZnJvbSBwb3B1bGFyIG11c2ljIGFydGlzdHMgYW5kIHRoZW4gYWNjZXNzIHRoZSBseXJpY3Mgb2YgdGhlaXIgc29uZ3MuCgooUmUtKXVzZSB0aGUgY29kZSBmcm9tIHRoZSBhYm92ZSBibG9nIHBvc3QgYW5kIHJlLWRvIHRoZWlyIHNjcmFwaW5nIHByb2Nlc3MgdXNpbmcgeW91ciBvd24gYXJ0aXN0IChjaGFydHMpIHNlbGVjdGlvbi4KCmBgYHtyfQojTk9URTogRGlmZmVyZW5jZXMgaW4gaHRtbCBub2RlIG5hbWVzIC0gQ09SUkVDVEVECiNOT1RFOiBGb3VuZCB0aGF0IGFydGlzdHMgaW5jbHVkZWQgZmVhdHVyaW5nLi4uIHdoaWNoIG1lYW50IEkgZ290IDQwNCBlcnJvcnMgLSBDT1JSRUNURUQKCiNMb2FkIFBhY2thZ2VzCmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KHJ2ZXN0KQoKI0lkZW50aWZ5IHRoZSB1cmwgZnJvbSB3aGVyZSB5b3Ugd2FudCB0byBleHRyYWN0IGRhdGEKVVJMQmFzZTwtImh0dHBzOi8vd3d3LmJpbGxib2FyZC5jb20vY2hhcnRzL2hvdC0xMDAtNjB0aC1hbm5pdmVyc2FyeSIKI1JlYWQgSFRNTCBvZiB0aGF0IHBhZ2UKcGFnZUJhc2U8LXJlYWRfaHRtbChVUkxCYXNlKQoKI0dldCB0aGUgYXJ0aXN0IG5hbWUKYXJ0aXN0PC1odG1sX25vZGVzKHBhZ2VCYXNlLCAiLmNoYXJ0LWxpc3QtaXRlbV9fYXJ0aXN0IikKI0NvbnZlcnQgdG8gY2hhcgphcnRpc3Q8LWFzLmNoYXJhY3RlcihodG1sX3RleHQoYXJ0aXN0KSkKCiNHZXQgdGhlIGFydGlzdCByYW5rCmFydGlzdFJhbms8LWh0bWxfbm9kZXMocGFnZUJhc2UsICIuY2hhcnQtbGlzdC1pdGVtX19yYW5rICIpCiNDb252ZXJ0IHRvIG51bWVyaWMKYXJ0aXN0UmFuazwtYXMubnVtZXJpYyhodG1sX3RleHQoYXJ0aXN0UmFuaykpCgojIFNhdmUgaXQgdG8gYSB0aWJibGUgZGYKICAjTm90ZSBob3cgd2UgcmVwbGFjZSB0aGUgJ1xuJyBjaGFyYWN0ZXIgKHdoaWNoIG1lYW5zIG5ld2xpbmUpIHdpdGggbm90aGluZwogICNUaGVuIHdlIGZpbHRlciBmb3IgdG9wIDEwCmFydGlzdHNUb3A8LXRpYmJsZSgnQXJ0aXN0JyA9IGdzdWIoIlxuIiwgIiIsIGFydGlzdCksCiAgICAgICAgICAgICAgICAgICAgICAnUmFuaycgPSBhcnRpc3RSYW5rKSAlPiUKICAgICAgICAgICAgICAgICAgZmlsdGVyKGFydGlzdFJhbmsgPD0gMTApCgojRm9ybWF0IHRoZSBsaW5rIHRvIG5hdmlnYXRlIHRvIHRoZSBhcnRpc3RzIGdlbml1cyB3ZWJwYWdlClVSTEdlbml1czwtcGFzdGUwKCJodHRwczovL2dlbml1cy5jb20vYXJ0aXN0cy8iLGFydGlzdHNUb3AkQXJ0aXN0KQoKI0lzc3VlcyBhcmllcyB3aGVuIHNvbmdzIGluIHRoZSBjaGFydCBhcmUgZnJvbSBvbmUgYXJ0aXN0IGZlYXR1cmluZyBhbm90aGVyCiNCZWNhdXNlIHRoZXNlIGNvbGxhYm9yYXRpb25zIGRvIG5vdCBoYXZlIHRoZWlyIG93biBhcnRpc3QgcGFnZQojV2UgdGhlcmVmb3JlIGNvbnNpZGVyIHRoZSBtYWluIGFydGlzdCBvbmx5CgojRWRpdCBzb25nIGxpbmsKICAjUmVtb3ZlIGV2ZXJ5dGhpbmcgYWZ0ZXIgdGhlIHdvcmQgRmVhdHVyaW5nClVSTEdlbml1czwtbGFwcGx5KFVSTEdlbml1cywgZnVuY3Rpb24oeCkge3N1YignRmVhdHVyaW5nLionLCAnJywgeCl9KQojVW5saXN0IHNvIGluIHNhbWUgZm9ybWF0ClVSTEdlbml1czwtdW5saXN0KFVSTEdlbml1cykKCiNJbml0aWFsaXplIGEgdGliYmxlIGRmIHRvIHN0b3JlIHRoZSByZXN1bHRzCmFydGlzdEx5cmljczwtdGliYmxlKCkKCiNSZXRyaWV2ZSBzb25nIGxpbmtzIGZvciBlYWNoIGFydGlzdCAKZm9yIChpIGluIDE6MTApIHsKICBwYWdlR2VuaXVzPC1yZWFkX2h0bWwoVVJMR2VuaXVzW2ldKQogIFVSTFNvbmdzIDwtIGh0bWxfbm9kZXMocGFnZUdlbml1cywgIi5taW5pX2NhcmRfZ3JpZC1zb25nIGEiKSAlPiUKICAgICAgaHRtbF9hdHRyKCJocmVmIikgCiAgCiAgICNJbm5lciBsb29wIHRvIGdldCB0aGUgU29uZyBOYW1lIGFuZCBMeXJpY3MgZnJvbSB0aGUgU29uZyBMaW5rICAgIAogICAgZm9yIChqIGluIDE6MTApIHsKICAgICAgCiAgICAgICMgR2V0IGx5cmljcwogICAgICBzb25nTHlyaWNzIDwtIHJlYWRfaHRtbChVUkxTb25nc1tqXSkgJT4lCiAgICAgICAgICBodG1sX25vZGVzKCJkaXYubHlyaWNzIHAiKSAlPiUKICAgICAgICAgIGh0bWxfdGV4dCgpCiAgICAgICAgCiAgICAgICMgR2V0IHNvbmcgbmFtZQogICAgICBzb25nTmFtZSA8LSByZWFkX2h0bWwoVVJMU29uZ3Nbal0pICU+JQogICAgICAgICBodG1sX25vZGVzKCJoMS5oZWFkZXJfd2l0aF9jb3Zlcl9hcnQtcHJpbWFyeV9pbmZvLXRpdGxlIikgJT4lCiAgICAgICAgIGh0bWxfdGV4dCgpCiAgICAgICAgCiAgICAgICMgU2F2ZSB0aGUgZGV0YWlscyB0byBhIHRpYmJsZSBkZgogICAgICBhcnRpc3RMeXJpY3MgPC0gcmJpbmQoYXJ0aXN0THlyaWNzLCB0aWJibGUoUmFuayA9IGFydGlzdHNUb3AkUmFua1tpXSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgQXJ0aXN0ID0gYXJ0aXN0c1RvcCRBcnRpc3RbaV0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFNvbmcgPSBzb25nTmFtZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgTHlyaWNzID0gc29uZ0x5cmljcyApKQogICAgICAKICAgICAgIyBJbnNlcnQgdGltZSBkZWxheQogICAgICBTeXMuc2xlZXAoMTApCiAgICAgfQp9IAoKI1ZpZXcgcmVzdWx0cwphcnRpc3RMeXJpY3MKYGBgCgojIyBUYXNrIDU6IENyZWF0aW5nIGEgbG9jYWwgZGF0YWJhc2Ugb2YgbWlzc2luZyBwZXJzb25zCgpUaGUgcHJvYmxlbSBvZiBtaXNzaW5nIHBlcnNvbnMgaW4gdGhlIFVLIGlzIGluY3JlYXNpbmdseSByZWNvZ25pc2VkIGluIGFjYWRlbWljIHJlc2VhcmNoLiBIb3dldmVyLCB0byBkYXRlIG5vdCBjdXJhdGVkIGRhdGFiYXNlIGV4aXN0cyB0aGF0IHJlc2VhcmNoZXJzIGNhbiBlYXNpbHkgZG93bmxvYWQgdG8gcXVlcnkgdGhlIGRhdGEgb2YgcGVvcGxlIHJlcG9ydGVkIG1pc3NpbmcuCgpfTm90ZSB0aGF0IHNvbWUgb2YgdGhlIGltYWdlcyBhcmUgZnJvbSBkZWFkIHBlcnNvbnMgYW5kIG1pZ2h0IGJlIGNvbmZyb250YXRpb25hbCB0byBsb29rIGF0Ll8KCllvdXIgdGFzayBpcyB0byBjcmVhdGUgYSBsb2NhbCBkYXRhYmFzZSBvbiB5b3VyIGNvbXB1dGVyIG9mIG1pc3NpbmcgZmVtYWxlcy4gVXNlIHRoaXMgdXJsYyBbKGh0dHBzOi8vd3d3Lm1pc3NpbmdwZXJzb25zLnBvbGljZS51ay9lbi1nYi9jYXNlLXNlYXJjaC85NDQ0NDQyKV0oaHR0cHM6Ly93d3cubWlzc2luZ3BlcnNvbnMucG9saWNlLnVrL2VuLWdiL2Nhc2Utc2VhcmNoLzk0NDQ0NDIpIGFzIGEgc3RhcnRpbmcgcG9pbnQgYW5kIHJldHJpZXZlIHRoZSAoMSkgZ2VuZGVyLCAoMikgYWdlLCAoMykgZXRobmljaXR5IGFuZCAoNCkgY2lyY3Vtc3RhbmNlcyBkZXRhaWxzIG9mIHRoZSBmaXJzdCB0aHJlZSBwYWdlcyBvZiBzZWFyY2ggcmVzdWx0cy4KCmBgYHtyfQojeW91ciBjb2RlIGNvbWVzIGhlcmUKI1Rha2VzIHRoZSBiYXNlIFVSTCBhbmQgcGFzdGVzIHdpdGggMSwyLDMgdG8gZ2V0IGEgbGlzdCBvZiBwYWdlcyB0byBzZWFyY2gKI0VhY2ggcGFnZSBpcyB0aGVuIHNlYXJjaGVkIGZvciB0aGUgJ1ZpZXcgQ2FzZSBEZXRhaWxzJyBidXR0b25zIGFuZCB0aGUgaHlwZXJsaW5rcyBub3RlZApVUkxNaXNzaW5nUGVyc29ucyA8LSBsYXBwbHkocGFzdGUwKCdodHRwczovL3d3dy5taXNzaW5ncGVyc29ucy5wb2xpY2UudWsvZW4tZ2IvY2FzZS1zZWFyY2gvOTQ0NDQ0Mj9vcmRlckJ5PWRhdGVEZXNjJnBhZ2U9JywgMTozKSwKICAgICAgICAgICAgICAgIGZ1bmN0aW9uKHVybCl7CiAgICAgICAgICAgICAgICAgICAgdXJsICU+JSByZWFkX2h0bWwoKSAlPiUgCiAgICAgICAgICAgICAgICAgICAgICAgIGh0bWxfbm9kZXMoIi5DYXNlIikgJT4lCiAgICAgICAgICAgICAgICAgICAgICAgIGh0bWxfbm9kZXMoIi5idG4tZGVmYXVsdCIpICU+JQogICAgICAgICAgICAgICAgICAgICAgICBodG1sX2F0dHIoJ2hyZWYnKQogICAgICAgICAgICAgICAgfSkKYGBgCgpgYGB7cn0KI1ZpZXcgT3V0cHV0ClVSTE1pc3NpbmdQZXJzb25zCgojVW5saXN0IG91dHB1dAojU2ltcGxpZnkgYXMgd2UgaGF2ZSBuZW5zdGVkIGxpc3RzOiBvbmUgZm9yIGVhY2ggcGFnZQpVUkxNaXNzaW5nUGVyc29uczwtdW5saXN0KFVSTE1pc3NpbmdQZXJzb25zKQoKI1ZpZXcgT3V0cHV0ClVSTE1pc3NpbmdQZXJzb25zCgojQWRkIGJhc2UgVVJMIHRvIGdldCBjb21wbGV0ZSBVUkwKVVJMTWlzc2luZ1BlcnNvbnNbXSA8LSBsYXBwbHkoVVJMTWlzc2luZ1BlcnNvbnMsIGZ1bmN0aW9uKHgpIHBhc3RlKCJodHRwczovL3d3dy5taXNzaW5ncGVyc29ucy5wb2xpY2UudWsvZW4tZ2IiLCB4LCBzZXA9IiIpKQoKI1VubGlzdApVUkxNaXNzaW5nUGVyc29uczwtdW5saXN0KFVSTE1pc3NpbmdQZXJzb25zKQoKI1ZpZXdPdXRwdXQKVVJMTWlzc2luZ1BlcnNvbnMKYGBgCgpgYGB7cn0KI0V4dHJhY3QgQ2FzZSBEZXRhaWxzIFNlY3Rpb24KI1VzZXMgbGFwcGx5IHRvIGFwcGx5IHRoZSBmb2xsb3dpbmcgZnVuY3Rpb24gdG8gZXZlcnkgVVJMIGluIG91ciBjYXNlIGxpc3QKICAjRXh0cmFjdCBhIGxpc3Qgb2YgdGhlIGNhc2UgZGV0YWlsIGZpZWxkIHRpdGxlcwogICNFeHRyYWN0IGEgbGlzdCBvZiB0aGUgY2FzZSBkZXRhaWxzIGZpZWxkIGRldGFpbHMKICAjQmluZCB0aGUgdHdvIHRvZ2V0aGVyCiAgI1JldHVybiBhIGRhdGFmcmFtZQoKZGJNaXNzaW5nUGVyc29uczwtIGxhcHBseShVUkxNaXNzaW5nUGVyc29ucywKICAgICAgICAgICAgICAgICAgICAgICAgICBmdW5jdGlvbih1cmwpewogICAgICAgICAgICAgICAgICAgICAgICAgICAgY2FzZURldGFpbHNUaXRsZTwtIHVybCAlPiUgcmVhZF9odG1sKCkgJT4lCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGh0bWxfbm9kZXMoJ2RsLkNhc2VEZXRhaWxzIGR0JykgJT4lCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGh0bWxfdGV4dCgpCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBjYXNlRGV0YWlsc0Rlc2NyaXB0aW9uPC11cmwgJT4lIHJlYWRfaHRtbCgpICU+JQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBodG1sX25vZGVzKCdkbC5DYXNlRGV0YWlscyBkZCcpICU+JQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBodG1sX3RleHQoKQogICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBjYXNlRGV0YWlsczwtZGF0YS5mcmFtZShjYXNlRGV0YWlsc1RpdGxlLCBjYXNlRGV0YWlsc0Rlc2NyaXB0aW9uKQogICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICByZXR1cm4oY2FzZURldGFpbHMpCiAgICAgICAgICAgICAgICAgICAgICAgICAgfSkKCiNWaWV3T3V0cHV0ClZpZXcoZGJNaXNzaW5nUGVyc29ucykKYGBgCgoKCiMjIFRhc2sgNjogV2ViLXNjcmFwaW5nIGZvciB0aGUgZGV0ZWN0aW9uIG9mIHBvdGVudGlhbGx5IHN1c3BpY2lvdXMgaXRlbXMKCllvdSBjYW4gdXNlIHdlYi1zY3JhcGluZyB0byBsb29rIGZvciBzdXNwaWNpb3VzIGl0ZW1zIG9uIG9ubGluZSBtYXJrZXQgcGxhY2VzLiBMZXQncyB0YWtlIHRoZSBleGFtcGxlIG9mIGNsb3RoaW5nIGZyb20gdGhlICJTdXByZW1lIiBicmFuZCBvbiBndW10cmVlLgoKWW91IGNhbiBhY2Nlc3MgdGhlIHNlYXJjaCByZXN1bHRzIGZvciBgc3VwcmVtZSBob29kaWVgIGhlcmU6IFtodHRwczovL3d3dy5ndW10cmVlLmNvbS9zZWFyY2g/ZmVhdHVyZWRfZmlsdGVyPWZhbHNlJnVyZ2VudF9maWx0ZXI9ZmFsc2Umc29ydD1kYXRlJnNlYXJjaF9zY29wZT1mYWxzZSZwaG90b3NfZmlsdGVyPWZhbHNlJnNlYXJjaF9jYXRlZ29yeT1hbGwmcT1zdXByZW1lK2hvb2RpZSZ0cT0lN0IlMjJpJTIyJTNBJTIyc3VwcmVtZSUyMiUyQyUyMnMlMjIlM0ElMjJzdXByZW1lK2hvb2RpZSUyMiUyQyUyMnAlMjIlM0E5JTJDJTIydCUyMiUzQTE0JTdEJnNlYXJjaF9sb2NhdGlvbj1dKGh0dHBzOi8vd3d3Lmd1bXRyZWUuY29tL3NlYXJjaD9mZWF0dXJlZF9maWx0ZXI9ZmFsc2UmdXJnZW50X2ZpbHRlcj1mYWxzZSZzb3J0PWRhdGUmc2VhcmNoX3Njb3BlPWZhbHNlJnBob3Rvc19maWx0ZXI9ZmFsc2Umc2VhcmNoX2NhdGVnb3J5PWFsbCZxPXN1cHJlbWUraG9vZGllJnRxPSU3QiUyMmklMjIlM0ElMjJzdXByZW1lJTIyJTJDJTIycyUyMiUzQSUyMnN1cHJlbWUraG9vZGllJTIyJTJDJTIycCUyMiUzQTklMkMlMjJ0JTIyJTNBMTQlN0Qmc2VhcmNoX2xvY2F0aW9uPSkKCllvdXIgdGFzayBub3cgaXMgdG8gc2NyYXBlIHRoZSAoMSkgcHJpY2UsICgyKSB0aXRsZSwgYW5kICgzKSBsb2NhdGlvbiBvZiBlYWNoIGFkLgoKYGBge3J9CiN5b3VyIGNvZGUgY29tZXMgaGVyZQoKI2FjY2VzIGd1bXRyZWUgcGFnZQp0YXJnZXRfdXJsID0gImh0dHBzOi8vd3d3Lmd1bXRyZWUuY29tL3NlYXJjaD9mZWF0dXJlZF9maWx0ZXI9ZmFsc2UmdXJnZW50X2ZpbHRlcj1mYWxzZSZzb3J0PWRhdGUmc2VhcmNoX3Njb3BlPWZhbHNlJnBob3Rvc19maWx0ZXI9ZmFsc2Umc2VhcmNoX2NhdGVnb3J5PWFsbCZxPXN1cHJlbWUraG9vZGllJnRxPSU3QiUyMmklMjIlM0ElMjJzdXByZW1lJTIyJTJDJTIycyUyMiUzQSUyMnN1cHJlbWUraG9vZGllJTIyJTJDJTIycCUyMiUzQTklMkMlMjJ0JTIyJTNBMTQlN0Qmc2VhcmNoX2xvY2F0aW9uPSIKdGFyZ2V0X3BhZ2UgPSByZWFkX2h0bWwodGFyZ2V0X3VybCkKYGBgCgpgYGB7cn0KIyBsb29rIGZvciBwYWdlIHN0cnVjdHVyZSBhbmQgZmluZCBwcm9kdWN0cyIKUHJvZHVjdExpc3RpbmdzID0gdGFyZ2V0X3BhZ2UgJT4lIGh0bWxfbm9kZXMoeHBhdGggPSAnLy8qW0BpZD0ic3JwLXJlc3VsdHMiXScpICU+JSBodG1sX25vZGVzKCJkaXYuc3JwLXJlc3VsdHMiKSAlPiUgaHRtbF9ub2RlcygiZGl2LmdyaWQtcm93IikgJT4lIGh0bWxfbm9kZXMoImRpdi5zcGFjZS1tYnhzOm50aC1jaGlsZCgzKSIpICU+JSBodG1sX25vZGVzKCIubGlzdC1saXN0aW5nLW1pbmkiKSAlPiUgaHRtbF9ub2RlcygibGkiKQpgYGAKCmBgYHtyfQojIGNvbnRlbnRzIGZvciBwcm9kdWN0cwpTaW5nbGVQcm9kcyA9IFByb2R1Y3RMaXN0aW5ncyAlPiUgaHRtbF9ub2RlcygiYXJ0aWNsZSIpICU+JSBodG1sX25vZGVzKCJhLmxpc3RpbmctbGluayIpICU+JSBodG1sX25vZGVzKCJkaXYubGlzdGluZy1jb250ZW50IikKYGBgCgpgYGB7cn0KIyB0aXRsZSwgcHJpY2UsIGxvY2F0aW9uIG9mIHByb2R1Y3RzClByb2R1Y3RzVGl0bGUgPSBTaW5nbGVQcm9kcyAlPiUgaHRtbF9ub2RlKCJoMi5saXN0aW5nLXRpdGxlIikgJT4lIGh0bWxfdGV4dCgpClByb2R1Y3RzUHJpY2UgPSBTaW5nbGVQcm9kcyAlPiUgaHRtbF9ub2RlKCJzcGFuLmxpc3RpbmctcHJpY2UiKSAlPiUgaHRtbF90ZXh0KCkKUHJvZHVjdHNMb2NhdGlvbiA9IFNpbmdsZVByb2RzICU+JSBodG1sX25vZGUoImRpdi5saXN0aW5nLWxvY2F0aW9uIikgJT4lIGh0bWxfdGV4dCgpCmBgYAoKYGBge3J9CiMgc2F2ZSBkYXRhIGluIGRhdGEgZnJhbWUKQWxsRGF0YSA9IGRhdGEuZnJhbWUoUHJvZHVjdHNUaXRsZSwgUHJvZHVjdHNQcmljZSwgUHJvZHVjdHNMb2NhdGlvbikgCmBgYAoKCiMjIFRhc2sgNzogU3RhcnQgd2l0aCB5b3VyIG93biB3ZWItc2NyYXBpbmcKCkNob29zZSBhIHdlYnNpdGUgdGhhdCB5b3Ugd2FudCB0byBzY3JhcGUgKGUuZy4gZm9yIHlvdXIgZmluYWwgcHJvamVjdCBmb3IgdGhpcyBtb2R1bGUpLiBJbiB0aGlzIHRhc2ssIHRyeSB0byB1bmRlcnN0YW5kIHRoZSBzdHJ1Y3R1cmUgb2YgdGhhdCB3ZWJzaXRlIGFuZCBob3cgeW91IGNhbiByZXRyaWV2ZSB0aGUgY29udGVudCB5b3Ugd2lzaCB0byBhY2Nlc3MuCgpXcml0ZSBhIHN0ZXB3aXNlIHBsYW4gb24gd2hhdCB5b3UgbmVlZCB0byBkbyB0byBvYnRhaW4gdGhhdCBkYXRhLgoKYGBge3J9CiN3cml0ZSB5b3VyIHBsYW4gaGVyZQoKI3N0ZXAxOgojc3RlcDI6CiNzdGVwMzoKI3N0ZXA0Ogojc3RlcDU6CmBgYAoKCk5vdyBzdGFydCBieSBjcmVhdGluZyBhIGBteV90YXJnZXRfdXJsYCB2YXJpYWJsZSBhbmQgZG8gdGhlIGluaXRpYWwgYWNjZXNzaW5nIG9mIHRoYXQgdXJsIHdpdGggdGhlIGByZWFkX2h0bWxgIGZ1bmN0aW9uOgoKYGBge3J9CiN5b3VyIGNvZGUgY29tZXMgaGVyZQpgYGAKCgojIyBUYXNrIDg6IFlvdXIgY3VzdG9tIHdlYi1zY3JhcGVyCgpTdGFydCBidWlsZGluZyB5b3VyIG93biB3ZWJzY3JhcGVyIGhlcmU6CgpgYGB7cn0KI3lvdXIgY29kZSBjb21lcyBoZXJlCmBgYAoKCg==