Scrapy 与 Privoxy 和 Tor:如何更新 IP

Posted

技术标签:

【中文标题】Scrapy 与 Privoxy 和 Tor:如何更新 IP【英文标题】:Scrapy with Privoxy and Tor: how to renew IP 【发布时间】:2017-12-14 01:31:42 【问题描述】:

我正在处理 Scrapy、Privoxy 和 Tor。我已经全部安装并正常工作。但是 Tor 每次都连接同一个 IP,所以我很容易被封禁。是否可以告诉 Tor 每 X 秒或每连接重新连接一次?

谢谢!

编辑配置: 对于用户代理池,我这样做了:http://tangww.com/2013/06/UsingRandomAgent/(我必须像 cmets 中所说的那样放置一个 _ init _.py 文件),对于 Privoxy 和 Tor,我遵循了 @987654322 @(我必须使用终端手动创建私人用户和私人组)。成功了:)

我的蜘蛛是这样的:

from scrapy.contrib.spiders import CrawlSpider
from scrapy.selector import Selector
from scrapy.http import Request

class YourCrawler(CrawlSpider):
    name = "spider_name"
    start_urls = [
    'https://example.com/listviews/titles.php',
    ]
    allowed_domains = ["example.com"]

    def parse(self, response):
        # go to the urls in the list
        s = Selector(response)
        page_list_urls = s.xpath('///*[@id="tab7"]/article/header/h2/a/@href').extract()
        for url in page_list_urls:
            yield Request(response.urljoin(url), callback=self.parse_following_urls, dont_filter=True)

        # Return back and go to bext page in div#paginat ul li.next a::attr(href) and begin again
        next_page = response.css('ul.pagin li.presente ~ li a::attr(href)').extract_first()
        if next_page is not None:
            next_page = response.urljoin(next_page)
            yield Request(next_page, callback=self.parse)

    # For the urls in the list, go inside, and in div#main, take the div.ficha > div.caracteristicas > ul > li
    def parse_following_urls(self, response):
        #Parsing rules go here
        for each_book in response.css('main#main'):
            yield 
                'editor': each_book.css('header.datos1 > ul > li > h5 > a::text').extract(),
            

在 settings.py 我有一个用户代理轮换和 privoxy:

DOWNLOADER_MIDDLEWARES = 
        #user agent
        'scrapy.contrib.downloadermiddleware.useragent.UserAgentMiddleware' : None,
        'spider_name.comm.rotate_useragent.RotateUserAgentMiddleware' :400,
        #privoxy
        'scrapy.downloadermiddlewares.httpproxy.HttpProxyMiddleware': 110,
        'spider_name.middlewares.ProxyMiddleware': 100
    

在 middlewares.py 我添加了:

class ProxyMiddleware(object):
    def process_request(self, request, spider):
        request.meta['proxy'] = 'http://127.0.0.1:8118'
        spider.log('Proxy : %s' % request.meta['proxy'])

我认为仅此而已……

编辑二 ---

好的,我更改了我的 middlewares.py 文件,正如博客中@Tomáš Linhart 所说:

class ProxyMiddleware(object):
    def process_request(self, request, spider):
        request.meta['proxy'] = 'http://127.0.0.1:8118'
        spider.log('Proxy : %s' % request.meta['proxy'])

from stem import Signal
from stem.control import Controller

class ProxyMiddleware(object):
    def process_request(self, request, spider):
        request.meta['proxy'] = 'http://127.0.0.1:8118'
        spider.log('Proxy : %s' % request.meta['proxy'])

    def set_new_ip():
        with Controller.from_port(port=9051) as controller:
            controller.authenticate(password='tor_password')
            controller.signal(Signal.NEWNYM)

但是现在真的很慢,而且似乎没有改变ip...我做得很好还是有什么问题?

【问题讨论】:

github上的这个项目解释了如何废弃匿名github.com/WiliTest/… 【参考方案1】:

这个blog post 可能会对您有所帮助,因为它处理了同样的问题。

编辑:根据具体要求(每个请求的新 IP 或 N 个请求之后),在中间件的 process_request 方法中适当调用 set_new_ip .但是请注意,对 set_new_ip 函数的调用不必总是确保新 IP(有一个指向常见问题解答的链接和解释)。

EDIT2:具有ProxyMiddleware 类的模块如下所示:

from stem import Signal
from stem.control import Controller

def _set_new_ip():
    with Controller.from_port(port=9051) as controller:
        controller.authenticate(password='tor_password')
        controller.signal(Signal.NEWNYM)

class ProxyMiddleware(object):
    def process_request(self, request, spider):
        _set_new_ip()
        request.meta['proxy'] = 'http://127.0.0.1:8118'
        spider.log('Proxy : %s' % request.meta['proxy'])

【讨论】:

嗯。所以我必须安装stem,然后像更改IP 地址 中使用我的蜘蛛代码导入stem 中的def set_new_ip(): 函数。它是否正确?或者def set_new_ip(): 必须放在中间件中? @Nikita 您的具体要求是什么?为每个蜘蛛运行,甚至每个请求获取新 IP? 对于每个请求或每 x 个请求。就是让无聊很难被IP封杀。 当你说«spider run»时,是指蜘蛛内部循环的每次运行,或者每次打开蜘蛛? 你如何设置 Scrapy + Privoxy + Tor 组合?【参考方案2】:

但是 Tor 每次都连接同一个 IP

那是documented Tor feature:

需要注意的重要一点是,新电路不一定 表示一个新的 IP 地址。基于启发式随机选择路径 比如速度和稳定性。大出口只有这么多 Tor 网络,因此重复使用您拥有的出口并不少见 以前。

这就是为什么使用下面的代码会导致再次重复使用相同的 IP 地址的原因。

from stem import Signal
from stem.control import Controller


with Controller.from_port(port=9051) as controller:
    controller.authenticate(password='tor_password')
    controller.signal(Signal.NEWNYM)

https://github.com/DusanMadar/TorIpChanger 帮助您管理这种行为。免责声明 - 我写了TorIpChanger

我还整理了一份关于如何将 Python 与 Tor 和 Privoxy 一起使用的指南:https://gist.github.com/DusanMadar/8d11026b7ce0bce6a67f7dd87b999f6b。

这是一个如何在 `ProxyMiddleware` 中使用 `TorIpChanger` (`pip install toripchanger`) 的示例。
from toripchanger import TorIpChanger


# A Tor IP will be reused only after 10 different IPs were used.
ip_changer = TorIpChanger(reuse_threshold=10)


class ProxyMiddleware(object):
    def process_request(self, request, spider):
        ip_changer.get_new_ip()
        request.meta['proxy'] = 'http://127.0.0.1:8118'
        spider.log('Proxy : %s' % request.meta['proxy'])

或者,如果您想在 10 个请求后使用不同的 IP,您可以执行以下操作。

from toripchanger import TorIpChanger


# A Tor IP will be reused only after 10 different IPs were used.
ip_changer = TorIpChanger(reuse_threshold=10)


class ProxyMiddleware(object):
    _requests_count = 0

    def process_request(self, request, spider):
        self._requests_count += 1
        if self._requests_count > 10:
            self._requests_count = 0 
            ip_changer.get_new_ip()

        request.meta['proxy'] = 'http://127.0.0.1:8118'
        spider.log('Proxy : %s' % request.meta['proxy'])

【讨论】:

以上是关于Scrapy 与 Privoxy 和 Tor:如何更新 IP的主要内容,如果未能解决你的问题,请参考以下文章

WindowTor(洋葱头路由)+Privoxy 网络实践(附带Java实例代码)

Win10环境下的Scrapy结合Tor进行匿名爬取

在 C# 中使用 Tor 下载文件

python3连接tor爬取暗网的一种实现方法

用privoxy来pac

Alpine Linux 的“--update add”命令说明