(05.18) 수정됨.
무시할 수 없는 크롤러 트래픽
검색 노출을 원한다면 검색엔진에서 운영하는 크롤러의 방문은 필수적이다.
그러나 내가 원하지 않는 크롤러의 방문 역시 은근 많은 트래픽을 유발한다.
최근에는 chat gpt와 같은 생성형 ai에서 사용하는 크롤러의 방문이 많이 보이고 있지만, 그외에도 파이썬 requests 모듈과 같은 패키지를 사용해 웹사이트를 방문해 내용을 가져가는 트래픽도 은근 많다.
개인적인 주저리를 기록하는 블로그에 크롤링해갈 필요가 있는 데이터가 대체 무엇인지 상상하기 어렵지만...
내 블로그에도 다음과 같이 사람들이 직접 만든 크롤러가 방문한 흔적들이 다수 남아있다.
>>> from ... import Hit as h
>>> h.objects.filter(user_agent='').count()
80
>>> h.objects.filter(_user_agent__icontains='python').count()
154
>>> h.objects.filter(user_agent__icontains='python').distinct().order_by().values_list('user_agent')
<QuerySet [('Python/3.12 aiohttp/3.11.11',), ('Python/3.9 aiohttp/3.10.6',), ('Python/3.12 aiohttp/3.10.5',), ('Python/3.10 aiohttp/3.8.4',), ('python-requests/2.31.0',), ('python-requests/2.32.3',), ('python-requests/2.27.1',), ('Python/3.11 aiohttp/3.11.13',), ('python-requests/2.28.1',), ('python-requests/2.25.1',), ('Python/3.12 aiohttp/3.11.16',), ('python-httpx/0.26.0',)]>
>>> h.objects.filter(user_agent__icontains='java').count()
2
>>> h.objects.filter(user_agent__icontains='java').distinct().order_by().values_list('user_agent')
<QuerySet [('Java/17.0.9',), ('Java/23.0.1',)]>
>>> h.objects.filter(user_agent__icontains='scrapy').count()
7646
>>> h.objects.filter(user_agent__icontains='scrapy').distinct().order_by().values_list('user_agent')
<QuerySet [('Scrapy/2.12.0 (+https://scrapy.org)',), ('Scrapy/2.11.2 (+https://scrapy.org)',)]>
크롤러 트래픽도 비용이다.
이런 크롤러들이 적당한 선을 지키며 트래픽을 많이 가져가지 않는다면 내 서버에 딱히 영향을 주는 것은 없다.
그러나, 필요 이상으로 트래픽을 발생시키는 크롤러는 차단해야 한다.
웹 서버의 경우 무료가 아니고, 트래픽이 과도하게 발생한다면 트래픽 초과분만큼의 비용을 지불해야 한다.
일반적으로 생성되는 크롤러라면 robots.txt를 방문하고 로봇 규약을 지키려 하겠지만, robots rule을 지키지 않는 크롤러도 상당수 존재한다.
장고의 장점이자 단점
장고의 장점이자 단점은 바로 자율성이다.
내가 원하는 기능이라면 능력껏 구현하는 것이 가능하지만, 그것을 구현하려면 하나부터 열까지 내가 직접 구현해야한다.
다행히도 장고 기본 기능을 활용해서 악성 크롤러에 대처할 수 있는 기능을 구현해볼 수 있다.
악성 크롤러를 괴롭혀보자.
https://httpstat.us/200?sleep=4000
httpstat이라는 사이트가 있다. http 요청 테스트를 위한 사이트인데, 위 링크의 경우 4초 대기 후, http 200 response를 전달한다.
초단위가 아니라 ms(millisecond)단위를 전달받기 때문에 때문에 1초 = 1000ms이다.
만약 어떤 크롤러가 robots.txt에서 차단을 했음에도 불구하고 지속적으로 트래픽을 유발한다면, 다음과 같은 미들웨어를 통해 괴롭히는 것이 가능하다.
chat gpt에서는 gpt bot과 같은 user agent를 사용한다. 따라서 user agent에 gpt라는 문자열이 포함되어있다면, 다음과 같이 리다이렉트 시켜버릴 수 있다.
from django.http import HttpRequest, HttpResponse
from django.utils.deprecation import MiddlewareMixin
from django.shortcuts import redirect
class Middleware(MiddlewareMixin):
def process_response(self, request: HttpRequest, response: HttpResponse):
user_agent: str = request.META.get('HTTP_USER_AGENT')
if 'gpt' in user_agent.lower():
return redirect('https://httpstat.us/200?sleep=59000')
return response
특별한 조치를 취해둔 것이 아니라면 gpt 크롤러는 59초간 응답을 대기해야할 것이다.
페이지가 삭제되었음을 뜻하는 404 응답이나 다른 응답을 받았다면 해당 페이지를 다시 방문하지 않겠지만, 응답 결과는 200이다.
특정 크롤러의 방문을 원치 않는다고 의사를 robots.txt에 밝혔음에도 불구하고 지속적으로 트래픽을 보내온다면, 요청마다 장시간 대기를 하게 만들어 작업 시간을 의도적으로 지연시켜버리자.