Jared Andrews

Woah: A Journey Into Clickhole's Interdimensional Cable With Selenium

I absolutely love ClickHole and back in December they posted an article, "Whoa: 1 Out Of 1,000 People Who Click On This Will Get Straight-Up Porn".

I, of course, clicked on over and after assuring ClickHole I was of the appropriate age, clicked the play button on an embedded video to see if I would be a "lucky one out of a thousand viewers to see bona fide porn".

'Willing to view sexually explicit material'

I was not one of these lucky viewers, instead I was treated to a video that prompted me to "think of a landmass".

I refreshed the page a few more times and saw equally bizarre videos but no "bona fide porn". The thing that was most curious to me though was that I didn't see any duplicate videos as I refreshed and I probably refreshed 20 times. This ClickHole article reminded me of Interdimensional Cable.

Did ClickHole actually make or find 1,000 videos to put in this article? Was there actually a porno hidden somewhere in there? This article was recently reposted on Facebook and I had to find out.

With a little source analysis and Selenium I was able to get screenshots of each of the videos. Before we get into the details here is the gallery of screenshots of all the videos:

'4786' '4787' '4788' '4789' '4790' '4791' '4792' '4793' '4794' '4795' '4796' '4797' '4798' '4799' '4800' '4801' '4802' '4803' '4804' '4805' '4806' '4807' '4808' '4809' '4810' '4811' '4812' '4813' '4814' '4815' '4826' '4827' '4828' '4829' '4830' '4831' '4832' '4833' '4834' '4835' '4836' '4837' '4838' '4839' '4840' '4841' '4842' '4843' '4844' '4845' '4851' '4852' '4853' '4854' '4855' '4856' '4858' '4859' '4860' '4861' '4862' '4863' '4864' '4865' '4866' '4867' '4868' '4869' '4870' '4871' '4872' '4873' '4874' '4875' '4876' '4877' '4878' '4879' '4880' '4881' '4882' '4883' '4884' '4885' '4886' '4887' '4888' '4889' '4890' '4891' '4892' '4893'

See that last one... bon fide porn. Per usual, ClickHole did not let it's readers down!

Into the Source of the Hole

But lets take a look behind the curtain. Examining the source of the article I found this chunk of JSON:

[{"url": "//www.onionstudios.com/embed?id=4838", "weight": 11}, {"url": "//www.onionstudios.com/embed?id=4839", "weight": 11}, {"url": "//www.onionstudios.com/embed?id=4840", "weight": 11}, {"url": "//www.onionstudios.com/embed?id=4841", "weight": 11}, {"url": "//www.onionstudios.com/embed?id=4842", "weight": 11}, {"url": "//www.onionstudios.com/embed?id=4843", "weight": 11}, {"url": "//www.onionstudios.com/embed?id=4844", "weight": 11}, {"url": "//www.onionstudios.com/embed?id=4788", "weight": 11}, {"url": "//www.onionstudios.com/embed?id=4789", "weight": 11}, {"url": "//www.onionstudios.com/embed?id=4790", "weight": 11}, {"url": "//www.onionstudios.com/embed?id=4791", "weight": 11}, {"url": "//www.onionstudios.com/embed?id=4798", "weight": 11}, {"url": "//www.onionstudios.com/embed?id=4807", "weight": 11}, {"url": "//www.onionstudios.com/embed?id=4808", "weight": 11}, {"url": "//www.onionstudios.com/embed?id=4809", "weight": 11}, {"url": "//www.onionstudios.com/embed?id=4795", "weight": 11}, {"url": "//www.onionstudios.com/embed?id=4796", "weight": 11}, {"url": "//www.onionstudios.com/embed?id=4797", "weight": 11}, {"url": "//www.onionstudios.com/embed?id=4815", "weight": 11}, {"url": "//www.onionstudios.com/embed?id=4827", "weight": 11}, {"url": "//www.onionstudios.com/embed?id=4828", "weight": 11}, {"url": "//www.onionstudios.com/embed?id=4829", "weight": 11}, {"url": "//www.onionstudios.com/embed?id=4830", "weight": 11}, {"url": "//www.onionstudios.com/embed?id=4799", "weight": 11}, {"url": "//www.onionstudios.com/embed?id=4800", "weight": 11}, {"url": "//www.onionstudios.com/embed?id=4801", "weight": 11}, {"url": "//www.onionstudios.com/embed?id=4802", "weight": 11}, {"url": "//www.onionstudios.com/embed?id=4854", "weight": 11}, {"url": "//www.onionstudios.com/embed?id=4845", "weight": 11}, {"url": "//www.onionstudios.com/embed?id=4852", "weight": 11}, {"url": "//www.onionstudios.com/embed?id=4853", "weight": 11}, {"url": "//www.onionstudios.com/embed?id=4787", "weight": 11}, {"url": "//www.onionstudios.com/embed?id=4855", "weight": 11}, {"url": "//www.onionstudios.com/embed?id=4856", "weight": 11}, {"url": "//www.onionstudios.com/embed?id=4858", "weight": 11}, {"url": "//www.onionstudios.com/embed?id=4859", "weight": 11}, {"url": "//www.onionstudios.com/embed?id=4860", "weight": 11}, {"url": "//www.onionstudios.com/embed?id=4861", "weight": 11}, {"url": "//www.onionstudios.com/embed?id=4862", "weight": 11}, {"url": "//www.onionstudios.com/embed?id=4863", "weight": 11}, {"url": "//www.onionstudios.com/embed?id=4864", "weight": 11}, {"url": "//www.onionstudios.com/embed?id=4865", "weight": 11}, {"url": "//www.onionstudios.com/embed?id=4866", "weight": 11}, {"url": "//www.onionstudios.com/embed?id=4867", "weight": 11}, {"url": "//www.onionstudios.com/embed?id=4868", "weight": 11}, {"url": "//www.onionstudios.com/embed?id=4869", "weight": 11}, {"url": "//www.onionstudios.com/embed?id=4870", "weight": 11}, {"url": "//www.onionstudios.com/embed?id=4871", "weight": 11}, {"url": "//www.onionstudios.com/embed?id=4872", "weight": 11}, {"url": "//www.onionstudios.com/embed?id=4873", "weight": 11}, {"url": "//www.onionstudios.com/embed?id=4811", "weight": 11}, {"url": "//www.onionstudios.com/embed?id=4812", "weight": 11}, {"url": "//www.onionstudios.com/embed?id=4813", "weight": 11}, {"url": "//www.onionstudios.com/embed?id=4814", "weight": 11}, {"url": "//www.onionstudios.com/embed?id=4792", "weight": 11}, {"url": "//www.onionstudios.com/embed?id=4806", "weight": 11}, {"url": "//www.onionstudios.com/embed?id=4826", "weight": 11}, {"url": "//www.onionstudios.com/embed?id=4851", "weight": 11}, {"url": "//www.onionstudios.com/embed?id=4803", "weight": 11}, {"url": "//www.onionstudios.com/embed?id=4804", "weight": 11}, {"url": "//www.onionstudios.com/embed?id=4805", "weight": 11}, {"url": "//www.onionstudios.com/embed?id=4831", "weight": 11}, {"url": "//www.onionstudios.com/embed?id=4832", "weight": 11}, {"url": "//www.onionstudios.com/embed?id=4833", "weight": 11}, {"url": "//www.onionstudios.com/embed?id=4834", "weight": 11}, {"url": "//www.onionstudios.com/embed?id=4835", "weight": 11}, {"url": "//www.onionstudios.com/embed?id=4793", "weight": 11}, {"url": "//www.onionstudios.com/embed?id=4786", "weight": 11}, {"url": "//www.onionstudios.com/embed?id=4794", "weight": 11}, {"url": "//www.onionstudios.com/embed?id=4836", "weight": 11}, {"url": "//www.onionstudios.com/embed?id=4810", "weight": 11}, {"url": "//www.onionstudios.com/embed?id=4837", "weight": 11}, {"url": "//www.onionstudios.com/embed?id=4874", "weight": 11}, {"url": "//www.onionstudios.com/embed?id=4875", "weight": 11}, {"url": "//www.onionstudios.com/embed?id=4876", "weight": 11}, {"url": "//www.onionstudios.com/embed?id=4877", "weight": 11}, {"url": "//www.onionstudios.com/embed?id=4878", "weight": 11}, {"url": "//www.onionstudios.com/embed?id=4879", "weight": 11}, {"url": "//www.onionstudios.com/embed?id=4880", "weight": 11}, {"url": "//www.onionstudios.com/embed?id=4881", "weight": 11}, {"url": "//www.onionstudios.com/embed?id=4882", "weight": 11}, {"url": "//www.onionstudios.com/embed?id=4883", "weight": 11}, {"url": "//www.onionstudios.com/embed?id=4884", "weight": 11}, {"url": "//www.onionstudios.com/embed?id=4885", "weight": 11}, {"url": "//www.onionstudios.com/embed?id=4886", "weight": 11}, {"url": "//www.onionstudios.com/embed?id=4887", "weight": 11}, {"url": "//www.onionstudios.com/embed?id=4888", "weight": 11}, {"url": "//www.onionstudios.com/embed?id=4889", "weight": 11}, {"url": "//www.onionstudios.com/embed?id=4890", "weight": 11}, {"url": "//www.onionstudios.com/embed?id=4891", "weight": 11}, {"url": "//www.onionstudios.com/embed?id=4892", "weight": 11}, {"url": "//www.onionstudios.com/embed?id=4893", "weight": 1}

A list containing pairs of urls and weights. There are 92 items with a weight of "11" and 1 item with a weight of "1". Impressive. I didn't dig into but presumably there is another piece of Javascript in the page which selects a video taking into account the weights of each item. It seems that ClickHole kept it's promise, you have a roughly 1 in a 1000 chance of seeing porn when viewing this article and given that there are 93 videos you aren't likely to see any duplicates while refreshing.

After seeing the final item with the weight of 1 I assumed I had found the bona fide porn and sure enough I was right. But given how bizarre the other videos I saw, I wondered... what else is in here? I decided to write a Selenium script to find out.

Scraping Each Video With Selenium

Using Mac OS 10.12.12, Python 2.7.11, Selenium 3.0.2 and Geckodriver 0.14.0 I ended out with the following sloppy code:

import time
import os
from selenium import webdriver

# adapted from http://stackoverflow.com/questions/21485300/how-to-explicitly-write-a-selenium-webdriver-screenshot-to-a-directory-in-python    
def save_screenshot(driver, directory, file_name_prefix):
    img_str64 = driver.get_screenshot_as_base64()
    f = open("%s/%s.png" % (directory, file_name_prefix), "wb")
    f.write(img_str64.decode("base64"))
    f.close()

driver = webdriver.Firefox(webdriver.FirefoxProfile("./path/to/firefox_profile"))

videos = [ ... list of video urls ... ]

# I found that taking the screenshot right away sometimes was too
# soon, waiting 6 seconds seemed to be the sweet spot
wait_time_to_screenshot = 6
# While figuring out that sweet spot I generated folder names like
# this so I didn't have to overwrite old screenshots every time I ran the code
directory = "screenshots_%i_%s" % (time.time(), wait_time_to_screenshot)

if not os.path.exists(directory):
    os.makedirs(directory)

for video in videos:
    print "Loading %s" % video

    label = video.split("=")[1]

    driver.get("http://%s" % video)
    # Click the screenshot
    driver.find_element_by_xpath("//div[@class='bulbs-video-poster-overlay']").click()

    time.sleep(wait_time_to_screenshot)

    save_screenshot(driver, directory, label)

Notes on Making Selenium Work

The reason I am even sharing this code is because I had a few problems getting Selenium and Firefox to do what I want. I was surprised at the lack of documentation and figured I would note what I had to do here.

While working on this I installed the WebDriver Element Locator Firefox Add-on which I used to generate the xpath for clicking the play button on each video. This was overkill but I wanted to try out the plugin and I give it a thumbs up!

Geckodriver

If you ran the above code without having Geckodriver on your path you would see that Firefox opens but nothing happens... a curious issue. To fix this you need to download the Geckodriver executable and then put it on your path. After doing this things will work as expected.

Turning on Flash

The Clickhole videos are in a Flash player. For quite a while now Flash has been disabled by default and must be turned on by clicking "allow" in a Firefox pop-up. Selenium is unable to click these pop-ups.

I immediately decided to make a new Firefox profile. To create a new Firefox profile run /Applications/Firefox.app/Contents/MacOS/firefox-bin -P.

To create a driver using a specific Firefox profile you can do the following:

driver = webdriver.Firefox(webdriver.FirefoxProfile("./path/to/firefox_profile"))

I figured there would be an option in about:config to turn off the Flash blocking but after messing around with various configuration keys that contain the word 'flash' I couldn't get it to go away. Searching around on Google didn't provide any clear answers either.

What I ended out doing was starting a Firefox instance using my new profile and then navigating to Clickhole video pressing 'allow and remember'. This preference seemed to save to the profile though I have no idea what preferences it changed. I'm sure I could have found out by diffing a clean profile against my changed profile but I stopped caring since I got what I needed.

There is an option in the "Choose User Profile" menu that says: "use the selected profile without asking at startup", make sure you uncheck this before starting up selenium profile.

After all this I was able to run my Selenium script and finally, I got to see some bona fide porn. Wow!