116

I want to make multiple GET requests using Tor to a webpage. I want to use a different ipaddress for each request.

import socks
import socket
socks.set_default_proxy(socks.SOCKS5, "127.0.0.1", 9150)
socket.socket = socks.socksocket
import requests
print (requests.get('http://icanhazip.com')).content

Using this, I made one request. How can I change the ipaddress to make another?

Sachin Kelkar
  • 1,282
  • 2
  • 11
  • 16
  • Isn't that what tor is supposed to do, i.e. obfuscate or otherwise make it difficult to determine the sender's IP address? – mhawke May 17 '15 at 11:19
  • 3
    Use stem https://stem.torproject.org/ – Padraic Cunningham May 17 '15 at 11:31
  • @mhawke it does, but once the connection handshake is made it is persistent in that set of ip address hops that it connects through, thus why the op would want to possibly change the ip address again. – james-see Nov 15 '16 at 17:04

9 Answers9

159

There are 2 aspects to your question -

  1. Making requests using Tor
  2. Renewing the connection as per requirement (in your case, after every request)

Part 1

The first one is easy to do with the latest (upwards of v2.10.0) requests library with an additional requirement of requests[socks] for using the socks proxy.

Installation -

pip install requests[socks]

Basic usage -

import requests

def get_tor_session():
    session = requests.session()
    # Tor uses the 9050 port as the default socks port
    session.proxies = {'http':  'socks5://127.0.0.1:9050',
                       'https': 'socks5://127.0.0.1:9050'}
    return session

# Make a request through the Tor connection
# IP visible through Tor
session = get_tor_session()
print(session.get("http://httpbin.org/ip").text)
# Above should print an IP different than your public IP

# Following prints your normal public IP
print(requests.get("http://httpbin.org/ip").text)

Part 2

To renew the Tor IP, i.e. to have a fresh visible exit IP, you need to be able to connect to the Tor service through it's ControlPort and then send a NEWNYM signal.

Normal Tor installation does not enable the ControlPort by default. You'll have to edit your torrc file and uncomment the corresponding lines.

ControlPort 9051
## If you enable the controlport, be sure to enable one of these
## authentication methods, to prevent attackers from accessing it.
HashedControlPassword 16:05834BCEDD478D1060F1D7E2CE98E9C13075E8D3061D702F63BCD674DE

Please note that the HashedControlPassword above is for the password "password". If you want to set a different password, replace the HashedControlPassword in the torrc by noting the output from tor --hash-password "<new_password>" where <new_password> is the password that you want to set.

................................................................................

Warning for Windows users: see post here.

There is an issue on windows where the setting for the controlport in the torrc file is ignored if tor was installed using the following command:

tor --service install

To resolve the issue, after editing your torrc file, type the following commands:

tor --service remove
tor --service install -options ControlPort 9051

................................................................................

Okay, so now that we have Tor configured properly, you will have to restart Tor if it is already running.

sudo service tor restart

Tor should now be up & running on the 9051 ControlPort through which we can send commands to it. I prefer to use the official stem library to control Tor.

Installation -

pip install stem

You may now renew the Tor IP by calling the following function.

Renew IP -

from stem import Signal
from stem.control import Controller

# signal TOR for a new connection 
def renew_connection():
    with Controller.from_port(port = 9051) as controller:
        controller.authenticate(password="password")
        controller.signal(Signal.NEWNYM)

To verify that Tor has a new exit IP, just rerun the code from Part 1. For some reason unknown to me, you need to create a new session object in order to use the new IP.

session = get_tor_session()
print(session.get("http://httpbin.org/ip").text)
shad0w_wa1k3r
  • 12,955
  • 8
  • 67
  • 90
  • 1
    part1 (to test it) you will need `pip install request[socks]` (if you get an error, `pip uninstall request` and try to install again) – JinSnow Oct 27 '17 at 19:55
  • 1
    @AshishNitinPatil sorry for the dumb question but in which scrapy file should we use that code? (In settings.py, the spider.py or another?) – JinSnow Oct 27 '17 at 20:10
  • 2
    @JinSnow that's more of a Scrapy question than Tor, see https://stackoverflow.com/questions/45009940/scrapy-with-privoxy-and-tor-how-to-renew-ip/45010141 for more help. – shad0w_wa1k3r Oct 28 '17 at 11:12
  • 2
    I suggest you use socks5h so that you delegate the DNS resolving to the proxy. – Christian Pao. Jan 14 '21 at 23:24
  • 1
    To renew without restarting you can add return get_tor_session() to renew_connection() and use session = renew_connection() in the main code – secf00tprint Mar 04 '21 at 08:40
  • Instead of `pip install requests[socks]` you can also just do `pip install pysocks` that also works fine. – james-see Nov 27 '21 at 17:33
  • 1
    @jamescampbell `pip install requests[socks]` installs both `requests` as well as the optional `socks` dependency (which is indeed `pysocks`), so no, you can't replace it with just `pip install pysocks`. And, it shouldn't be added separately, let requests handle the dependency, since there maybe version / compatibility issues. – shad0w_wa1k3r Dec 20 '21 at 22:55
43

Here is the code you want to use (download the stem package using pip install stem)

from stem import Signal
from stem.control import Controller

with Controller.from_port(port = 9051) as controller:
    controller.authenticate(password='your password set for tor controller port in torrc')
    print("Success!")
    controller.signal(Signal.NEWNYM)
    print("New Tor connection processed")

Good luck and hopefully that works.

james-see
  • 12,210
  • 6
  • 40
  • 47
  • I have tried this solution but get this error: error(61, 'Connection refused'). It seems port 9051 is not open on my machine, I'm on a mac and have been trying to do everything I can find to open this port but still get this error. Any ideas? – kflaw Jan 23 '17 at 14:02
  • 1
    @kflaw you need to uncomment your control port in torrc then restart tor. By default 9051 control port turned off because it is not needed for regular tor use. – james-see Jan 23 '17 at 14:05
  • thanks for the reply, I forgot to mention that I did uncomment the control port in torrc as well as the authenticate with hash password. What am I missing? – kflaw Jan 23 '17 at 14:09
  • @kflaw Can you post a link to your code in codepen or something like that so I can repeat your error? Thanks – james-see Jan 23 '17 at 14:12
  • here is a link to a gist - I've tried several code snippets I found in similar posts but all get stuck on trying to establish the control port. https://gist.github.com/kflaw/1a41654993e9692914a7fa18f8819a8f – kflaw Jan 23 '17 at 14:21
  • @kflaw based on your checks for the password, I believe that you are not setting your hashed password properly in your torrc for controlport setting HashedControlPassword. Here is a gist that will get you there. Just set the plaintext password you want to generate hash for: https://gist.github.com/jamesacampbell/2f170fc17a328a638322078f42e04cbc – james-see Jan 23 '17 at 21:16
  • thanks for the reply! I actually followed the instructions here https://linuxaria.com/howto/how-to-anonymize-the-programs-from-your-terminal-with-torify for creating the hashed password, and copied and pasted what it gave me in torrc. I think I did that correctly. Also I don't ever get to that point in the sample code I sent, i won't let me even set port 9051 as controller, it errors there. In your sample code provided above it would error on this line: with Controller.from_port(port = 9051) as controller: I think the port is not open and everything i have tried to open it so far does not work – kflaw Jan 23 '17 at 21:24
  • @kflaw are you running python 2.7x or 3x? – james-see Jan 23 '17 at 21:27
  • II'm using 2.7x – kflaw Jan 23 '17 at 21:33
  • @kflaw brew install python so you can get python3. The code works for python3. Edit: nevermind it works in 2.7 as well. I am getting errors using your example as well. I will take a look at your example but my code works if you just want to use that. – james-see Jan 23 '17 at 21:34
  • @kflaw I responded back to your gist with my version of it that works. – james-see Jan 23 '17 at 21:42
  • thanks for the code unfortunately it didn't work, i still get the error Unable to connect to port 9051 ([Errno 61] Connection refused) – kflaw Jan 24 '17 at 00:30
  • @kflaw Two ideas left: run code as sudo because permissions issue so sudo python script.py or ensure you really have uncommented the right rows in torrc and that your tor service is actually using and reading from the torrc file you are editing. – james-see Jan 24 '17 at 00:33
  • thanks for all your ideas. didn't work running as sudo, and i did double-check torrc rows I uncommented. How do I check that tor is reading that torrc file? and also, I notice it is named as torrc.sample, do I need to remove the .sample? Sorry I am totally new with this. – kflaw Jan 24 '17 at 00:40
  • @kflaw lol yes you need to remove the .sample. That is the issue. Its not reading from your torrc file but a default one. – james-see Jan 24 '17 at 00:41
  • @kflaw to do that simply mv torrc.sample torrc and dont forget to restart tor service. brew services restart tor – james-see Jan 24 '17 at 00:44
  • did it and worked thanks for much for your help! all that for something so simple – kflaw Jan 24 '17 at 00:45
  • @kflaw np. Thats always the way it is. Something simple. – james-see Jan 24 '17 at 00:46
  • I inserted this into my code and it is working to change the circuits, but now the site I am trying to hit is blocking me.. but when I change back to my code without establishing new connections at set intervals, it works.. i wonder why this is? – kflaw Jan 24 '17 at 01:32
  • @kflaw beyond scope of original question here, please post a new question and I will happily try to answer it there or ask me over on IRC / twitter DM. Thanks – james-see Jan 29 '17 at 20:27
  • I had to add "ControlPort 9051" in torrc-defaults file – Zohab Ali Sep 30 '17 at 19:34
  • How do you get the password? – Mr.Dark Apr 03 '20 at 07:52
  • @Mr.Dark it is listed above: https://linuxaria.com/howto/how-to-anonymize-the-programs-from-your-terminal-with-torify tells you how to do that, you update hashed password in torrc file along with the ControlPort line. – james-see Apr 03 '20 at 13:58
  • Isint there anyway to have tor in python without installing tor in the pc? Am a windows user? Should this be another question? – Mr.Dark Apr 03 '20 at 14:01
  • @Mr.Dark You can ask as a new question sure. The community will definitely help with that. In the question description provide as much detail as possible on what you are actually trying to do. – james-see Apr 03 '20 at 15:32
35

You can try pure-python tor protocol implementation Torpy. No need original Tor client or Stem dependency at all.

$ pip3 install torpy[requests]
...

$ python3.7
>>> from torpy.http.requests import TorRequests
>>> with TorRequests() as tor_requests:
...    print("build circuit")
...    with tor_requests.get_session() as sess:
...        print(sess.get("http://httpbin.org/ip").json())
...        print(sess.get("http://httpbin.org/ip").json())
...    print("renew circuit")
...    with tor_requests.get_session() as sess:
...        print(sess.get("http://httpbin.org/ip").json())
...        print(sess.get("http://httpbin.org/ip").json())
...
build circuit
{'origin': '23.129.64.190, 23.129.64.190'}
{'origin': '23.129.64.190, 23.129.64.190'}
renew circuit
{'origin': '198.98.50.112, 198.98.50.112'}
{'origin': '198.98.50.112, 198.98.50.112'}

So each time when you getting new session you get new identity (basically you get new circuit with new exit node). See more examples at readme file https://github.com/torpyorg/torpy

James Brown
  • 494
  • 4
  • 4
  • Hello, please I need your help with torpy how to use socket with torpy can you help to write a script for me – iven Mar 07 '21 at 07:35
  • sess.get("http....org") works normally, but when I use with sess.get("http....onion") it give me this error:::: Exception: You must connect to guard node first – pupuupup Mar 22 '21 at 17:40
  • I could not get this to work at all without using some sort of control port or tor service. – james-see Dec 22 '21 at 00:35
  • I tried this and also got error "Exception: You must connect to guard node first" – Leo Torres Jan 20 '22 at 04:54
20

You can use torrequest library (shameless plug). It's available on PyPI.

from torrequest import TorRequest

with TorRequest() as tr:
  response = tr.get('http://ipecho.net/plain')
  print(response.text)  # not your IP address

  tr.reset_identity()

  response = tr.get('http://ipecho.net/plain')
  print(response.text)  # another IP address, not yours
Erdi Aker
  • 786
  • 7
  • 9
  • 3
    `tr.reset_identity()` does not change the IP address. Am I missing something? Do I need to change the tor config file or something? – Shivam Gaur Feb 16 '17 at 07:19
  • 5
    I know this is an old question and answer, but this one worked for me. I just had to do this: with TorRequest(proxy_port=9050, ctrl_port=9051, password='password') as tr: – alex Jun 09 '17 at 10:45
  • 2
    tr.reset_identity() doesn't work for me. But anyway, you can create many TorRequest(proxy_port=9050, ctrl_port=9051, password='password') objects, each new object will have new IP/session. – Timur Nurlygayanov Feb 25 '19 at 13:43
8

Requests supports proxies using the SOCKS protocol from version 2.10.0.

import requests
proxies = {
    'http': 'socks5://localhost:9050',
    'https': 'socks5://localhost:9050'
}
url = 'http://httpbin.org/ip'
print(requests.get(url, proxies=proxies).text)
chossenger
  • 613
  • 6
  • 15
Karimov Dmitriy
  • 140
  • 2
  • 6
  • 1
    You never set the proxy with requests. And when I set the proxy with requests 2.11, I get connection error `Failed to establish a new connection` – user193661 Aug 12 '16 at 04:02
  • 1
    Yes, I missed _proxies_ parameter. Fixed, thanks for the notice. And I updated requests to 2.11 and checked this script - it works. Did you start tor service? If I stop tor I get the same error. – Karimov Dmitriy Aug 24 '16 at 05:06
4

This answer complete the one of Ashish Nitin Patil for windows (feel free to update this answer)

Part 2

ControlPort 9051
## If you enable the controlport, be sure to enable one of these
## authentication methods, to prevent attackers from accessing it.
HashedControlPassword 16:05834BCEDD478D1060F1D7E2CE98E9C13075E8D3061D702F63BCD674DE

The HashedControlPassword above is the password. If you want to set a different password in the console navigate to \Tor Browser\Browser\TorBrowser\Tor and type the following commands: tor.exe --hash-password password_XYZ | more). It will give you something like HashedControlPassword 16:54C092A8... This is your password. Now you can add it to the torrc file (Tor Browser\Browser\TorBrowser\Data\Tor\torrc).

You will need then to restart Tor:

tor --service remove
tor --service install -options ControlPort 9051

To check if that works type netstat -an you will now see that port 9051 is open.

Notice that tor --service install -... will create Tor Win32 Service. For some reason, it seems you have to stop the service to use the browser (run services.msc)

EDIT: you will find many pieces of information here (About port number & proxy, Tor, Privoxy, Auto switch user agent...).

JinSnow
  • 1,553
  • 4
  • 27
  • 49
3

This code works fine. Using Tor, it changes the IP address after each request.

import time, socks, socket
from urllib2 import urlopen
from stem import Signal
from stem.control import Controller

nbrOfIpAddresses=3

with Controller.from_port(port = 9051) as controller:
   controller.authenticate(password = 'my_pwd')
   socks.setdefaultproxy(socks.PROXY_TYPE_SOCKS5, "127.0.0.1", 9050)
   socket.socket = socks.socksocket   

   for i in range(0, nbrOfIpAddresses):
       newIP=urlopen("http://icanhazip.com").read()
       print("NewIP Address: %s" % newIP)
       controller.signal(Signal.NEWNYM)
       if controller.is_newnym_available() == False:
        print("Waitting time for Tor to change IP: "+ str(controller.get_newnym_wait()) +" seconds")
        time.sleep(controller.get_newnym_wait())
   controller.close()
  • 1
    This is really great code, but I am confused what the `nbrOfIpAddresses=3` is doing. Is it getting a list of fixed ip addresses to begin with? – user321627 Aug 26 '18 at 23:36
  • nbrOfIpAddresses=3 is just the second argument in that range() he's looping over. It essentially means he reads the URL 3 times. – TKK Mar 04 '21 at 18:53
1

The requests in requesocks is super old, it doesn't have response.json() and many other stuff.

I would like to keep my code clean. However, requests currently doesn't have socks5 supported yet (for more detail, read this thread https://github.com/kennethreitz/requests/pull/478)

So I used Privoxy as a http proxy that connects Tor for now.

Install and configure Privoxy on Mac

brew install privoxy
vim /usr/local/etc/privoxy/config
# put this line in the config
forward-socks5 / localhost:9050 .
privoxy /usr/local/etc/privoxy/config

Install and configure Privoxy on Ubuntu

sudo apt-get install privoxy
sudo vim /etc/privoxy/config
# put this line in the config
forward-socks5 / localhost:9050 .
sudo /etc/init.d/privoxy restart

Now I can use Tor like a http proxy. Below is my python script.

import requests

proxies = {
  'http': 'http://127.0.0.1:8118',
}

print requests.get('http://httpbin.org/ip', proxies=proxies).text
Tobias
  • 1
  • 2
  • 4
  • 1
    on osx, the last line `privoxy /usr/local/etc/privoxy/config` returns this error `2016-08-06 23:47:01.761 00000048 Error: Wrong number of parameters for forward-socks5 in configuration file.` – Shoham Aug 06 '16 at 20:49
  • Requests does allow the use of socks 5 proxy. Works fine. – james-see Aug 10 '16 at 01:02
1

A good function to renew your IP. Windows example

def renew_tor_ip():
    with Controller.from_port(port = 9051) as controller:
        controller.authenticate(password="aAjkaI19!!laksjd")
        controller.signal(Signal.NEWNYM)

Example of usage

import requests
import time
from stem import Signal
from stem.control import Controller


def get_current_ip():
    session = requests.session()

    # TO Request URL with SOCKS over TOR
    session.proxies = {}
    session.proxies['http']='socks5h://localhost:9150'
    session.proxies['https']='socks5h://localhost:9150'

    try:
        r = session.get('http://httpbin.org/ip')
    except Exception as e:
        print(str(e))
    else:
        return r.text

#16:8EE7AEE3F32EEEEB605C6AA6C47B47808CA6A81FA0D76546ADC05F0F15 to aAjkaI19!!laksjd
#cmd shell "C:\Users\Arthur\Desktop\Tor Browser\Browser\TorBrowser\Tor\tor.exe" --hash-password aAjkaI19!!laksjd | more
#Torcc config
#ControlPort 9051
#HashedControlPassword 16:8EE7AEE3F32EEEEB605C6AA6C47B47808CA6A81FA0D76546ADC05F0F15

def renew_tor_ip():
    with Controller.from_port(port = 9051) as controller:
        controller.authenticate(password="aAjkaI19!!laksjd")
        controller.signal(Signal.NEWNYM)


for i in range(5):
    print(get_current_ip())
    renew_tor_ip()
    time.sleep(5)
Arthur Yakovlev
  • 8,933
  • 8
  • 32
  • 48