

웹에는 다른 페이지에서 링크가 없어 사실상 찾아가기 어려운 '고립된 페이지'들이 있다. 이런 페이지들은 새로 만들어졌거나 아직 잘 알려지지 않은 경우이다.
웹 크롤러는 웹의 대부분을 탐색하려면 특정 출발점에서 시작해야 한다. 이걸 '루트 집합(root set)'이라고 부른다. 루트 집합은 모든 페이지에 도달할 수 있는 출발점 역할을 한다.
좋은 루트 집합은 다음과 같은 것들로 구성된다:
검색 엔진 같은 큰 크롤러는 사용자들이 루트 집합에 새로운 페이지를 추가할 수 있게 해준다. 이 루트 집합은 시간이 지나면서 계속 성장하고, 새로운 크롤링을 시작하는 '시드(seed)' 목록이 된다.
그림 9-1과 같은 웹 그래프를 보면, 웹 크롤러가 어떻게 페이지들을 찾아가는지 알 수 있다. 이 그림은 특정 루트 집합(예: A, G, S)만 가지고도 웹 상의 거의 모든 페이지(A부터 R까지)에 도달할 수 있음을 보여준다. 즉, 웹의 모든 페이지를 일일이 다 시작점으로 삼을 필요 없이, 몇몇 핵심적인 출발점만 잘 선택해도 전체 웹을 효율적으로 탐색할 수 있다는 의미이다.
웹 로봇은 다양한 목적으로 웹을 돌아다닌다. 주요 용도들은 다음과 같다.
웹 크롤러는 인터넷을 돌아다니면서 웹 페이지들을 계속 읽는다. 웹 페이지 안에는 다른 웹 페이지로 연결되는 '링크(URL)'들이 많이 숨어 있다.
../images/photo.jpg처럼 짧게 표시된 링크가 있다. 이것을 '상대 링크'라고 부른다. 이는 완전한 주소가 아니라, 현재 페이지를 기준으로 '한 단계 위 폴더에 있는 이미지' 같은 식으로 표시된 것이다.http://www.example.com/images/photo.jpg처럼 완전한 주소(절대 링크)로 바꿔줘야 한다. 그래야 나중에 이 링크를 따라갈 때 어디로 가야 할지 정확히 알 수 있다. 마치 "책 102페이지에 그림 있어"가 아니라 "도서관에 있는 이 책의 102페이지에 그림 있어"라고 정확하게 말해주는 것과 같다. 이렇게 링크 목록은 크롤링을 진행하면서 점점 더 많아지고 복잡해진다.
로봇이 웹을 크롤링하다 보면 마치 뫼비우스의 띠처럼 같은 페이지를 계속해서 방문하게 될 때가 있다. 이것을 '순환(Cycle)' 또는 '루프(Loop)'라고 부른다. 로봇이 한 번 방문했던 곳을 또 방문하고, 또 방문하면 시간 낭비가 심하고, 결국 모든 웹 페이지를 다 탐색하지 못하고 특정 부분에서 갇히게 된다.
아래 그림 9-2 (하이퍼링크의 웹을 크롤링하기)를 보면서 설명한다.
만약 로봇이 여기서 A 페이지를 다시 방문한다면 어떻게 될까?
A → B → C → A → B → C → A ... 이렇게 끝없이 같은 세 페이지를 왔다 갔다 하게 된다. 이것이 바로 순환이다.
왜 순환을 피해야 할까?
순환에 빠지면 로봇은 다음과 같은 문제를 겪는다.
순환을 피하는 방법
로봇이 순환에 빠지지 않으려면 "내가 어디를 방문했었는지"를 반드시 기억하고 있어야 한다. 마치 지도를 보면서 "아, 여기는 방금 지나왔던 길이네? 다른 길로 가야겠다!" 하고 아는 것과 같다. 로봇은 이미 방문했던 페이지라면 다시 방문하지 않도록 목록을 만들어 관리한다. 이 목록을 보통 '방문 URL 목록'이라고 부르며, 크롤링이 진행될수록 이 목록은 계속해서 커진다.
요약하자면
웹 크롤러는 웹 페이지를 돌아다니며 링크를 찾아내고 그 주소를 정확하게 정리(링크 추출과 상대 링크 정상화)해야 하며, 한번 방문했던 곳은 다시 가지 않도록 기억해서 무한 반복(순환)에 빠지지 않도록 주의(순환 피하기)해야 한다. 그래야 웹의 방대한 정보를 효율적으로 수집할 수 있다.
크롤러가 무한 반복(루프)에 빠지는 것은 여러모로 해롭다.
크롤러가 방문했던 URL들을 계속해서 추적하는 것은 쉽지 않다. 왜냐하면 웹에는 셀 수 없이 많은 웹페이지(수억 개 이상)가 존재하고, 계속해서 새로운 콘텐츠가 생성되기 때문이다.
만약 전 세계 웹 콘텐츠의 대부분을 크롤링하려고 한다면, 수억 개의 URL을 방문해야 한다. 각 URL을 방문했는지 빠르게 추적하는 작업은 매우 중요하고 도전적인 일이다. URL이 워낙 많기 때문에, 어떤 URL을 방문했는지 빠르고 효율적으로 확인하려면 복잡한 자료 구조를 사용해야 한다. 이 자료 구조는 속도와 메모리 사용 면에서 효율적이어야 한다.
수억 개의 URL은 엄청난 메모리 공간을 요구한다. 예를 들어, 평균 URL 길이가 40바이트이고 크롤러가 5억 개의 URL을 크롤링했다고 가정하면, 이 URL들을 저장하는 데 20GB 이상의 메모리가 필요하다. (40바이트 x 5억 URL = 20GB!)
대규모 웹 크롤러가 방문했던 곳을 관리하기 위해 주로 사용하는 유용한 기법들은 다음과 같다.
체크포인트
로봇 프로그램이 갑작스럽게 중단될 경우를 대비해, 방문한 URL의 목록이 디스크에 저장되었는지 확인하는 것이 중요하다. 그래야 나중에 다시 시작할 때 이어서 크롤링을 할 수 있다.
파티셔닝
웹이 계속 커지면서, 한 대의 컴퓨터에 하나의 로봇만으로는 전체 웹을 크롤링하기 불가능해졌다. 한 대의 컴퓨터로는 메모리, 디스크 공간, 연산 능력, 네트워크 대역폭이 충분하지 않기 때문이다. 그래서 몇몇 대규모 웹 로봇은 각각이 분리된 여러 대의 컴퓨터가 동시에 작업하는 '농장(farm)'을 이용한다. 각 로봇에게는 특정 URL들의 '일부'가 할당되어 그에 대한 책임을 진다. 로봇들은 서로 도와가며 웹을 크롤링한다. 개별 로봇들은 URL들을 이리저리 넘겨주거나, 오동작하는 동료를 돕거나, 그 외 다른 이유로 자신들의 활동을 조절하기 위해 서로 커뮤니케이션을 해야 할 필요가 있다.
아무리 방문 기록을 잘 관리하는 자료 구조를 갖추고 있더라도, 하나의 웹 페이지가 여러 개의 다른 주소(URL)를 가질 수 있다. 이것을 '별칭'이라고 한다. 즉, 여러 개의 URL이 겉보기에는 달라 보여도 실제로는 같은 내용을 가리키고 있는 것이다. 이렇게 되면 로봇은 같은 페이지를 다른 페이지로 착각하고 또 방문할 수 있다.
http://www.foo.com/bar.html 과 http://www.foo.com:80/bar.htmlhttp://www.foo.com/~fred 과 http://www.foo.com/%7Ffred%7F와 같이 특수 문자가 인코딩되어 있어도 같은 주소를 가리킬 수 있다.http://www.foo.com/X.html#early 과 http://www.foo.com/X.html#middle# 뒤에 붙는 태그(앵커)는 페이지 내의 특정 위치를 나타내므로, 태그가 다르더라도 기본적으로 같은 페이지를 가리킨다.http://www.foo.com/readme.htm 과 http://www.foo.com/README.HTM.htm과 .HTM)http://www.foo.com/ 과 http://www.foo.com/index.htmlhttp://www.foo.com/index.html 과 http://209.231.87.45/index.htmlwww.foo.com)과 해당 도메인의 IP 주소(209.231.87.45)는 결국 같은 서버를 가리키므로, 같은 주소로 인식된다.이처럼 겉으로는 다른 URL처럼 보여도 실제로는 같은 콘텐츠를 가리키는 경우가 많다. 로봇들은 URL을 표준 형식으로 변환하는 '정규화'만으로는 해결할 수 없는 이런 다양한 별칭들을 마주하게 될 것이다.
대부분의 웹 크롤러나 로봇은 이러한 별칭 문제로 인한 중복 방문을 피하기 위해 URL을 '정규화(Normalization)'하는 시도를 한다. 정규화는 다른 URL이지만 같은 리소스를 가리키는 경우를 미리 걸러내기 위한 작업이다. 로봇은 다음 방법으로 URL을 표준화한다.
http://www.foo.com/ → http://www.foo.com:80/)%XX와 같이 인코딩된 문자를 원래 문자로 되돌린다. (예: %7F → ~)#와 그 이후의 문자열(태그 또는 앵커)은 페이지 내의 특정 위치를 가리킬 뿐, 페이지 자체는 같으므로 제거한다.이러한 단계들은 위 표 9-1의 a부터 c까지의 문제(기본 포트, 이스케이프 문자, 태그)를 해결할 수 있다. 하지만 모든 웹 서버에 대한 지식 없이 d부터 f까지와 같은 다른 종류의 중복을 피하기는 어렵다.
file.htm과 File.HTM을 다르게 취급), 어떤 서버는 구분하지 않는다.http://www.foo.com/과 http://www.foo.com/index.html)이 같은 리소스를 가리키는지 알려면, 해당 웹 서버의 색인 파일 설정(기본 페이지 이름)을 알아야 할 필요가 있다.
파일 시스템에는 '심벌릭 링크(Symbolic Link)'라는 특별한 바로가기가 있다. 이건 마치 윈도우의 '바로가기' 파일이나 리눅스의 '심볼릭 링크'와 비슷하다. 이 심벌릭 링크는 실제 파일이나 디렉터리가 아니라, 다른 파일이나 디렉터리를 가리키는 포인터 같은 존재다.
문제는 이 심벌릭 링크가 끝없이 이어지는 디렉터리 계층 구조를 만들 수 있다는 점이다. 예를 들어, dir1이라는 폴더 안에 dir2라는 심벌릭 링크가 있는데, 이 dir2가 다시 dir1을 가리키는 식이다. 이렇게 되면 로봇은 dir1 → dir2 → dir1 → dir2... 하면서 무한히 같은 곳을 맴돌게 된다.
이런 심벌릭 링크 순환은 서버 관리자가 실수로 만들기도 하지만, 때로는 '사악한 웹 마스터'가 로봇을 함정에 빠뜨리기 위해 악의적으로 만들기도 한다. 로봇이 이런 순환에 빠지면 계속 같은 곳만 돌다가 더 이상 다른 페이지를 탐색하지 못하게 된다.
index.html과 subdir라는 디렉터리가 있다.subdir가 위쪽(상위 디렉터리)을 가리키는 심벌릭 링크로 설정된 경우다.http://www.foo.com/index.html을 가져오고, subdir/index.html로 이어지는 링크를 발견한다.http://www.foo.com/subdir/index.html을 가져오고, subdir/logo.gif로 이어지는 링크를 발견한다.http://www.foo.com/subdir/logo.gif를 가져오고, 더 이상 링크가 없으면 여기서 탐색이 끝난다. (이것은 일반적인 경우)하지만 그림 9-3b처럼 subdir가 상위 디렉터리를 가리키는 심벌릭 링크라면 상황이 달라진다.
http://www.foo.com/index.html을 가져온다.subdir/index.html 링크를 발견하고 http://www.foo.com/subdir/index.html을 가져온다.subdir가 상위 디렉터리를 가리키므로, http://www.foo.com/subdir/subdir/index.html 같은 주소가 생성될 수 있다.subdir/subdir/subdir/... 처럼 URL 길이가 계속 늘어나면서 무한 루프에 빠지게 된다. 크롤러는 이 URL이 이전에 방문했던 곳인지 판단하기 매우 어려워진다.
웹에는 '동적 가상 공간'이라는 개념이 있다. 이건 웹사이트의 특정 부분이 사용자 입력이나 시간에 따라 무한히 많은 페이지를 생성할 수 있다는 의미다. 예를 들어, 온라인 캘린더나 검색 결과 페이지, 게시판 등은 사용자가 날짜를 바꾸거나 검색어를 입력할 때마다 새로운 URL을 가진 페이지를 만들어낸다.
순환을 완벽하게 피하는 가장 좋은 방법은 잘 설계된 크롤러를 만드는 것이다. 실제로 웹 크롤링은 단순히 기술적인 문제뿐 아니라, 웹의 복잡성과 동적인 특성 때문에 늘 어려운 과제다.
사람의 모니터링:
웹은 매우 거친 곳이다. 로봇은 아무리 잘 만들어도 스스로 해결할 수 없는 문제에 부딪히게 될 때가 있다. 그래서 모든 상업용 수준의 로봇은 사람이 로봇의 진행 상황을 쉽게 모니터링하고, 뭔가 특이한 일이 일어나면 즉시 인지할 수 있도록 설계되어야 한다. 그렇지 않으면 웹사이트 운영자들이 당신의 로봇 때문에 피해를 입고 불만을 제기할 수도 있다.
웹처럼 거대한 데이터를 크롤링하기 위한 좋은 '스파이더 휴리스틱(탐색 전략)'을 만드는 작업은 언제나 진행 중이다. 시간이 지남에 따라 새로운 규칙이 만들어지고, 웹에 새로운 종류의 리소스가 추가됨에 따라 적용된다. 좋은 규칙들은 항상 진화한다.
더 작고 맞춤형 크롤러들은 자신이 어떤 자원(서버, 네트워크 대역폭 등)에 얼마나 영향을 줄 것인지 스스로 제어할 수 있다. 또는 심지어 그 자원 자체가 크롤링을 수행하는 사람의 제어 하에 있을 수도 있다(인트라넷 사이트처럼). 이런 크롤러들은 문제를 예방하기 위해 사람의 모니터링에 더욱 의존한다.
로봇은 다른 HTTP 클라이언트 프로그램과 다르지 않다. HTTP 명세의 규칙을 지키고, 적절한 HTTP 요청 헤더를 사용해야 한다. 많은 로봇이 HTTP 기능을 최소한으로만 구현하려 하는데, 이로 인해 문제가 생길 수 있다. 많은 로봇이 HTTP/1.0 요청을 보내는데, 이는 요구사항이 적기 때문이다.
로봇들은 HTTP 기능을 최소한으로 구현하려 해도, 대부분은 자신의 신원(특히 User-Agent HTTP 헤더)을 알리는 헤더를 구현하고 전송한다. 이는 로봇의 능력, 출처, 발신을 알려준다. 잘못된 크롤러 소유자를 찾거나, 서버가 로봇에게 어떤 종류의 콘텐츠를 제공할지 결정할 때 유용하다.
기본적인 신원 식별 헤더:
User-Agent: SnipBot 1.0)
로봇은 Host 헤더를 지원해야 한다. 가상 호스팅이 널리 퍼진 상황에서 Host 헤더를 보내지 않으면 로봇이 잘못된 콘텐츠를 가져올 수 있다. HTTP/1.1은 Host 헤더 사용을 요구한다.
대부분의 서버는 특정 사이트 하나를 운영하도록 설정된다. 그러나 Host 헤더를 포함하는 크롤러는 두 개 이상의 사이트를 운영하는 서버에 요청을 보낼 수도 있다. (예: joes-hardware.com과 www.foo.com).
크롤러가 이미 가져온 콘텐츠를 다시 가져올 필요가 없도록 If-Modified-Since 같은 HTTP 요청 헤더를 사용한다. 이는 캐시와 유사한 동작으로, 리소스를 다시 가져올지 말지 서버가 결정하게 하여 대역폭과 시간을 절약한다. 로봇은 이 기법을 사용해서 서버에 저장된 리소스의 유효성을 검사하여, 리소스가 변경되지 않았다면 다시 가져오지 않는다. 이는 로컬 캐시와 비슷하게 작동한다.
로봇은 단순히 웹 페이지를 가져오는 것을 넘어, 서버가 보내는 다양한 HTTP 응답을 이해하고 처리해야 한다. 응답 코드를 이해하고 그에 따라 다르게 행동해야 한다. (예: 200 OK는 성공, 404 Not Found는 페이지 없음)
엔티티: HTTP 응답에 포함된 본문(데이터)을 '엔티티'라고 한다. 로봇은 엔티티가 어떤 종류의 콘텐츠(HTML, 이미지 등)인지 알기 위해 Content-Type과 같은 HTTP 헤더를 확인해야 한다.
또한 http-equiv="refresh" 정보를 찾아내기 위해 HTML 문서의 <HEAD> 태그를 탐색할 수 있다. 이는 페이지 리다이렉션(refresh 메타 태그)을 지시하므로, 이를 이해하고 따라가야 한다.
웹 관리자는 많은 로봇이 자신의 사이트를 방문할 것을 명시하고, 그 로봇들의 요청을 예상해야 한다. 많은 웹 사이트들은 브라우저 종류를 감지하여 콘텐츠를 최적화하는데, 이로 인해 로봇에게는 오류 페이지를 제공할 수도 있다.
사이트 관리자는 로봇 요청을 처리하기 위한 전략을 세워야 한다. 로봇이 자신의 사이트에 방문했다가 콘텐츠를 얻지 못하고 당황하는 일이 없도록 대비해야 한다.
웹 사이트 운영자는 HTML 문서의 <HEAD> 태그 안에 '로봇 메타 지시자(Meta Robots tag)'를 넣어서 크롤러에게 특정 페이지를 어떻게 처리할지 알려줄 수 있다.
이런 메타 지시자는 로봇이 페이지를 어떻게 다룰지 명확히 알려주므로, 웹 관리자가 로봇의 행동을 제어하는 데 유용하다.
제대로 동작하지 않는 로봇은 웹에 큰 문제를 일으킬 수 있다.

웹 관리자가 로봇의 접근을 제한하는 표준 방법이 있다. 바로 robots.txt 파일이다.
robots.txt란? 웹 사이트의 루트 디렉터리에 있는 텍스트 파일이다. 웹 사이트 관리자가 어떤 로봇이 어떤 부분에 접근해도 되는지 또는 안 되는지 규칙을 명시한다.robots.txt 파일을 가져와 읽는다. 이 파일에 자신에게 적용되는 규칙이 있으면 그 규칙에 따라 행동한다.robots.txt 표준은 여러 버전(0.0, 1.0, 2.0)이 있으며, 대부분의 상업용 크롤러와 검색 엔진은 표준을 따르려 한다.robots.txt 파일의 구성: robots.txt 파일은 User-Agent로 로봇의 종류를 지정하고, Disallow나 Allow 규칙으로 특정 URL이나 디렉터리에 대한 접근을 허용하거나 금지한다./temp/*는 /temp/로 시작하는 모든 URL)robots.txt 파일의 예시:User-Agent: *
Disallow: /private/
Disallow: /temp/
User-Agent: myrobot
Disallow: /cgi-bin/ 이 예시는 모든 로봇에게 /private/와 /temp/ 디렉터리 접근을 금지하고, myrobot이라는 로봇에게는 /cgi-bin/ 디렉터리 접근을 금지한다.robots.txt의 개선과 한계: robots.txt 파일이 없으면 로봇은 서버에 접근할 수 있다고 간주한다. 웹 마스터는 robots.txt 파일의 내용이 제대로 구성되었는지 주의해야 한다. 대부분의 크롤러는 HTTP/1.1 클라이언트가 아니므로 캐시 제어 헤더를 이해하지 못할 수도 있다.robots.txt 파일은 완벽하지 않다: robots.txt는 모든 로봇이 따라야 하는 '권고'일 뿐이다. 악의적인 로봇은 무시할 수 있다.robots.txt 파일 캐싱: 로봇은 robots.txt 파일을 캐시(임시 저장)한다. 하지만 캐시된 정보가 오래되면 실제 웹 서버의 robots.txt 규칙과 달라져 문제가 생길 수 있다.robots.txt를 무시하는 로봇에게 유용하다.robots.txt 파일 검증로봇을 개발할 때는 robots.txt 파일을 올바르게 해석하는지 검증하는 것이 매우 중요하다. 이를 위해 온라인 검증 도구를 사용하거나, RobotsRules 같은 라이브러리를 이용할 수 있다. 이는 로봇이 웹 관리자의 의도를 존중하며 올바르게 행동하도록 돕는다.
robots.txt 파일 외에 HTML 문서 안에 META 태그를 사용하여 개별 페이지 단위로 로봇의 동작을 제어할 수도 있다.
<META NAME="ROBOTS" CONTENT="NOINDEX,NOFOLLOW">robots.txt 규칙보다 우선하며, 로봇은 페이지를 가져온 후에야 이 태그를 확인할 수 있다.1993년에 맷 코플러(Matthew Koehler)가 만든 로봇 아키텍처가 있다. 이 로봇은 가이드라인을 따라 설계되었고, 오늘날 로봇 설계에도 유용하게 쓰인다.
로봇이 어떤 기능들을 하는지 단계별로 설명한다.
User-Agent 헤더를 포함한다.robots.txt 규칙 확인하는 일 (제어): robots.txt 파일을 읽어서 접근이 허용된 페이지인지 확인한다.이 외에도 다양한 기능들이 로봇에 포함될 수 있다.
웹 로봇의 가장 중요한 활용처는 바로 인터넷 검색엔진이다. 오늘날 가장 큰 검색엔진은 사용자들이 관심 있는 정보를 찾을 수 있도록 방대한 서비스를 제공한다. 검색엔진은 단순히 웹을 돌아다니는 것뿐만 아니라, 로봇이 가져온 정보를 색인(index)하고 사용자에게 보여주는 복잡한 과정을 거친다.
검색엔진은 사용자들이 실제로 웹을 이용하는 방식(흥미를 끄는 정보가 담긴 페이지는 잘 안 변하지만, 별 재미 없는 페이지는 금방 바뀐다든지)을 이해해야 한다.
많은 사용자가 개인 웹페이지 정보를 웹에 공개하는데, 검색엔진은 이런 개인 정보가 포함된 페이지를 필터링하거나 표시하지 않도록 주의해야 한다. 검색 결과는 웹 페이지의 HTTP 요청을 기반으로 만들어진다. 검색엔진은 페이지의 '흥미도'를 평가해야 하고, 사용자가 검색한 내용과 얼마나 관련 있는지(연관성 랭킹)도 판단해야 한다.

일반적인 검색엔진은 크게 두 부분으로 나뉜다.
검색엔진의 복잡성은 웹 콘텐츠가 끊임없이 변하기 때문에 발생한다. 새로운 페이지가 계속 생기고 기존 페이지가 업데이트되거나 사라지므로, 검색엔진의 색인도 항상 최신 상태를 유지하기 위해 끊임없이 업데이트되어야 한다.

'풀 텍스트 색인'은 각 단어가 포함된 문서를 찾아주는 데이터베이스다. 문서에 단어가 어디에 나왔는지까지는 기록하지 않고, 단순히 '이 문서에 이런 단어가 있다'는 정보만 가지고 있어도 충분하다.

사용자가 검색엔진 게이트웨이에 질의(검색 요청)를 보내는 방법은 HTML 폼을 이용한 HTTP POST 요청과 같다. 게이트웨이는 검색 요청을 풀 텍스트 색인으로 보내고, 색인은 결과를 보여준다. (예: joes-hardware.com의 검색 질의 예시)
검색엔진은 질의에 관련된 문서를 찾으면, 게이트웨이는 이 문서들을 사용자에게 친숙한 페이지 형식으로 변환하여 보여준다.
'스패밍'은 웹 페이지 내용과 실제 검색 결과가 일치하지 않게 만드는 행위를 말한다.