ScrapyでProxyを使う方法

# Proxyの使い方

Proxyの機能は **HttpProxyMiddleware** で提供されていて、デフォルトで利用可能となっている。
`Request`オブジェクトに対し`meta`キーで指定することで利用できる。

[公式ドキュメント](https://doc.scrapy.org/en/latest/topics/downloader-middleware.html?highlight=proxy#module-scrapy.downloadermiddlewares.httpproxy)はこちら。


```python
# e.g.
url = 'http://example.com/'
meta = {'proxy': 'http://145.239.92.106:3128'}
scrapy.Request(url, meta=meta)
```



## SpiderのRequestでProxyを使う

`start_urls` に指定したURLへのアクセスにProxyを使うには `start_requests()` をOverrideすればよい。`start_requests()`のドキュメントは[こちら](https://doc.scrapy.org/en/latest/topics/spiders.html?highlight=start_requests#scrapy.spiders.Spider.start_requests)

```python
class MySpider(scrapy.Spider):
    start_requests = ['http://www.example1.com/', 'http://www.example2.com/']

    def start_requests(self):
        for url in self.start_urls:
            meta = {'proxy': 'http://145.239.92.106:3128'}
            yield scrapy.Request(url, meta=meta)
```

## CrawlSpiderのRequestでProxyを使う

`start_requests()` のOverrideはSpiderと同様にする必要がある。
しかしCrawlSpiderの場合はRuleによって得られたLinkへアクセスするRequestにもProxyを設定する必要がある。

この場合 `_build_request()` をOverrideすればよい。 CrawlSpiderのソースコードは[こちら](https://github.com/scrapy/scrapy/blob/master/scrapy/spiders/crawl.py)

```python
class MySpider(CrawlSpider):
    def _build_request(self, rule, link):
        # CrawlSpider の実装
        #r = Request(url=link.url, callback=self._response_downloaded)
        #r.meta.update(rule=rule, link_text=link.text)
        #return r
        r = super(MySpider, self)._build_request(rule, link)
        r.meta.update(proxy='http://175.139.252.193:80')
        return r
```

## 複数のProxyを設定して利用する

Proxyを複数用意してランダムで利用する。Proxyは外部ファイルで設定しておくと利用しやすい。

```yaml:proxies.yml
---
- http://190.145.80.114:3130
- http://148.217.94.54:3128
- http://125.212.207.121:3128
```

```python
class MySpider(scrapy.Spider):
    proxies = None

    # proxies.yml からランダムに選ぶ
    def get_proxy(self):
        if not self.proxies:
            f = open('proxies.yml')
            self.proxies = yaml.load(f)
            f.close()
        return self.proxies[int(random() * len(self.proxies))]

    def start_requests(self):
        for url in self.start_urls:
            meta = {'proxy': self.get_proxy()}
            yield scrapy.Request(url, meta=meta)    
```

## Proxyのエラーに対処する

Proxyが動いていなかったり、アクセスが拒否された時には別のProxyを設定したい。こういう場合には `errback` にcallbackメソッドを指定する。

```python
class MySpider(scrapy.Spider):
    start_requests = ['http://www.example1.com/', 'http://www.example2.com/']
    proxies = None

    def get_proxy(self):
        if not self.proxies:
            f = open('proxies.yml')
            self.proxies = yaml.load(f)
            f.close()
        return self.proxies[int(random() * len(self.proxies))]

    def start_requests(self):
        for url in self.start_urls:
            meta = {'proxy': self.get_proxy()}
            yield scrapy.Request(
                url,
                meta=meta,
                callback=self.parse_page,
                errback=self.new_request
            )

    def new_request(self, failure):
        meta = {'proxy': self.get_proxy()}
        yield scrapy.Request(
            url=failure.request.url,
            meta=meta,
            callback=self.parse_page,
            errback=self.new_request
        )
```

# Tips 利用可能なProxyのリストを作る

## ワンライナーでproxyをチェックする

フリーのプロキシーサーバーは[こういったサイト](https://www.proxydocker.com/ja/)にまとめられている。必要に応じてピックアップし一覧にする。
例えばこのようなもの。

```bash
$ cat proxy_list
192.241.157.239:8080
125.212.207.121:3128
145.239.92.106:3128
185.93.3.123:8080
175.139.252.193:80
```

簡単なワンライナーでチェックして利用の可否を判定する。

```bash
$ cat proxy_list |\
  xargs -I@ bash -c "URL=@; curl --proxy http://@ http://example.com 1>/dev/null 2>&1 && echo success \$URL || echo failure \$URL"
```

`success` となったProxyを利用する。