2015/09/23 中野 豊 @ 株式会社カカクコム
User-Agent: * # 全てのクローラは
Disallow: /admin # /admin以下にアクセスしないこと
Crawl-Delay: 5 # 5秒以上リクエスト間隔をあけること
User-Agent: BadBot # BadBotだけは
Disallow: / # アクセス禁止
<!-- head内で、このページの全リンクを辿らないよう指示 -->
<meta name="robots" content="nofollow"/>
<!-- 個別のaタグで、リンクをたどらないよう指示 -->
<a href="/path/to/some/page" rel="nofollow">xxx</a>
User-Agent: * または クローラのUA
Disallow: /testdir2
Crawl-Delay: 3
ROBOTSTXT_OBEY = True
from urllib2 import urlopen, Request
DEFAULT_DELAY = 0
def get_delay(spider_ua, hostname, port=80):
"""spiderのUAに対し、ホストのrobots.txtでリクエスト間隔が指定されていればその値を返す"""
robotstxt = _get_robotstxt(spider_ua, hostname, port)
ua_name = spider_ua.split('/')[0].lower()
delay_instrucstions = _parse_delay(robotstxt)
delay = delay_instrucstions.get(ua_name, delay_instrucstions.get('*', DEFAULT_DELAY))
return delay
def _get_robotstxt(ua, hostname, port):
"""与えられたホストのrobots.txtの文字列を取得する"""
url = 'http://{}:{}/robots.txt'.format(hostname, port)
try:
p = urlopen(Request(url, headers={'User-Agent': ua}))
robotstxt = p.read()
p.close()
except:
robotstxt = None
return robotstxt
def _parse_delay(robotstxt):
"""
robots.txtの内容を文字列で受け取り、User-Agentごとのリクエスト間隔指定を
{User-Agent: そのUAに対するDelay, User-Agent, そのUAに対するDelay, ...}
の形で返す
"""
delay_instructions = {}
if not robotstxt:
return delay_instructions
uas = []
for line in robotstxt.splitlines():
item = line.lower().split('#')[0].strip()
if item.startswith('user-agent:'):
uas.append(item.split(':', 1)[1].strip())
elif item.startswith('crawl-delay:'):
delay = item.split(':', 1)[1].strip()
try:
delay = int(delay)
if uas and delay >= 0:
for ua in uas:
delay_instructions[ua] = delay
except:
pass
finally:
uas = []
return delay_instructions
import re
from scrapy.core.downloader import Slot, Downloader, _get_concurrency_delay
from robotsdelay import get_delay
class CrawlDelayConsciousSlot(Slot):
"""リクエスト先ホストのrobots.txtに合わせて、ダウンロード間隔を調節するSlot"""
def __init__(self, concurrency, delay, settings):
super(CrawlDelayConsciousSlot, self).__init__(concurrency, delay, settings)
self.default_delay = delay
self.crawl_delay_cache = {}
self.ua = settings.get('USER_AGENT')
self.url_pattern = re.compile('^http[s]{0,1}://([^:/]+)[:]{0,1}([0-9]{0,5}).*$')
def download_delay(self):
"""override
次にリクエストするホストに合わせ、delayを設定し直す
"""
if self.queue:
req_host, req_port = self._get_hostport(self.queue[0][0])
if req_host:
self.delay = self._delay_for_host(self.ua, req_host, req_port)
return super(CrawlDelayConsciousSlot, self).download_delay()
def _get_hostport(self, request):
"""リクエストURLから、リクエスト先のホスト名orIPアドレスとポート番号を返す"""
url_match = self.url_pattern.match(request.url)
if not url_match:
return (None, None)
host_name, port_num = url_match.groups()
port_num = int(port_num) if port_num else 80
return (host_name, port_num)
def _delay_for_host(self, ua, host, port):
"""
リクエスト先ホストに応じたdownload delayを返す。
ホストへの初回リクエスト時のみrobots.txtを確認する。
2回目以降はキャッシュから返す
"""
if host not in self.crawl_delay_cache:
crawl_delay = get_delay(ua, host, port)
self.crawl_delay_cache[host] = max(self.default_delay, crawl_delay)
return self.crawl_delay_cache[host]
class CrawlDelayConsciousDownloader(Downloader):
"""SlotをCrawlDelayConsciousSlotに差し替えたDownloader"""
def _get_slot(self, request, spider):
key = self._get_slot_key(request, spider)
if key not in self.slots:
conc = self.ip_concurrency if self.ip_concurrency else self.domain_concurrency
conc, delay = _get_concurrency_delay(conc, spider, self.settings)
self.slots[key] = CrawlDelayConsciousSlot(conc, delay, self.settings)
return key, self.slots[key]
# robots.txtのCrawl-Delayをリクエスト間隔に反映するDownloaderを使う
import sys
sys.path.append('上記コードを置いたパス')
DOWNLOADER = 'downloader.CrawlDelayConsciousDownloader'