악성 크롤러를 괴롭히는 방법

작성자: [관리자] 하얀설표

2025.04.20 17:36 (KST) 작성됨

2025.05.18 22:54 (KST) 수정됨






(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에 밝혔음에도 불구하고 지속적으로 트래픽을 보내온다면, 요청마다 장시간 대기를 하게 만들어 작업 시간을 의도적으로 지연시켜버리자.

 

 

 

 






추천 (0)


글 목록

댓글을 달 수 없는 게시물입니다.


"분류없음" 카테고리의 #Python, #Django 관련 게시물

분류없음
예제)특정 조합이 리스트 요소에 반드시 포함되어야 한다는 사실만 알 때의 조건식
수정 08.20 | [관리자] 하얀설표
👍 0
#Python, #예제
🗨️ 0
썸네일
분류없음
주식시장 개장일과 휴장일 정보를 간단하게 가져오는 방법(엑셀, 파이썬)
수정 07.20 | [관리자] 하얀설표
👍 0
#Python, #주식
🗨️ 0
분류없음
해결) 장고 bulk_update의 메모리 누수 문제(django orm bluk_update method memory leak)
수정 07.12 | [관리자] 하얀설표
👍 0
#Python, #Django
🗨️ 0
분류없음
거지같은 subQuery와 outerRef
수정 06.29 | [관리자] 하얀설표
👍 0
#Django
🗨️ 0
분류없음
장고) pk 리스트에 등록된 순서대로 오브젝트를 가져오기 예제
작성 06.29 | [관리자] 하얀설표
👍 0
#Django
🗨️ 0
분류없음
해결) django.db.utils.OperationalError: database is locked
수정 06.18 | [관리자] 하얀설표
👍 0
#Python, #에러해결, #Django
🗨️ 0
썸네일
분류없음
장고 예제) 게시판 기능 만들기
수정 06.17 | [관리자] 하얀설표
👍 0
#Django, #Django 예제
🗨️ 0
분류없음
장고 튜토리얼) settings.py 분리하기
수정 06.15 | [관리자] 하얀설표
👍 0
#Django, #Django 튜토리얼
🗨️ 0
썸네일
분류없음
장고 튜토리얼) 패키지 설치와 프로젝트 설치 및 실행 방법
수정 06.15 | [관리자] 하얀설표
👍 0
#Django, #Django 튜토리얼
🗨️ 0
분류없음
장고 튜토리얼) 관리자 계정과 일반 계정 만드는 방법
수정 06.15 | [관리자] 하얀설표
👍 0
#Django, #Django 튜토리얼
🗨️ 0