如何在Python中通过Tor进行urllib2请求?

55

我正在尝试使用 Python 编写的爬虫程序来爬取网站。我想将 Tor 集成到 Python 中,也就是说,我想使用 Tor 匿名爬网站。

我尝试了这个方法,但好像并没有起作用。我检查了一下我的 IP 地址,发现它仍然和以前使用 Tor 之前的一样。我是通过 Python 来检查的。

import urllib2
proxy_handler = urllib2.ProxyHandler({"tcp":"http://127.0.0.1:9050"})
opener = urllib2.build_opener(proxy_handler)
urllib2.install_opener(opener)

5
只是想让你知道,whatismyipaddress网站的服务条款:未经事先书面许可,您不得使用脚本、代理、应用程序或以其他自动化方式查询该网站。 - LiraNuna
5
使用urllib和Tor的方法 - LiraNuna
6
考虑到另一个帖子上没有被广泛接受或特别有用的答案,我认为保持当前这个帖子的开放是有价值的,因此我投赞成票。 - jrista
1
不完全是重复,我认为 - 那是一个更一般的问题,这个是在请求帮助特定的代码片段。 - Vinay Sajip
你可以检查一下这个端口,它对我很有帮助。https://dev59.com/I2kw5IYBdhLWcg3wbqOZ - torayeff
12个回答

23
你正在尝试连接到一个SOCKS端口,但是Tor会拒绝除了SOCKS流量以外的任何流量。你可以通过一个中间人Privoxy使用8118端口进行连接。
例如:
proxy_support = urllib2.ProxyHandler({"http" : "127.0.0.1:8118"})
opener = urllib2.build_opener(proxy_support) 
opener.addheaders = [('User-agent', 'Mozilla/5.0')]
print opener.open('http://www.google.com').read()

请注意,传递给ProxyHandler的属性不应包含ip:port的http前缀。


1
8118不是Tor端口,而是privoxy端口!Tor默认侦听9050端口。但由于您正在尝试通过HTTP代理进行连接,因此需要连接到8118端口,这就是privoxy所提供的代理。 - Pankaj
7
这个答案很糟糕,你应该感到羞愧。Tor的控制端口是9051,而不是9050。9050是socks端口,你可以像这样使用它:https://dev59.com/sXE95IYBdhLWcg3watQ3。 - s3v3n
我犯了一个错误,把Privoxy的端口和Tor的端口搞混了,但是无论中间人是谁,最终结果都是一样的。特别是因为大多数Tor安装都与Privoxy捆绑在一起。 - Dmitri Farkov
1
你把TorTor Bundle混淆了。Tor Bundle确实附带了VidaliaPrivoxyFirefox,但是也有一个独立的Tor版本,可以通过apt-get/yum在Linux上安装。 - s3v3n
1
啊,我的错。无论如何,我从未试图将自己塑造成Tor专家,只是建议了一个对我有效的解决方案。 - Dmitri Farkov

9
pip install PySocks

然后:

import socket
import socks
import urllib2

ipcheck_url = 'http://checkip.amazonaws.com/'

# Actual IP.
print(urllib2.urlopen(ipcheck_url).read())

# Tor IP.
socks.setdefaultproxy(socks.PROXY_TYPE_SOCKS5, '127.0.0.1', 9050)
socket.socket = socks.socksocket
print(urllib2.urlopen(ipcheck_url).read())

只使用 urllib2.ProxyHandlerhttps://dev59.com/EXNA5IYBdhLWcg3wF5uO#2015649 中所示会失败,原因如下:

Tor is not an HTTP Proxy

提到了:如何在urllib2中使用SOCKS 4/5代理?

在Ubuntu 15.10、Tor 0.2.6.10和Python 2.7.10上进行了测试。


2
使用Python 3,我无法使用9050端口。我遇到了以下错误:urllib.error.URLError: <urlopen error PySocks doesn't support IPv6>。但是,如果使用9150端口,则可以正常工作。 - J0ANMM
@JoanMM 感谢您的报告。请提供您的确切操作系统、Python和Tor版本。在您的计算机上,它是否适用于Python 2? - Ciro Santilli OurBigBook.com
Mac OS X 版本 10.9.5 / Python 3.5.2 / Tor 浏览器 Mac 版本 6.0.5 - OS X (10.6+)。我没有检查 Python 2,我只使用 Python 3。 - J0ANMM

3
以下代码在Python 3.4上百分之百可用(使用此代码时需要保持TOR浏览器开启)。
此脚本通过socks5连接到TOR,从checkip.dyn.com获取IP地址,更改身份并重新发送请求以获取新的IP地址(循环10次)。
您需要安装适当的库才能使其正常工作。(享受并不要滥用)
import socks
import socket
import time
from stem.control import Controller
from stem import Signal
import requests
from bs4 import BeautifulSoup
err = 0
counter = 0
url = "checkip.dyn.com"
with Controller.from_port(port = 9151) as controller:
    try:
        controller.authenticate()
        socks.setdefaultproxy(socks.PROXY_TYPE_SOCKS5, "127.0.0.1", 9150)
        socket.socket = socks.socksocket
        while counter < 10:
            r = requests.get("http://checkip.dyn.com")
            soup = BeautifulSoup(r.content)
            print(soup.find("body").text)
            counter = counter + 1
            #wait till next identity will be available
            controller.signal(Signal.NEWNYM)
            time.sleep(controller.get_newnym_wait())
    except requests.HTTPError:
        print("Could not reach URL")
        err = err + 1
print("Used " + str(counter) + " IPs and got " + str(err) + " errors")

2

使用 Privoxy 作为 Tor 前面的 HTTP 代理对我而言可以正常工作 - 这是一个爬虫模板:


import urllib2
import httplib

from BeautifulSoup import BeautifulSoup
from time import sleep

class Scraper(object):
    def __init__(self, options, args):
        if options.proxy is None:
            options.proxy = "http://localhost:8118/"
        self._open = self._get_opener(options.proxy)

    def _get_opener(self, proxy):
        proxy_handler = urllib2.ProxyHandler({'http': proxy})
        opener = urllib2.build_opener(proxy_handler)
        return opener.open

    def get_soup(self, url):
        soup = None
        while soup is None:
            try:
                request = urllib2.Request(url)
                request.add_header('User-Agent', 'foo bar useragent')
                soup = BeautifulSoup(self._open(request))
            except (httplib.IncompleteRead, httplib.BadStatusLine,
                    urllib2.HTTPError, ValueError, urllib2.URLError), err:
                sleep(1)
        return soup

class PageType(Scraper):
    _URL_TEMPL = "http://foobar.com/baz/%s"

    def items_from_page(self, url):
        nextpage = None
        soup = self.get_soup(url)

        items = []
        for item in soup.findAll("foo"):
            items.append(item["bar"])
            nexpage = item["href"]

        return nextpage, items

    def get_items(self):
        nextpage, items = self._categories_from_page(self._START_URL % "start.html")
        while nextpage is not None:
            nextpage, newitems = self.items_from_page(self._URL_TEMPL % nextpage)
            items.extend(newitems)
        return items()

pt = PageType()
print pt.get_items()

2

以下是使用Tor代理下载文件的Python代码(请更新URL):

import urllib2

url = "http://www.disneypicture.net/data/media/17/Donald_Duck2.gif"

proxy = urllib2.ProxyHandler({'http': '127.0.0.1:8118'})
opener = urllib2.build_opener(proxy)
urllib2.install_opener(opener)

file_name = url.split('/')[-1]
u = urllib2.urlopen(url)
f = open(file_name, 'wb')
meta = u.info()
file_size = int(meta.getheaders("Content-Length")[0])
print "Downloading: %s Bytes: %s" % (file_name, file_size)

file_size_dl = 0
block_sz = 8192
while True:
    buffer = u.read(block_sz)
    if not buffer:
        break

    file_size_dl += len(buffer)
    f.write(buffer)
    status = r"%10d  [%3.2f%%]" % (file_size_dl, file_size_dl * 100. / file_size)
    status = status + chr(8)*(len(status)+1)
    print status,

f.close()

2

更新 - 最新版(v2.10.0以上)requests库支持使用socks代理,但需要额外安装requests[socks]模块。

安装 -

pip install requests requests[socks]

基本用法 -

import requests
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'}

# Make a request through the Tor connection
# IP visible through Tor
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
旧版回答 — 尽管这是一个老帖子,但我还回答了,因为似乎没有人提到requesocks库。

它基本上是requests库的一个端口。请注意,该库是一个旧版本(最后更新于2013年3月25日),可能没有与最新的requests库相同的功能。

安装 -

pip install requesocks

基本用法 -

# Assuming that Tor is up & running
import requesocks
session = requesocks.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'}
# Make a request through the Tor connection
# IP visible through Tor
print session.get("http://httpbin.org/ip").text
# Above should print an IP different than your public IP
# Following prints your normal public IP
import requests
print requests.get("http://httpbin.org/ip").text

1
你也可以使用内置的最新版本的requests,其中有一个proxy=参数,你可以传递socks5:127.0.0.1:9050 - james-see

2

以下解决方案适用于我在Python 3中的应用。修改自CiroSantilli的答案:

使用urllib(Python 3中的urllib2名称):

import socks
import socket
from urllib.request import urlopen

url = 'http://icanhazip.com/'

socks.setdefaultproxy(socks.PROXY_TYPE_SOCKS5, '127.0.0.1', 9150)
socket.socket = socks.socksocket

response = urlopen(url)
print(response.read())

使用requests库:

import socks
import socket
import requests

url = 'http://icanhazip.com/'

socks.setdefaultproxy(socks.PROXY_TYPE_SOCKS5, '127.0.0.1', 9150)
socket.socket = socks.socksocket

response = requests.get(url)
print(response.text)

使用 Selenium 和 PhantomJS:
from selenium import webdriver

url = 'http://icanhazip.com/'

service_args = [ '--proxy=localhost:9150', '--proxy-type=socks5', ]
phantomjs_path = '/your/path/to/phantomjs'

driver = webdriver.PhantomJS(
    executable_path=phantomjs_path, 
    service_args=service_args)

driver.get(url)
print(driver.page_source)
driver.close()

注意:如果您计划经常使用Tor,请考虑捐赠以支持他们的出色工作!(捐赠链接)


1

也许您遇到了一些网络连接问题?上述脚本对我有效(我替换了不同的URL-我使用了http://stackoverflow.com/ - 我按预期获取了页面:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd" >
 <html> <head>

<title>Stack Overflow</title>        
<link rel="stylesheet" href="/content/all.css?v=3856">

(etc.)


0

你可以使用torify

运行你的程序:

~$torify python your_program.py

0

我想和大家分享一下一个对我有效的解决方案(Python3,Windows10):

步骤1:开启Tor ControlPort,端口为9151

Tor服务默认运行在端口9150上,而ControlPort则运行在9151端口。当你运行netstat -an时,你应该能够看到本地地址为127.0.0.1:9150127.0.0.1:9151

[go to windows terminal]
cd ...\Tor Browser\Browser\TorBrowser\Tor
tor --service remove
tor --service install -options ControlPort 9151
netstat -an 

步骤2:Python脚本如下。

# library to launch and kill Tor process
import os
import subprocess

# library for Tor connection
import socket
import socks
import http.client
import time
import requests
from stem import Signal
from stem.control import Controller

# library for scraping
import csv
import urllib
from bs4 import BeautifulSoup
import time

def launchTor():
    # start Tor (wait 30 sec for Tor to load)
    sproc = subprocess.Popen(r'.../Tor Browser/Browser/firefox.exe')
    time.sleep(30)
    return sproc

def killTor(sproc):
    sproc.kill()

def connectTor():
    socks.setdefaultproxy(socks.PROXY_TYPE_SOCKS5, "127.0.0.1", 9150, True)
    socket.socket = socks.socksocket
    print("Connected to Tor")

def set_new_ip():
    # disable socks server and enabling again
    socks.setdefaultproxy()
    """Change IP using TOR"""
    with Controller.from_port(port=9151) as controller:
        controller.authenticate()
        socks.setdefaultproxy(socks.PROXY_TYPE_SOCKS5, "127.0.0.1", 9150, True)
        socket.socket = socks.socksocket
        controller.signal(Signal.NEWNYM)

def checkIP():
    conn = http.client.HTTPConnection("icanhazip.com")
    conn.request("GET", "/")
    time.sleep(3)
    response = conn.getresponse()
    print('current ip address :', response.read())

# Launch Tor and connect to Tor network
sproc = launchTor()
connectTor()

# list of url to scrape
url_list = [list of all the urls you want to scrape]

for url in url_list:
    # set new ip and check ip before scraping for each new url
    set_new_ip()
    # allow some time for IP address to refresh
    time.sleep(5)
    checkIP()

    '''
    [insert your scraping code here: bs4, urllib, your usual thingy]
    '''

# remember to kill process 
killTor(sproc)

上面的脚本将为您想要抓取的每个URL更新IP地址。只需确保睡眠时间足够长,以便IP更改。上次测试是昨天。希望这可以帮助您!


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接