データナード

機械学習と自然言語処理についての備忘録 (旧ナード戦隊データマン)

スクレイピングにおいてIPのBanを防ぐ方法

自然言語処理では、しばしばコーパスを作るためにWeb上のリソースを利用します。そのためにスクレイピングをするのですが、大量のリクエストを特定のサイトに送るとBanされる可能性があります。今回はそれを防ぐ一つの方法を書きます。(悪用厳禁)

TL;DR

VPNを使おう。

概要

nordvpnのようなVPNを使えば、数十の国の数千のサーバを利用することができます。もし、これらの膨大なサーバリストを使ってスクレイピングに利用することができれば、以下の2つのメリットがあります:

  1. ランダムにIPを変え続ければブロックされる可能性が下がり、仮にブロックされても別のサーバーのIPを使えばいい。
  2. 複数のサーバのIPを利用してスクレイピングするので、並列化すれば、time.sleepの間隔を長めにしてもスクレイピングできるデータ量は多い。

実用的には、大量のサーバホストのなかからランダムに1つ選んでそこへ接続するという方法があります。

コード例

metadata.py

proxyサーバリストをmetadata.pyという名前で作成します。(名前は何でもいいです)

PROXY: proxyのURLのフォーマット。UNはユーザ名、 PSはパスワード、SVはPROXIES内に保存されているサーバプレフィクス。

PROXY = "http://{UN}:{PS}@{SV}.nordvpn.com:80"

PROXIES = [
    "md8",
    "no135",
    "es59",
    "se161"
    ...(と数千のサーバリストを書いていく)
]

requestsを使った接続

requestsモジュールを使ってプロキシに接続し、ターゲットのサイトを取得できます。

import json
import random
import requests
import metadata


def load_account(account_file):
    with open(account_file) as f:
        ac = json.load(f)
        return ac


ac = load_account("account.json")


def build_proxy(ac):
    sv = random.choice(metadata.PROXIES)
    return metadata.PROXY.format(UN=ac["user"], PS=ac["pass"], SV=sv)


def download_html(url):
    proxy = build_proxy(ac)
    r = requests.get(url, proxies={"http": proxy, "https": proxy})
    return r.text


if __name__ == "__main__":
    html = download_html(
        "https://search.yahoo.com/search?p=test&fr=yfp-t&fp=1&toggle=1&cop=mss&ei=UTF-8"
    )
    print(html)

サーバリストの見つけ方

nordvpnのサーバリストは基本的に国名のprefixとサーバ番号の組み合わせです。

サーバリストを発見するには、以下のページから探すことができます。

https://nordvpn.com/servers/tools/

あるいは、ロケーションコードが分かる場合は以下のスクリプトを使えます:

import socket
import threading
import queue

HOST = "{LOC}{NUM}.nordvpn.com"


def socket_it(url):
    try:
        socket.getaddrinfo(url, 80, proto=socket.IPPROTO_TCP)
        return url
    except Exception:
        return None


def run(q, url):
    q.put(socket_it(url))


def check(location_code, max_num=1000):
    q = queue.Queue()
    for i in range(max_num):
        u = HOST.format(LOC=location_code, NUM=str(i))
        t = threading.Thread(target=run, args=(q, u))
        t.daemon = True
        t.start()
    return q


if __name__ == "__main__":
    import sys
    q = check(sys.argv[1])
    while not q.empty():
        s = q.get(timeout=0.1)
        if s is not None:
            print(s)
$ python3 check_server.py jp

[出力]

jp201.nordvpn.com
jp202.nordvpn.com
jp204.nordvpn.com
jp203.nordvpn.com
jp208.nordvpn.com
jp209.nordvpn.com
jp218.nordvpn.com
jp219.nordvpn.com
jp220.nordvpn.com
jp221.nordvpn.com
jp222.nordvpn.com
jp225.nordvpn.com
jp234.nordvpn.com
jp235.nordvpn.com
jp233.nordvpn.com
jp236.nordvpn.com
jp227.nordvpn.com
jp223.nordvpn.com
jp226.nordvpn.com
jp224.nordvpn.com
jp232.nordvpn.com

参考

  1. How to use proxies with Python Requests module
  2. Server recommended by NordVPN | NordVPN
  3. How to set up HTTP proxy on various browsers | NordVPN