Automated Anonymous Interactions with Websites Using Python and Tor

The debate around privacy and anonymity on the internet really grinds my gears, because tbh shouldn’t it be everyone’s basic right to make a script to vote 10,000 times from 10,000 different IPs in a poll on the website of your local newspaper? I think so. The battle against anonymity is kinda like the battle against piracy, in that thermodynamics favours the dissenters. What I mean by that is that whilst there may be many solutions to a problem like blocking The Pirate Bay, there are many MORE workarounds, so, although a certain system may be working now, there are inevitably many higher-entropy states. In a scenario where you’re trying to block, or even discern, a path between two entities, somehow there’ll be a way. And people are smart. Bless ’em. Leaky buckets.

ANYWAY, let’s bring things right back down and set out how you can do stuff on websites en-masse. Anonymity is only relevant because most sites are clever enough to recognise that when the same identity tries to do the same thing a few times, something a bit dodge may be afoot. To mitigate that, we’ll be harnessing the power of Tor. If you don’t know what Tor is, I hope you’re having a pleasant retirement.

A few swift disclaimers:

  • Don’t do anything bad. It’s not my fault if you do.
  • Whilst everything below seems to maintain anonymity, it’s not my fault if you end up living in the Ecuadorian embassy.

Python, wonderful, wonderful language that it is, has libraries that both interact with Tor, and interact with web pages. With a little jiggerypokery, you can get these to talk to each other and do amusing things.

Prerequisites

  1. Install the Tor browser bundle. We’re not actually going to use Tor browser for this shizz, but it’s the easiest way to get a fully configured Tor setup, and to start that service up.
  2. Install the Python splinter library. This is what we’ll use to control a browser window in code: send it to websites, click on things, fill in forms, and anything else we fancy.
    sudo pip install splinter
  3. Install the Python stem module. We need this to talk to Tor.
     sudo pip install stem
  4. Install the Firefox web driver, which is included in the Selenium installation. We need this because Firefox plays most nicely with the rest of the setup, and I’m too lazy and/or inept to figure out how to get everything working nicely with my preferred browser.
     sudo pip install selenium

Now we’re laughing. HAHAHA.

Connecting to Tor

Open the Tor Browser, allow it to go through it’s little initialisation, then once you get to the green screen congratulating you on being connected, minimise but don’t close that window. Having the browser open means that a Tor connection is running on your computer, and, whilst we aren’t going to use the Tor Browser window we just opened because we can’t control it automatically through Python, we are going to funnel our traffic through said Tor connection.

What we now do, is instantiate a Splinter browser, and tell it to use the Tor instance running locally on port 9150 as a proxy for SSL, socks and ftp…

import stem.process
from stem import Signal
from stem.control import Controller
from splinter import Browser

proxyIP = "127.0.0.1"
proxyPort = 9150

proxy_settings = {"network.proxy.type":1,
    "network.proxy.ssl": proxyIP,
    "network.proxy.ssl_port": proxyPort,
    "network.proxy.socks": proxyIP,
    "network.proxy.socks_port": proxyPort,
    "network.proxy.socks_remote_dns": True,
    "network.proxy.ftp": proxyIP,
    "network.proxy.ftp_port": proxyPort
}
browser = Browser('firefox', profile_preferences=proxy_settings)
browser.visit("http://www.icanhazip.com")

If you run this and it’s working, a Firefox browser will open itself, go to www.icanhazip.com (which, for the rain men among you, is a website which tells you the IP you’re using to access it), and receive an IP other than your actual one.

If you get some error related to loading “the profile” or similar, check you have the most recent version of selenium via

sudo pip install --upgrade selenium

If you get some error about the proxy server not accepting connections, make sure you have Tor Browser open. (I did mention that earlier, if you recall.)

NB: you have to include the “http://” in the website url.

Getting a New IP

Given that this is potentially something that we’ll want to do often in…. whatever it is that we’re doing, it makes sense to make it into a cheeky function:

def switchIP():
    with Controller.from_port(port=9151) as controller:
        controller.authenticate()
        controller.signal(Signal.NEWNYM)

The “NEWNYM” signal is apparently what you send to your Tor connection when you want a new identity. Who knew?

To test that this is working, we can request a new IP 10 times, and after each request visit www.icanhazip.com to verify that we are indeed coming via a different exit node…

for x in range(10):
    browser.visit("http://www.icanhazip.com")
    switchIP()
    time.sleep(5)

Greatness beckons.

Interacting With Websites

Ah yes. Doing stuff. This is where Splinter delivers.

What this code looks like depends very much on the website you want to do stuff on, and what you want to do. For this example I’m going to a bit of commenting, but if that doesn’t cover whatever you want to do, then Splinter is extremely well-documented so just zip over there and look it up.

Purely by way of an example, let’s head over to dogdogfish.com and register our opinion once or twice…

def interactWithSite(browser, deduplication):
    browser.visit("http://dogdogfish.com/python-2/generating-b2b-sales-data-in-python/")
    browser.fill("comment", "But the thing is... Why would anyone ever want to do this? I must have thought that "+str(deduplication)+" times...")
    browser.fill("author", "Pebblor El Munchy")
    browser.fill("email", "barack@tehwhitehouz.gov")
    browser.fill("url", "https://upload.wikimedia.org/wikipedia/en/1/16/Drevil_million_dollars.jpg")
    button = browser.find_by_name("submit")
    button.click()

For finding the names of textboxes, buttons, etc, just do a cheeky “inspect element” on the page:

Screen Shot 2016-04-19 at 20.53.43
Screen Shot 2016-04-19 at 20.25.19

Then finally you can draw all of this together with the following few lines:

for x in range(1000):
    interactWithSite(browser, x)
    switchIP()
    time.sleep(5)

Internet, I am thy master.

4 thoughts on “Automated Anonymous Interactions with Websites Using Python and Tor”

  1. Great article.
    Would be great if you could outline how to do the same thing using Chrome…having some issues getting this working.

  2. Did the following code

    import stem.process
    from stem import Signal
    from stem.control import Controller
    from splinter import Browser

    proxyIP = “127.0.0.1”
    proxyPort = 9150

    proxy_settings = {“network.proxy.type”: 1,
    “network.proxy.ssl”: proxyIP,
    “network.proxy.ssl_port”: proxyPort,
    “network.proxy.socks”: proxyIP,
    “network.proxy.socks_port”: proxyPort,
    “network.proxy.socks_remote_dns”: True,
    “network.proxy.ftp”: proxyIP,
    “network.proxy.ftp_port”: proxyPort
    }
    browser = Browser(‘firefox’, profile_preferences=proxy_settings)
    browser.visit(“http://www.icanhazip.com”)

    Didn’t work. Just get this error

    Traceback (most recent call last):
    File “C:\Users\User\AppData\Local\Programs\Python\Python35-32\lib\site-packages\selenium\webdriver\common\service.py”, line 74, in start
    stdout=self.log_file, stderr=self.log_file)
    File “C:\Users\User\AppData\Local\Programs\Python\Python35-32\lib\subprocess.py”, line 947, in __init__
    restore_signals, start_new_session)
    File “C:\Users\User\AppData\Local\Programs\Python\Python35-32\lib\subprocess.py”, line 1224, in _execute_child
    startupinfo)
    FileNotFoundError: [WinError 2] The system cannot find the file specified

    During handling of the above exception, another exception occurred:

    Traceback (most recent call last):
    File “C:/Users/User/PycharmProjects/LittleBot/Main.py”, line 15, in
    browser = Browser(‘firefox’, profile_preferences=proxy_settings)
    File “C:\Users\User\AppData\Local\Programs\Python\Python35-32\lib\site-packages\splinter\browser.py”, line 63, in Browser
    return driver(*args, **kwargs)
    File “C:\Users\User\AppData\Local\Programs\Python\Python35-32\lib\site-packages\splinter\driver\webdriver\firefox.py”, line 48, in __init__
    timeout=timeout)
    File “C:\Users\User\AppData\Local\Programs\Python\Python35-32\lib\site-packages\selenium\webdriver\firefox\webdriver.py”, line 140, in __init__
    self.service.start()
    File “C:\Users\User\AppData\Local\Programs\Python\Python35-32\lib\site-packages\selenium\webdriver\common\service.py”, line 81, in start
    os.path.basename(self.path), self.start_error_message)
    selenium.common.exceptions.WebDriverException: Message: ‘geckodriver’ executable needs to be in PATH.

    Exception ignored in: <bound method Service.__del__ of >
    Traceback (most recent call last):
    File “C:\Users\User\AppData\Local\Programs\Python\Python35-32\lib\site-packages\selenium\webdriver\common\service.py”, line 173, in __del__
    self.stop()
    File “C:\Users\User\AppData\Local\Programs\Python\Python35-32\lib\site-packages\selenium\webdriver\common\service.py”, line 145, in stop
    if self.process is None:
    AttributeError: ‘Service’ object has no attribute ‘process’

    Process finished with exit code 1

    I installed all the libraries, and even did that –upgrade selenium thing.
    Is there any way to solve this?

  3. Hey, is there anyway you can update this tutorial? It’s exactly what I need but the new version of selenium requires the geckodriver in order to work… I tried downgrading to an older version of selenium but it still wasn’t working.

  4. something similar endless while loop wit tor…

    from tbselenium.tbdriver import TorBrowserDriver
    from pyvirtualdisplay import Display
    import time

    while True:
    try:
    display = Display(visible=0, size=(800, 600))
    display.start()
    with TorBrowserDriver(“/path/to/tor/”) as driver:
    driver.get(‘https://whatsmyip.org/’)
    time.sleep(20)
    display.stop()

    pass
    except Exception as e:
    raise
    else:
    pass
    finally:
    pass

Comments are closed.