Computer >> 컴퓨터 >  >> 프로그래밍 >> Ruby

나의 첫 번째 웹 스크래퍼 만들기, 2부: Mechanize로 페이지 탐색과 폼 자동화하기

이 튜토리얼에서는 Mechanize를 사용해 링크를 클릭하고, 폼을 작성하며, 파일을 업로드하는 방법을 배웁니다. 또한 Mechanize의 페이지 객체를 원하는 대로 다루는 방법과 구글 검색을 자동화하여 결과를 저장하는 방법까지 살펴봅니다.

주요 내용

  • 단일 페이지 vs. 페이지네이션
  • Mechanize
  • Agent
  • Page
  • Nokogiri 메서드
  • 링크
  • 클릭

단일 페이지 vs. 페이지네이션

지금까지 우리는 Nokogiri를 사용해 단일 페이지의 화면을 어떻게 스크랩하는지 알아보았습니다. 이것은 한 단계 더 나아가 여러 페이지에서 콘텐츠를 추출하는 방법을 배우기 위한 좋은 기반이 되었습니다.

결국 우리가 해결하려는 문제는 140개가 넘는 에피소드의 콘텐츠를 가져오는 것인데, 이 정도 분량은 하나의 웹 페이지에 담기에는 무리가 있습니다. 따라서 페이지네이션을 다뤄야 하며, 콘텐츠를 따라 깊이 들어가는 방법을 찾아야 합니다.

바로 이 지점에서 Nokogiri의 역할은 끝나고, Mechanize라는 유용한 젬(gem)이 등장합니다.

Mechanize란?

Mechanize는 강력한 도구로, 콘텐츠를 추출해야 하는 웹사이트와의 상호작용을 자동화할 수 있게 해줍니다. 그런 의미에서 Capybara로 테스트할 때 접했던 일부 기능과 비슷하게 느껴질 수 있습니다.

오해하지 마시기 바랍니다. Nokogiri로 단일 페이지를 다루는 것 자체도 훌륭하지만, 좀 더 복잡한 데이터 추출 작업에는 조금 더 강력한 성능이 필요합니다. Mechanize를 사용하면 필요한 만큼 많은 페이지를 크롤링하고, 요소들과 상호작용하며, 인간의 행동을 모방해 자동화할 수 있습니다. 상당히 강력한 기능입니다!

이 젬은 링크를 따라가고, 폼 필드를 작성하고, 데이터를 제출할 수 있게 해주며, 심지어 쿠키 처리까지 가능합니다. 즉, 사용자가 비공개 세션에 로그인하는 과정을 흉내 내어 본인만 접근할 수 있는 사이트의 콘텐츠도 가져올 수 있다는 뜻입니다.

자신의 계정 정보로 로그인 폼을 작성하고 Mechanize에게 이후 과정을 어떻게 따라갈지 알려주면 됩니다. 링크 클릭과 폼 제출이 가능하기 때문에 이 도구로 할 수 없는 일은 거의 없습니다. Mechanize는 Nokogiri와 밀접한 관계가 있으며 Nokogiri에 의존합니다. 그리고 Aaron Patterson 역시 이 멋진 젬의 저자 중 한 명입니다.

Mechanize 에이전트 인스턴스 생성

본격적으로 시작하기 전에 먼저 Mechanize 에이전트를 인스턴스화해야 합니다.

some_scraper.rb

agent는 Nokogiri에서 했던 것처럼 페이지를 가져오는 데 사용됩니다.

some_scraper.rb

여기서 일어나는 일은 Mechanize 에이전트가 팟캐스트 페이지와 해당 페이지의 쿠키를 함께 가져왔다는 것입니다.

페이지 콘텐츠 추출하기

이제 추출 준비가 된 페이지를 확보했습니다. 본격적으로 추출하기 전에 inspect 메서드를 사용해 내부 구조를 들여다보는 것을 권장합니다.

some_scraper.rb

출력 결과가 상당히 방대합니다. 직접 확인해 보면 Mechanize::Page 객체가 어떤 것으로 구성되어 있는지 알 수 있습니다. 여기에서 해당 페이지의 모든 속성을 볼 수 있습니다.

개인적으로 이 객체는 추출하려는 데이터를 잘라내는 데 매우 유용한 도구라고 생각합니다.

Output

HTML 페이지 자체를 확인하고 싶다면 body 또는 content 메서드를 붙여 사용하면 됩니다.

some_scraper.rb

팟캐스트 사이트는 페이지에 포함된 요소 종류가 많지 않으므로, github.com에서 반환되는 Mechanize::Page 객체도 살펴볼 가치가 있습니다. 훨씬 다양한 콘텐츠가 담겨 있어 감각을 익히기에 좋습니다.

Output github.com

다시 팟캐스트로 돌아가서, 인코딩 정보, HTTP 응답 코드, URI, 응답 헤더 같은 것들도 확인할 수 있습니다.

some_scraper.rb

Output

더 깊이 파고들면 확인할 수 있는 것들이 많지만, 여기까지만 다루겠습니다.

Nokogiri 메서드 활용하기

  • at
  • search

Mechanize는 내부적으로 Nokogiri를 사용해 페이지에서 데이터를 스크랩합니다. 첫 번째 글에서 배운 Nokogiri 지식을 Mechanize 페이지에도 그대로 적용할 수 있습니다. 즉, 일반적으로 페이지 탐색에는 Mechanize를 사용하고, 실제 스크래핑 작업에는 Nokogiri 메서드를 활용하는 방식입니다.

예를 들어 단일 객체를 검색하려면 at을 사용하고, 특정 페이지에서 셀렉터에 일치하는 모든 객체를 얻으려면 search를 사용합니다. 다시 말해, 이 메서드들은 Nokogiri 문서 객체와 Mechanize 페이지 객체 양쪽 모두에서 동작합니다.

some_scraper.rb

Output

링크 다루기

  • links
  • link_with
  • links_with

이제 사이트 전체를 원하는 대로 탐색할 수 있습니다. 아마도 Mechanize의 가장 중요한 부분은 링크를 자유롭게 다룰 수 있는 능력일 것입니다. 그렇지 않다면 굳이 Nokogiri만으로 충분했을 테니까요. 페이지에 링크 목록을 요청하면 무엇이 반환되는지 살펴보겠습니다.

some_scraper.rb

꽤 긴 목록이 반환됩니다. 하나씩 짚어보겠습니다. Mechanize에게 다른 곳을 살펴보라고 지시하지 않았기 때문에, 첫 번째 페이지에서 발견된 링크들의 배열을 얻게 됩니다. Mechanize는 해당 페이지를 위에서 아래로 순회하며 링크 목록을 반환합니다.

참고로, 이 출력 결과는 제 팟캐스트 리디자인의 최종 결과물이기도 합니다. 시연용으로는 이 버전이 좀 더 나은 것 같습니다. 최종 결과물이 어떤 모습인지, 그리고 왜 예전 Sinatra 사이트를 스크랩해야 했는지 엿볼 수 있습니다.

언제나 그렇듯이, 링크에서 텍스트만 추출할 수도 있습니다.

some_scraper.rb

링크를 한꺼번에 모두 가져오는 것은 유용할 수도 있지만 때로는 번거롭기만 할 수 있습니다. 다행히 필요한 것을 정밀하게 걸러낼 수 있는 몇 가지 도구가 마련되어 있습니다.

특정 조건(예: 링크 텍스트)에 맞는 링크를 찾으려면 links_withlink_with 같은 더 깔끔한 API를 사용할 수 있습니다. 또한 동일한 Focus 링크가 여러 개라면 대괄호 []를 사용해 페이지에서 특정 번호의 링크를 지목할 수 있습니다.

some_scraper.rb

링크 텍스트가 아니라 링크 자체가 목적이라면, 특정 href 값만 지정하면 됩니다. Mechanize는 이런 경우에도 방해하지 않습니다. text 대신 href를 인자로 넘기면 됩니다.

some_scraper.rb

원하는 텍스트를 가진 첫 번째 링크만 찾고 싶다면 다음 문법을 활용할 수도 있습니다. 매우 편리하고 가독성도 좋습니다.

some_scraper.rb

그렇다면 이 Focus 링크를 실제로 따라가 그 뒤에 무엇이 숨어 있는지 볼 수는 없을까요? 직접 click해 봅시다!

클릭하기

some_scraper.rb

앞서와 마찬가지로 긴 링크 목록을 또 얻게 됩니다. .click.links를 이렇게 간단히 연결할 수 있다는 점에 주목하세요. Mechanize가 링크를 클릭하고 새 목적지 페이지로 이동해 줍니다. 링크 목록도 함께 요청했기 때문에 새 페이지에서 발견할 수 있는 모든 링크를 받게 됩니다.

예를 들어 동일한 인터뷰이의 텍스트 링크가 두 개 있는데, 하나는 태그 페이지로, 다른 하나는 최신 에피소드 페이지로 연결된다고 가정해 봅시다. 두 페이지 각각의 링크를 모두 가져오고 싶다면 다음과 같이 하면 됩니다.

some_scraper.rb

이 코드는 두 페이지의 링크 목록을 모두 반환합니다. 각 링크를 순회하면서 Mechanize가 클릭된 링크를 따라가고, 새 페이지에서 발견한 링크들을 수집해 줍니다. 아래에 조합 예시 몇 가지를 정리해 두었으니 참고하세요.

some_scraper.rb

폼 다루기

  • submit
  • field_with
  • checkbox_with
  • radiobuttons_with
  • file_uploads

이번에는 폼을 살펴보겠습니다!

some_scraper.rb

forms 메서드를 사용하면 폼이 하나뿐이더라도 배열이 반환됩니다. 이제 폼 이름이 "f"라는 것을 알았으므로, 단수형 메서드 form을 사용해 해당 폼만 정확히 지목할 수 있습니다.

some_scraper.rb

form('f')를 사용해 작업하려는 특정 폼만 골라냈습니다. 덕분에 배열이 아닌 단일 객체가 반환됩니다.

텍스트 입력 필드의 이름(q)도 확인할 수 있습니다.

이 이름으로 필드를 지목하고 Ruby 속성처럼 값을 설정할 수 있습니다. 새로운 값만 지정해 주면 됩니다. 위 출력 예시에서 볼 수 있듯이 기본값은 비어 있습니다.

some_scraper.rb

위 출력에서 볼 수 있듯이 텍스트 필드의 값이 New Google Search로 변경되었습니다. 이제 폼을 submit하고 구글이 반환하는 페이지에서 결과를 수집하기만 하면 됩니다. 이보다 쉬울 수 없습니다. 이번에는 다른 키워드로 검색해 봅시다!

some_scraper.rb

여기서는 CSS 셀렉터 h3.r로 검색 결과 헤더를 식별하고, 그 text를 매핑한 뒤 pretty print로 결과를 출력했습니다. 어렵지 않았죠? 물론 간단한 예시지만, 이를 응용할 수 있는 무한한 가능성을 생각해 보시길 바랍니다!

Output

Mechanize는 다양한 입력 필드를 다룰 수 있도록 지원합니다. 파일 업로드조차 가능합니다!

  • field_with
  • checkbox_with
  • radiobuttons_with
  • file_uploads

라디오 버튼과 체크박스도 이름으로 식별한 후, 예상하셨겠지만 check 메서드로 선택할 수 있습니다.

some_scraper.rb

option 태그는 드롭다운 목록에서 항목 하나를 선택할 때 사용됩니다. 마찬가지로 이름으로 대상을 찾고, 원하는 옵션 번호를 선택하면 됩니다.

some_scraper.rb

파일 업로드는 폼에 텍스트를 입력하는 방식과 비슷하게 Ruby 속성처럼 설정하여 처리합니다. 업로드 필드를 식별한 후 전송하려는 파일 경로(파일 이름)를 지정하면 됩니다. 말로 설명하면 복잡해 보이지만 실제로는 간단합니다. 살펴보겠습니다!

some_scraper.rb

마무리 생각

보셨듯이, 결국 마법 같은 것은 없습니다! 이제 스스로 재미있는 프로젝트를 진행할 준비가 되었습니다. Nokogiri와 Mechanize에 대해 배울 것이 더 남아 있긴 하지만, 불필요한 부분에 시간을 낭비하기보다는 직접 만져보면서 익히고, 초급 튜토리얼 범위를 넘어선 문제에 부딪힐 때 추가 문서를 찾아보는 것이 좋습니다.

이 젬이 얼마나 아름답게 단순하면서도 얼마나 강력한 힘을 제공하는지 느끼셨기를 바랍니다. 대중문화에서 배웠듯이, 강력한 힘에는 책임이 따릅니다. 반드시 합법적인 범위 내에서, 그리고 API가 제공되지 않는 경우에만 사용하세요. 이런 도구를 자주 사용할 일은 없겠지만, 진짜 스크래핑 작업이 필요할 때는 놀랍도록 유용하다는 것을 알게 될 것입니다.