Scrapy与Privoxy和Tor结合使用:如何更新IP

22

我正在处理Scrapy、Privoxy和Tor。我已经将它们全部安装好并且正常工作。但是Tor每次连接时使用相同的IP地址,这样我很容易被禁止。是否有可能告诉Tor每X秒或每X个连接重新连接一次?

谢谢!

关于配置的编辑: 对于用户代理池,我做了以下操作:http://tangww.com/2013/06/UsingRandomAgent/(根据评论中的说明,我必须放置一个_init_.py文件),至于Privoxy和Tor,我遵循了http://www.andrewwatters.com/privoxy/(我需要手动在终端中创建私有用户和私有组)。 它有效 :)

我的爬虫代码如下:

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上的项目说明了如何使用Scrapy、Tor、Privoxy和UserAgent来进行匿名抓取。https://github.com/WiliTest/Anonymous-scrapping-Scrapy-Tor-Privoxy-UserAgent - J. Does
2个回答

13

但 Tor 每次连接都使用相同的 IP 地址。

这是一个Tor 的官方文档提到的特性

需要注意的一点是新的通路并不一定意味着新的 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'])

12

这篇博客文章可能有所帮助,因为它处理了同样的问题。

编辑:根据具体要求(每个请求后或N个请求后更改IP),在中间件的process_request方法中适当调用set_new_ip。请注意,set_new_ip函数的调用不必总是保证新的IP地址(FAQ中有解释链接)。

编辑2: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():必须放在中间件中? - user7499416
你是如何设置Scrapy + Privoxy + Tor组合的? - Tomáš Linhart
@Nikita编辑了答案以反映您对问题的补充。 - Tomáš Linhart
谢谢!这是为每个请求(或N个请求后)生成新IP。这是人们避免被封禁的做法吗? - user7499416
@Nikita 这是其中一种方法,其他方法在这里提到:(https://doc.scrapy.org/en/latest/topics/practices.html#avoiding-getting-banned)。 - Tomáš Linhart
显示剩余16条评论

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