Computer >> Máy Tính >  >> Lập trình >> Ruby

Xây dựng công cụ quét trang web đầu tiên của bạn, Phần 2

Trong hướng dẫn này, bạn sẽ tìm hiểu cách sử dụng Cơ học để nhấp vào liên kết, điền vào biểu mẫu và tải tệp lên. Bạn cũng sẽ tìm hiểu cách bạn có thể cắt các đối tượng trang Cơ học và cách tự động hóa tìm kiếm trên Google và lưu kết quả của nó.

Chủ đề

  • Trang đơn so với Phân trang
  • Cơ giới hóa
  • Đại lý
  • Trang
  • Phương pháp Nokogiri
  • Liên kết
  • Nhấp vào
  • Biểu mẫu

Trang đơn so với Phân trang

Cho đến nay, chúng tôi đã dành một ít thời gian để tìm ra cách chúng tôi có thể quét màn hình của một trang bằng cách sử dụng Nokogiri. Đây là cơ sở tốt để tiến lên một bước và học cách trích xuất nội dung từ nhiều trang.

Rốt cuộc, vấn đề mà chúng tôi đang cố gắng giải quyết liên quan đến việc lấy nội dung từ hơn 140 tập — đó là nhiều nội dung hơn mức có thể phù hợp một cách hợp lý với một trang web. Chúng tôi phải làm việc với phân trang và cần phải tìm ra cách để theo dõi nội dung xuống lỗ thỏ.

Đây là nơi Nokogiri dừng lại và một viên ngọc hữu ích khác có tên là Mechanize phát huy tác dụng.

Cơ giới hóa

Cơ khí hóa là một công cụ mạnh mẽ khác có rất nhiều tính năng tốt để cung cấp. Về cơ bản, nó cho phép bạn tự động hóa các tương tác với các trang web mà bạn cần trích xuất nội dung. Theo nghĩa đó, nó nhắc nhở tôi một chút về một số chức năng mà bạn có thể biết khi thử nghiệm với Capybara.

Đừng hiểu sai ý tôi, chơi với Nokogiri trên một trang đơn lẻ là điều tuyệt vời, nhưng đối với các công việc trích xuất dữ liệu phức tạp hơn, chúng tôi cần nhiều mã lực hơn một chút. Về cơ bản, chúng ta có thể thu thập thông tin qua bao nhiêu trang tùy thích và tương tác với các yếu tố của chúng — bắt chước và tự động hóa hành vi của con người. Công cụ khá mạnh mẽ!

Đá quý này cho phép bạn theo dõi các liên kết, điền vào các trường của biểu mẫu và gửi dữ liệu đó — ngay cả việc xử lý cookie cũng có trên bàn. Điều đó có nghĩa là bạn cũng có thể bắt chước đăng nhập của người dùng vào các phiên riêng tư và lấy nội dung từ một trang web mà chỉ bạn mới có quyền truy cập.

Bạn điền thông tin đăng nhập bằng thông tin đăng nhập của mình và cho Mechanize biết cách làm theo. Vì bạn có thể nhấp vào liên kết và gửi biểu mẫu, nên có rất ít điều bạn không thể làm với công cụ này. Nó có mối quan hệ mật thiết với Nokogiri và cũng phụ thuộc vào nó. Aaron Patterson lại là một trong những tác giả của viên ngọc đáng yêu này.

Khởi tạo tác nhân cơ khí hóa

Trước khi có thể bắt đầu cơ giới hóa mọi thứ, chúng ta cần khởi tạo tác nhân Cơ khí hóa.

some_scraper.rb

require 'mechanize'

agent = Mechanize.new

agent này sẽ được sử dụng để tìm nạp một trang, tương tự như những gì chúng tôi đã làm với Nokogiri.

some_scraper.rb

require 'mechanize'

agent = Mechanize.new

podcast_url = "https://betweenscreens.fm/"

page = agent.get(podcast_url)

Điều xảy ra ở đây là tác nhân Cơ khí hóa có trang podcast và cookie của nó.

Trích xuất nội dung trang

Bây giờ chúng tôi có một trang đã sẵn sàng để trích xuất. Trước khi chúng tôi làm như vậy, tôi khuyên chúng tôi nên xem xét kỹ lưỡng bằng cách sử dụng inspect phương pháp.

some_scraper.rb

require 'mechanize'

agent = Mechanize.new

podcast_url = "https://betweenscreens.fm/"

page = agent.get(podcast_url)

puts page.inspect

Kết quả là khá thực chất. Hãy xem và tự mình xem Mechanize::Page là gì đối tượng bao gồm. Tại đây bạn có thể xem tất cả các thuộc tính cho trang đó.

Đối với tôi, đây là một đối tượng thực sự tiện dụng để chia nhỏ dữ liệu bạn muốn trích xuất.

Đầu ra

#<Mechanize::Page
 {url #https://betweenscreens.fm/>}
 {meta_refresh}
 {title "Between | Screens "}
 {iframes
  #<Mechanize::Page::Frame
   nil
   "https://w.soundcloud.com/player/?url=https%3A//api.soundcloud.com/tracks/290328784&color=ff0000&auto...>
  #<Mechanize::Page::Frame
   nil
   "https://w.soundcloud.com/player/?url=https%3A//api.soundcloud.com/tracks/290126141&color=ff0000&auto...>
  #<Mechanize::Page::Frame
   nil
   "https://w.soundcloud.com/player/?url=https%3A//api.soundcloud.com/tracks/289018386&color=ff0000&auto...>
  #<Mechanize::Page::Frame
   nil
   "https://w.soundcloud.com/player/?url=https%3A//api.soundcloud.com/tracks/287425105&color=ff0000&auto...>
  #<Mechanize::Page::Frame
   nil
   "https://w.soundcloud.com/player/?url=https%3A//api.soundcloud.com/tracks/287105342&color=ff0000&auto...>
  #<Mechanize::Page::Frame
   nil
   "https://w.soundcloud.com/player/?url=https%3A//api.soundcloud.com/tracks/221003494&color=ff0000&auto...>
  #<Mechanize::Page::Frame
   nil
   "">https://w.soundcloud.com/player/?url=https%3A//api.soundcloud.com/tracks/218101809&color=ff0000&auto...}
 {frames}
 {links
  #<Mechanize::Page::Link "Logo cube" "/">
  #https://github.com/vis-kid/betweenscreens">
  #<Mechanize::Page::Link "about" "pages/about/">
  #<Mechanize::Page::Link "design" "design/">
  #<Mechanize::Page::Link "code" "code/">
  #<Mechanize::Page::Link "Randy J. Hunt" "episodes/144/">
  #<Mechanize::Page::Link "Jason Long" "episodes/143/">
  #<Mechanize::Page::Link "David Heinemeier Hansson" "episodes/142/">
  #<Mechanize::Page::Link "Zach Holman" "episodes/141/">
  #<Mechanize::Page::Link "Joel Glovier" "episodes/140/">
  #<Mechanize::Page::Link "João Ferreira" "episodes/139/">
  #<Mechanize::Page::Link "Corwin Harrell" "episodes/138/">
  #<Mechanize::Page::Link "Older Stuff »" "page/2/">
  #<Mechanize::Page::Link "Exercise" "/tags/exercise/">
  #<Mechanize::Page::Link "Company benefits" "/tags/company-benefits/">
  #<Mechanize::Page::Link "Tmux" "/tags/tmux/">
  #<Mechanize::Page::Link "FileTask" "/tags/filetask/">
  #<Mechanize::Page::Link "Decision making" "/tags/decision-making/">
  #<Mechanize::Page::Link "Favorite feature" "/tags/favorite-feature/">
  #<Mechanize::Page::Link "Working out" "/tags/working-out/">
  #<Mechanize::Page::Link "Scott Savarie" "/tags/scott-savarie/">
  #<Mechanize::Page::Link "Titles" "/tags/titles/">
  #<Mechanize::Page::Link "Erik Spiekermann" "/tags/erik-spiekermann/">
  #<Mechanize::Page::Link "Newbie mistakes" "/tags/newbie-mistakes/">
  #<Mechanize::Page::Link "Playbook" "/tags/playbook/">
  #<Mechanize::Page::Link "Delegation" "/tags/delegation/">
  #<Mechanize::Page::Link "Heat maps" "/tags/heat-maps/">
  #<Mechanize::Page::Link "Europe" "/tags/europe/">
  #<Mechanize::Page::Link "Sizing type" "/tags/sizing-type/">
  #<Mechanize::Page::Link "Focus" "/tags/focus/">
  #<Mechanize::Page::Link "Virtual assistants" "/tags/virtual-assistants/">
  #<Mechanize::Page::Link "Writing" "/tags/writing/">
  #<Mechanize::Page::Link "Hacking" "/tags/hacking/">
  #<Mechanize::Page::Link "Joel Glovier" "/tags/joel-glovier/">
  #<Mechanize::Page::Link "Corwin Harrell" "/tags/corwin-harrell/">
  #<Mechanize::Page::Link "Mario C. Delgado" "/tags/mario-c-delgado/">
  #<Mechanize::Page::Link "Tom Dale" "/tags/tom-dale/">
  #<Mechanize::Page::Link "Obie Fernandez" "/tags/obie-fernandez/">
  #<Mechanize::Page::Link "Chad Pytel" "/tags/chad-pytel/">
  #<Mechanize::Page::Link "Zach Holman" "/tags/zach-holman/">
  #<Mechanize::Page::Link "Max Luster" "/tags/max-luster/">
  #<Mechanize::Page::Link "Kyle Fiedler" "/tags/kyle-fiedler/">
  #<Mechanize::Page::Link "Roberto Machado" "/tags/roberto-machado/">}
 {forms}>

Nếu bạn muốn xem chính trang HTML, bạn có thể gắn thẻ trên body hoặc content các phương pháp.

some_scraper.rb

...

print page.body

...

Đầu ra

<!doctype html>

<html>
  <head>
    <meta charset="utf-8" />
    <meta http-equiv='X-UA-Compatible' content='IE=edge;chrome=1' />
    <meta content="IE=edge,chrome=1" http-equiv="X-UA-Compatible">
    <meta name="viewport" content="initial-scale=1">
    <title>Between | Screens </title>
    <link rel="alternate" type="application/atom+xml" title="Atom Feed" href="/feed.xml" />
    <link href="stylesheets/all-11b45acc.css" rel="stylesheet" />
    <script src="javascripts/all-4c20da82.js"></script>
  </head>

  <body>
    <header>
      <div id="logo">
        <a href="/"><img src="images/Between_Screens_Logo_Cube_Up-539d6997.svg" alt="Logo cube" /></a>
      </div>
      <nav class="navigation">
        <ul class="nav-list"> 
fork">https://github.com/vis-kid/betweenscreens">fork!
          <li><a href="pages/about/">about</a></li>
          <li><a href="design/">design</a></li>
          <li><a href="code/">code</a></li>
        </ul>
      </nav>
    </header>

    <div id="main" role="main">
      <div class='posts'>
        <ul>
          <li>
            <article class="index-article">
              <span class='post-date'>Oct 27 | 2016</span><h2 class='post-title'><a href="episodes/144/">Randy J. Hunt</a></h2>
              <h3 class='topic-list'>Organizing teams | Diversity | Desires | Pizza rule | Effective over clever | Novel solutions | Straightforwardness | Research | Coffeeshop test | Small changes | Reducing errors | Granular diffs</h3>
              <div class='soundcloud-player-small'>
                <iframe width="100%"
                  height="166"
                  scrolling="no"
                  frameborder="no"
                  src="https://w.soundcloud.com/player/?url=https%3A//api.soundcloud.com/tracks/290328784&color=ff0000&...>
              </div>
            </article>
          </li>

          <li>
            <article class="index-article">
              <span class='post-date'>Oct 25 | 2016</span><h2 class='post-title'><a href="episodes/143/">Jason Long</a></h2>
              <h3 class='topic-list'>Open source | Empathy | Lower barriers | Learning tool | Design contributions | Git website | Branding | GitHub | Neovim | Tmux | Design love | Knowing audiences | Showing work | Dribbble | Progressions | Ideas</h3>
              <div class='soundcloud-player-small'>
                <iframe width="100%"
                height="166"
                scrolling="no"
                frameborder="no"
                src="https://w.soundcloud.com/player/?url=https%3A//api.soundcloud.com/tracks/290126141&color=ff0000&...>
              </div>
            </article>
          </li>

          <li>
            <article class="index-article">
              <span class='post-date'>Oct 18 | 2016</span><h2 class='post-title'><a href="episodes/142/">David Heinemeier Hansson</a></h2>
              <h3 class='topic-list'>Rails community | Tone | Technical disagreements | Community policing | Ungratefulness | No assholes allowed | Basecamp | Open source persona | Aspirations | Guarding motivations | Dealing with audiences | Pressure | Honesty | Diverse opinions | Small talk</h3>
              <div class='soundcloud-player-small'>
                <iframe width="100%"
                height="166"
                scrolling="no"
                frameborder="no"
                src="https://w.soundcloud.com/player/?url=https%3A//api.soundcloud.com/tracks/289018386&color=ff0000&...>
              </div>
            </article>
          </li>

          <li>
            <article class="index-article">
              <span class='post-date'>Oct 12 | 2016</span><h2 class='post-title'><a href="episodes/141/">Zach Holman</a></h2>
              <h3 class='topic-list'>Getting Fired | Taboo | Transparency | Different Perspectives | Timing | Growth Stages | Employment & Dating | Managers | At-will Employment | Tech Industry | Europe | Low hanging Fruits | Performance Improvement Plans | Meeting Goals | Surprise Firings | Firing Fast | Mistakes | Company Culture | Communication</h3>
              <div class='soundcloud-player-small'>  
                <iframe width="100%"
                  height="166"
                  scrolling="no"
                  frameborder="no"
                  src="https://w.soundcloud.com/player/?url=https%3A//api.soundcloud.com/tracks/287425105&color=ff0000&...>
              </div>
            </article>
          </li>

          <li>
            <article class="index-article">
              <span class='post-date'>Oct 10 | 2016</span><h2 class='post-title'><a href="episodes/140/">Joel Glovier</a></h2>
              <h3 class='topic-list'>Digital Product Design | Product Design @ GitHub | Loving Design | Order & Chaos | Drawing | Web Design | HospitalRun | Diversity | Startup Culture | Improving Lives | CURE International | Ember | Offline First | Hospital Information System | Designers & Open Source</h3>
              <div class='soundcloud-player-small'>
                <iframe width="100%"
                  height="166"
                  scrolling="no"
                  frameborder="no"
                  src="https://w.soundcloud.com/player/?url=https%3A//api.soundcloud.com/tracks/287105342&color=ff0000&...>
              </div>
            </article>
          </li>

          <li>
            <article class="index-article">
              <span class='post-date'>Aug 26 | 2015</span><h2 class='post-title'><a href="episodes/139/">João Ferreira</a></h2>
              <h3 class='topic-list'>Masters @ Work | Subvisual | Deadlines | Design personality | Design problems | Team | Pushing envelopes | Delightful experiences | Perfecting details | Company values</h3>
              <div class='soundcloud-player-small'>
                <iframe width="100%"
                height="166"
                scrolling="no"
                frameborder="no"
                src="https://w.soundcloud.com/player/?url=https%3A//api.soundcloud.com/tracks/221003494&color=ff0000&...>
              </div>
            </article>
          </li>

          <li>
            <article class="index-article">
              <span class='post-date'>Aug 06 | 2015</span><h2 class='post-title'><a href="episodes/138/">Corwin Harrell</a></h2>
              <h3 class='topic-list'>Q&A | 01 | University | Graphic design | Design setup | Sublime | Atom | thoughtbot | Working location | Collaboration & pairing | Vim advocates | Daily routine | Standups | Clients | Coffee walks | Investment Fridays |</h3>
              <div class='soundcloud-player-small'>
                <iframe width="100%"
                height="166"
                scrolling="no"
                frameborder="no"
                src="https://w.soundcloud.com/player/?url=https%3A//api.soundcloud.com/tracks/218101809&color=ff0000&...>
              </div>
            </article>
          </li>
        </ul>
      </div>

      <section>
        <div class='pagination-link'><a href="page/2/">Older Stuff »</a></div>
      </section>
    </div>

    <footer>
      <div class='footer-tags'>
        <h3>Random Tags</h3>
        <ul class='random-tag-list'>
          <li><a href="/tags/exercise/">Exercise</a></li>
          <li><a href="/tags/company-benefits/">Company benefits</a></li>
          <li><a href="/tags/tmux/">Tmux</a></li>
          <li><a href="/tags/filetask/">FileTask</a></li>
          <li><a href="/tags/decision-making/">Decision making</a></li>
          <li><a href="/tags/favorite-feature/">Favorite feature</a></li>
          <li><a href="/tags/working-out/">Working out</a></li>
          <li><a href="/tags/scott-savarie/">Scott Savarie</a></li>
          <li><a href="/tags/titles/">Titles</a></li>
          <li><a href="/tags/erik-spiekermann/">Erik Spiekermann</a></li>
          <li><a href="/tags/newbie-mistakes/">Newbie mistakes</a></li>
          <li><a href="/tags/playbook/">Playbook</a></li>
          <li><a href="/tags/delegation/">Delegation</a></li>
          <li><a href="/tags/heat-maps/">Heat maps</a></li>
          <li><a href="/tags/europe/">Europe</a></li>
          <li><a href="/tags/sizing-type/">Sizing type</a></li>
          <li><a href="/tags/focus/">Focus</a></li>
          <li><a href="/tags/virtual-assistants/">Virtual assistants</a></li>
          <li><a href="/tags/writing/">Writing</a></li>
          <li><a href="/tags/hacking/">Hacking</a></li>
        </ul>
      </div>

      <div class='recent-posts'>
        <h3>Random Interviewees</h3>
        <ul>
          <li><a href="/tags/joel-glovier/">Joel Glovier</a></li>
          <li><a href="/tags/corwin-harrell/">Corwin Harrell</a></li>
          <li><a href="/tags/mario-c-delgado/">Mario C. Delgado</a></li>
          <li><a href="/tags/tom-dale/">Tom Dale</a></li>
          <li><a href="/tags/obie-fernandez/">Obie Fernandez</a></li>
          <li><a href="/tags/chad-pytel/">Chad Pytel</a></li>
          <li><a href="/tags/zach-holman/">Zach Holman</a></li>
          <li><a href="/tags/max-luster/">Max Luster</a></li>
          <li><a href="/tags/kyle-fiedler/">Kyle Fiedler</a></li>
          <li><a href="/tags/roberto-machado/">Roberto Machado</a></li>
        </ul>
      </div>
    </footer>
  </body>
</html>

Vì podcast này chỉ có một số lượng nhỏ các phần tử khác nhau trên trang, đây là Mechanize::Page được trả lại từ github.com. Nó có nhiều nội dung hơn để xem. Tôi nghĩ điều này là quan trọng để có được cảm nhận.

Xuất github.com

#<Mechanize::Page
 {url #https://github.com/>}
 {meta_refresh}
 {title "How people build software · GitHub"}
 {iframes}
 {frames}
 {links
  #<Mechanize::Page::Link "Skip to content" "#start-of-content">
  #https://github.com/">
  #<Mechanize::Page::Link "\n          Personal\n" "/personal">
  #<Mechanize::Page::Link "\n          Open source\n" "/open-source">
  #<Mechanize::Page::Link "\n          Business\n" "/business">
  #<Mechanize::Page::Link "\n          Explore\n" "/explore">
  #<Mechanize::Page::Link "Sign up" "/join?source=header-home">
  #<Mechanize::Page::Link "Sign in" "/login">
  #<Mechanize::Page::Link "Pricing" "/pricing">
  #<Mechanize::Page::Link "Blog" "/blog">
  #https://help.github.com">
  #https://github.com/search">
  #https://help.github.com/terms">
  #https://help.github.com/privacy">
  #<Mechanize::Page::Link "Sign up for GitHub" "/join?source=button-home">
  #<Mechanize::Page::Link
   "\n      \n        \n      \n      \n        A whole new Universe\n        \n          Learn about the exciting features and announcements revealed at this year's GitHub Universe conference.\n        \n      \n    "
   "/universe-2016">
  #<Mechanize::Page::Link "Individuals " "/personal">
  #<Mechanize::Page::Link "Communities " "/open-source">
  #<Mechanize::Page::Link "Businesses " "/business">
  #<Mechanize::Page::Link "NASA" "//github.com/nasa">
  #<Mechanize::Page::Link "Sign up for GitHub" "/join?source=button-home">
  #https://github.com/contact">
  #https://developer.github.com">
  #https://training.github.com">
  #https://shop.github.com">
  #https://github.com/blog">
  #https://github.com/about">
  #https://github.com">
  #https://github.com/site/terms">
  #https://github.com/site/privacy">
  #https://github.com/security">
  #https://status.github.com/">
  #https://help.github.com">
  #<Mechanize::Page::Link "Reload" "">
  #<Mechanize::Page::Link "Reload" "">}
 {forms
  #<Mechanize::Form
   {name nil}
   {method "GET"}
   {action "/search"}
   {fields
    [hidden:0x3feb90f8297c type: hidden name: utf8 value: ✓]
    [text:0x3feb90f827d8 type: text name: q value: ]}
   {radiobuttons}
   {checkboxes}
   {file_uploads}
   {buttons}>
  #<Mechanize::Form
   {name nil}
   {method "POST"}
   {action "/join"}
   {fields
    [hidden:0x3feb90f7be38 type: hidden name: utf8 value: ✓]
    [hidden:0x3feb90f7bbb8 type: hidden name: authenticity_token value: vjRATKj7smXreq6Lt02r+MzW+ewWoi+fRzQXPedFAlOZgwzxQ0dZnChirhDfd7vyWZZZBO+ZFydLNedjIEDsrQ==]
    [text:0x3feb90f7b9d8 type: text name: user[login] value: ]
    [text:0x3feb90f7b7f8 type: text name: user[email] value: ]
    [field:0x3feb90f7b654 type: password name: user[password] value: ]
    [hidden:0x3feb90f7b474 type: hidden name: source value: form-home]}
   {radiobuttons}
   {checkboxes}
   {file_uploads}
   {buttons [button:0x3feb90f7a038 type: submit name:  value: ]}>}>

Quay lại podcast, bạn cũng có thể xem những thứ như mã hóa, mã phản hồi HTTP, URI hoặc tiêu đề phản hồi.

some_scraper.rb

require 'mechanize'

agent = Mechanize.new

podcast_url = "https://betweenscreens.fm/"

page = agent.get(podcast_url)

puts 'Encodings'
puts page.encodings
puts 'Repsonse Headers'
puts page.response
puts 'HTTP response code'
puts page.code
puts 'URI'
puts page.uri

Đầu ra

Encodings
EUC-JP
utf-8
utf-8

Repsonse Headers
{"server"=>"GitHub.com", "date"=>"Sat, 29 Oct 2016 17:56:00 GMT", "content-type"=>"text/html; charset=utf-8", "transfer-encoding"=>"chunked", "last-modified"=>"Fri, 28 Oct 2016 01:48:56 GMT", "access-control-allow-origin"=>"*", "expires"=>"Sat, 29 Oct 2016 18:06:00 GMT", "cache-control"=>"max-age=600", "content-encoding"=>"gzip", "x-github-request-id"=>"501C936D:C723:1631523C:5814E2B0"}

HTTP response code
200

URI
https://betweenscreens.fm/

Có rất nhiều thứ khác nếu bạn muốn tìm hiểu sâu hơn. Tôi sẽ để nó ở đó.

Phương pháp Nokogiri

  • at
  • search

Cơ khí hóa sử dụng Nokogiri để loại bỏ dữ liệu từ các trang. Bạn có thể áp dụng những gì bạn đã học về Nokogiri trong bài viết đầu tiên và sử dụng nó trên các trang Cơ khí hóa. Điều đó có nghĩa là bạn thường sử dụng Cơ học để điều hướng các trang và các phương pháp Nokogiri cho nhu cầu tìm kiếm của mình.

Ví dụ:nếu bạn muốn tìm kiếm một đối tượng, bạn có thể sử dụng at , trong khi search trả về tất cả các đối tượng sẽ khớp với một bộ chọn trên một trang cụ thể. Để diễn đạt lại điều đó, các phương pháp này sẽ hoạt động trên cả các đối tượng tài liệu Nokogiri và Cơ giới hóa các đối tượng trang.

some_scraper.rb

require 'mechanize'

agent = Mechanize.new

podcast_url = "https://betweenscreens.fm/"

page = agent.get(podcast_url)

first_title = page.at('h2.post-title')

all_titles = page.search('h2.post-title')

all_titles.each do |title|
  puts title
end

puts " * "*33

puts first_title

Đầu ra

<h2 class="post-title"><a href="episodes/144/">Randy J. Hunt</a></h2>
<h2 class="post-title"><a href="episodes/143/">Jason Long</a></h2>
<h2 class="post-title"><a href="episodes/142/">David Heinemeier Hansson</a></h2>
<h2 class="post-title"><a href="episodes/141/">Zach Holman</a></h2>
<h2 class="post-title"><a href="episodes/140/">Joel Glovier</a></h2>
<h2 class="post-title"><a href="episodes/139/">João Ferreira</a></h2>
<h2 class="post-title"><a href="episodes/138/">Corwin Harrell</a></h2>
 *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  * 
<h2 class="post-title"><a href="episodes/144/">Randy J. Hunt</a></h2>

Liên kết

  • links
  • link_with
  • links_with

Chúng tôi cũng có thể điều hướng toàn bộ trang web theo ý thích của mình. Có lẽ phần quan trọng nhất của Mechanize là khả năng cho phép bạn chơi với các liên kết. Nếu không, bạn có thể tự mình gắn bó với Nokogiri. Hãy xem những gì chúng tôi nhận được sẽ trả lại nếu chúng tôi yêu cầu một trang cung cấp các liên kết của nó.

some_scraper.rb

require 'mechanize'

agent = Mechanize.new

podcast_url = "https://betweenscreens.fm/"

page = agent.get(podcast_url)

puts "#{page.links}"

Đầu ra

[#<Mechanize::Page::Link "Logo cube" "/">
, #https://github.com/vis-kid/betweenscreens">
, #<Mechanize::Page::Link "about" "pages/about/">
, #<Mechanize::Page::Link "design" "design/">
, #<Mechanize::Page::Link "code" "code/">
, #<Mechanize::Page::Link "Randy J. Hunt" "episodes/144/">
, #<Mechanize::Page::Link "Jason Long" "episodes/143/">
, #<Mechanize::Page::Link "David Heinemeier Hansson" "episodes/142/">
, #<Mechanize::Page::Link "Zach Holman" "episodes/141/">
, #<Mechanize::Page::Link "Joel Glovier" "episodes/140/">
, #<Mechanize::Page::Link "João Ferreira" "episodes/139/">
, #<Mechanize::Page::Link "Corwin Harrell" "episodes/138/">
, #<Mechanize::Page::Link "Older Stuff »" "page/2/">
, #<Mechanize::Page::Link "Exercise" "/tags/exercise/">
, #<Mechanize::Page::Link "Company benefits" "/tags/company-benefits/">
, #<Mechanize::Page::Link "Tmux" "/tags/tmux/">
, #<Mechanize::Page::Link "FileTask" "/tags/filetask/">
, #<Mechanize::Page::Link "Decision making" "/tags/decision-making/">
, #<Mechanize::Page::Link "Favorite feature" "/tags/favorite-feature/">
, #<Mechanize::Page::Link "Working out" "/tags/working-out/">
, #<Mechanize::Page::Link "Scott Savarie" "/tags/scott-savarie/">
, #<Mechanize::Page::Link "Titles" "/tags/titles/">
, #<Mechanize::Page::Link "Erik Spiekermann" "/tags/erik-spiekermann/">
, #<Mechanize::Page::Link "Newbie mistakes" "/tags/newbie-mistakes/">
, #<Mechanize::Page::Link "Playbook" "/tags/playbook/">
, #<Mechanize::Page::Link "Delegation" "/tags/delegation/">
, #<Mechanize::Page::Link "Heat maps" "/tags/heat-maps/">
, #<Mechanize::Page::Link "Europe" "/tags/europe/">
, #<Mechanize::Page::Link "Sizing type" "/tags/sizing-type/">
, #<Mechanize::Page::Link "Focus" "/tags/focus/">
, #<Mechanize::Page::Link "Virtual assistants" "/tags/virtual-assistants/">
, #<Mechanize::Page::Link "Writing" "/tags/writing/">
, #<Mechanize::Page::Link "Hacking" "/tags/hacking/">
, #<Mechanize::Page::Link "Joel Glovier" "/tags/joel-glovier/">
, #<Mechanize::Page::Link "Corwin Harrell" "/tags/corwin-harrell/">
, #<Mechanize::Page::Link "Mario C. Delgado" "/tags/mario-c-delgado/">
, #<Mechanize::Page::Link "Tom Dale" "/tags/tom-dale/">
, #<Mechanize::Page::Link "Obie Fernandez" "/tags/obie-fernandez/">
, #<Mechanize::Page::Link "Chad Pytel" "/tags/chad-pytel/">
, #<Mechanize::Page::Link "Zach Holman" "/tags/zach-holman/">
, #<Mechanize::Page::Link "Max Luster" "/tags/max-luster/">
, #<Mechanize::Page::Link "Kyle Fiedler" "/tags/kyle-fiedler/">
, #<Mechanize::Page::Link "Roberto Machado" "/tags/roberto-machado/">
]

Chúa ơi, hãy phá vỡ điều này. Vì chúng tôi đã không yêu cầu Mechanize tìm kiếm ở nơi khác, chúng tôi nhận được một loạt các liên kết chỉ từ trang đầu tiên đó. Mechanize đi qua trang đó theo thứ tự giảm dần và trả về cho bạn danh sách các liên kết này từ trên xuống dưới. Tôi đã tạo một hình ảnh nhỏ với các con trỏ màu xanh lục đến các liên kết khác nhau mà bạn có thể thấy trong đầu ra.

Nhân tiện, điều này đã cho bạn thấy kết quả cuối cùng của việc thiết kế lại cho podcast của tôi. Tôi nghĩ phiên bản này tốt hơn một chút cho mục đích trình diễn. Bạn cũng có thể hiểu được kết quả cuối cùng như thế nào và lý do tại sao tôi cần loại bỏ trang Sinatra cũ của mình.

Ảnh chụp màn hình

Xây dựng công cụ quét trang web đầu tiên của bạn, Phần 2 Xây dựng công cụ quét trang web đầu tiên của bạn, Phần 2 Xây dựng công cụ quét trang web đầu tiên của bạn, Phần 2

Như mọi khi, chúng tôi cũng có thể chỉ trích xuất văn bản từ đó.

some_scraper.rb

require 'mechanize'

agent = Mechanize.new

podcast_url = "https://betweenscreens.fm/"

page = agent.get(podcast_url)

page.links.each do |link|
  puts link.text
end

Đầu ra

Logo cube
fork!
about
design
code
Randy J. Hunt
Jason Long
David Heinemeier Hansson
Zach Holman
Joel Glovier
João Ferreira
Corwin Harrell
Older Stuff »
Exercise
Company benefits
Tmux
FileTask
Decision making
Favorite feature
Working out
Scott Savarie
Titles
Erik Spiekermann
Newbie mistakes
Playbook
Delegation
Heat maps
Europe
Sizing type
Focus
Virtual assistants
Writing
Hacking
Joel Glovier
Corwin Harrell
Mario C. Delgado
Tom Dale
Obie Fernandez
Chad Pytel
Zach Holman
Max Luster
Kyle Fiedler
Roberto Machado

Nhận hàng loạt các liên kết này có thể rất hữu ích hoặc đơn giản là tẻ nhạt. May mắn cho chúng tôi, chúng tôi có một vài công cụ để tinh chỉnh những gì chúng tôi cần.

some_scraper.rb

require 'mechanize'

agent = Mechanize.new

podcast_url = "https://betweenscreens.fm/"

page = agent.get(podcast_url)

focus_link = agent.page.links.find { |link| link.text == 'Focus' }

puts focus_link

Đầu ra

Focus

Bùm! Bây giờ chúng tôi đang nhận được ở đâu đó! Chúng tôi có thể phóng to các liên kết cụ thể như vậy. Chúng tôi có thể nhắm mục tiêu các liên kết phù hợp với một tiêu chí nhất định — chẳng hạn như văn bản của nó — với một API đẹp hơn như links_with hoặc link_with . Ngoài ra, nếu chúng ta có nhiều Focus liên kết, chúng tôi có thể phóng to một số cụ thể trên trang bằng cách sử dụng dấu ngoặc nhọn [] .

some_scraper.rb

require 'mechanize'

agent = Mechanize.new

podcast_url = "https://betweenscreens.fm/"

page = agent.get(podcast_url)

focus_link = agent.page.links_with(:text => 'Focus')[2]

puts focus_link

Nếu bạn không theo sau văn bản liên kết mà là chính liên kết, bạn chỉ cần chỉ định một href cụ thể để tìm liên kết đó. Cơ giới hóa sẽ không cản đường bạn. Thay vì text , bạn cung cấp các phương thức bằng href .

some_scraper.rb

page = agent.page.link_with(href: '/episodes/95/')

page = agent.page.links_with(href: '/episodes/95/')

Nếu bạn chỉ muốn tìm liên kết đầu tiên với văn bản mong muốn, bạn cũng có thể sử dụng cú pháp này. Rất tiện lợi và dễ đọc hơn một chút.

some_scraper.rb

focus_links = agent.page.link_with(:text => 'Focus')

Còn việc theo dõi gã đó và xem điều gì ẩn đằng sau Focus này liên kết? Hãy click nó!

Nhấp vào

some_scraper.rb

require 'mechanize'

agent = Mechanize.new

podcast_url = "https://betweenscreens.fm/"

page = agent.get(podcast_url)

focus_links = agent.page.links.find { |link| link.text == 'Focus' }.click.links

puts focus_links

Điều này sẽ mang lại cho chúng tôi một danh sách dài các liên kết như trước đây. Xem việc kết hợp .click.links dễ dàng như thế nào . Cơ học nhấp vào liên kết cho bạn và theo trang đến đích mới. Vì chúng tôi cũng đã yêu cầu một danh sách các liên kết, chúng tôi sẽ nhận được tất cả các liên kết mà Mechanize có thể tìm thấy trên trang mới đó.

Giả sử tôi có hai liên kết văn bản của cùng một người được phỏng vấn — một liên kết đến các thẻ và một liên kết đến một tập gần đây — và tôi muốn nhận các liên kết từ mỗi trang này.

some_scraper.rb

require 'mechanize'

agent = Mechanize.new

podcast_url = "https://betweenscreens.fm/"

page = agent.get(podcast_url)

links = agent.page.links_with(text: "Some interviewee")

links.each do |link|
  puts link.click.links
end

Điều này sẽ cung cấp cho bạn một danh sách các liên kết cho cả hai trang. Bạn lặp lại từng liên kết cho người được phỏng vấn và Mechanize đi theo liên kết đã nhấp và thu thập các liên kết mà nó tìm thấy trên trang mới cho bạn. Dưới đây, bạn có thể tìm thấy một vài ví dụ, nơi bạn có thể so sánh các kết hợp để giúp bạn bắt đầu.

some_scraper.rb

agent.page.links.find { |l| l.text == 'Focus' }
agent.page.links.find { |l| l.text == 'Focus' }.click
agent.page.link_with(text: 'Focus')
agent.page.links_with(text: 'Focus')[0]
agent.page.links_with(text: 'Focus')[1].click
agent.page.links_with(text: 'Focus')[2].click.links
agent.page.link_with(href: '/some-href')
agent.page.link_with(href: '/some-href').click
agent.page.links_with(href: '/some-href')
agent.page.links_with(href: '/some-href').click

Biểu mẫu

  • submit
  • field_with
  • checkbox_with
  • radiobuttons_with
  • file_uploads

Hãy xem các biểu mẫu!

some_scraper.rb

require 'mechanize'

agent = Mechanize.new

google_url = "https://google.com/"

page = agent.get(google_url)

forms = page.forms

puts forms.inspect

Đầu ra

[#<Mechanize::Form
# Attention!!
 {name "f"}
# Attention!!
 {method "GET"}
 {action "/search"}
 {fields
  [hidden:0x3fea91d2eb08 type: hidden name: ie value: ISO-8859-1]
  [hidden:0x3fea91d2e964 type: hidden name: hl value: es]
  [hidden:0x3fea91d2e7e8 type: hidden name: source value: hp]
  [hidden:0x3fea91d2e5f4 type: hidden name: biw value: ]
  [hidden:0x3fea91d2e428 type: hidden name: bih value: ]
# Attention!!
  [text:0x3fea91d2e248 type:  name: q value: ]
# Attention!!
  [hidden:0x3fea91d2bcb4 type: hidden name: gbv value: 1]}
 {radiobuttons}
 {checkboxes}
 {file_uploads}
 {buttons
  [submit:0x3fea91d2e0f4 type: submit name: btnG value: Buscar con Google]
  [submit:0x3fea91d2be80 type: submit name: btnI value: Voy a tener suerte]}>
]

Because we use the forms method, we get an array returned—even when we only have one form returned to us. Now that we know that the form has the name "f" , we can use the singular version form to hone in on that one.

...

{name "f"}

...

some_scraper.rb

require 'mechanize'

agent = Mechanize.new

google_url = "https://google.com/"

page = agent.get(google_url)

search_form = page.form('f')

puts search_form.inspect

Using form('f') , we singled out the particular form we want to work with. As a result, we will not get an array returned.

Đầu ra

#<Mechanize::Form
# Attention!!
 {name "f"}
# Attention!!
 {method "GET"}
 {action "/search"}
 {fields
  [hidden:0x3ffe9ce85ba4 type: hidden name: ie value: ISO-8859-1]
  [hidden:0x3ffe9ce859d8 type: hidden name: hl value: es]
  [hidden:0x3ffe9ce857bc type: hidden name: source value: hp]
  [hidden:0x3ffe9ce85618 type: hidden name: biw value: ]
  [hidden:0x3ffe9ce853e8 type: hidden name: bih value: ]
# Attention!!
  [text:0x3ffe9ce851cc type:  name: q value: ]
# Attention!!
  [hidden:0x3ffe9ce84bdc type: hidden name: gbv value: 1]}
 {radiobuttons}
 {checkboxes}
 {file_uploads}
 {buttons
  [submit:0x3ffe9ce85078 type: submit name: btnG value: Buscar con Google]
  [submit:0x3ffe9ce84e48 type: submit name: btnI value: Voy a tener suerte]}>

We can also identify the name of the text input field (q ).

...

[text:0x3ffe9ce851cc type:  name: q value: ]

...

We can target it by that name and set its value like Ruby attributes. All we need to do is provide it with a new value. You can see from the output example above that it is empty by default.

some_scraper.rb

require 'mechanize'

agent = Mechanize.new

google_url = "https://google.com/"

page = agent.get(google_url)

search_form = page.form('f')
search_form.q = 'New Google Search'

puts search_form.inspect

Đầu ra

#<Mechanize::Form
 {name "f"}
 {method "GET"}
 {action "/search"}
 {fields
  [hidden:0x3fcb85b6a784 type: hidden name: ie value: ISO-8859-1]
  [hidden:0x3fcb85b6a57c type: hidden name: hl value: es]
  [hidden:0x3fcb85b6a3b0 type: hidden name: source value: hp]
  [hidden:0x3fcb85b6a16c type: hidden name: biw value: ]
  [hidden:0x3fcb85b67f20 type: hidden name: bih value: ]
# Attention!!
  [text:0x3fcb85b67d18 type:  name: q value: New Google Search]
# Attention!!
  [hidden:0x3fcb85b67728 type: hidden name: gbv value: 1]}
 {radiobuttons}
 {checkboxes}
 {file_uploads}
 {buttons
  [submit:0x3fcb85b67b9c type: submit name: btnG value: Buscar con Google]
  [submit:0x3fcb85b67994 type: submit name: btnI value: Voy a tener suerte]}>

As you can observe above, the value for the text field has changed to New Google Search . Now we only need to submit the form and collect the results from the page that Google returns. It couldn’t be any easier. Let’s search for something else this time!

some_scraper.rb

require 'mechanize'

agent = Mechanize.new

google_url = "https://google.com/"
page = agent.get(google_url)

search_form = page.form('f')
search_form.q = 'GitHub TouchFart'

page = agent.submit(search_form)

pp page.search('h3.r').map(&:text)

Here I identified the search results header using a CSS selector h3.r , mapped its text , and pretty printed the results. Wasn’t that hard, was it? That is an easy example, sure, but think about the endless possibilities you have at your disposal with this!

Đầu ra

["GitHub - hungtruong/TouchFart: A fart app for the new Macbook ...",
 "TouchFart/TouchFart at master · hungtruong/TouchFart · GitHub",
 "Commits · hungtruong/TouchFart · GitHub",
 "Projects · hungtruong/TouchFart · GitHub",
 "Pull Requests · hungtruong/TouchFart · GitHub",
 "Issues · hungtruong/TouchFart · GitHub",
 "TouchFart/license.txt at master · hungtruong/TouchFart · GitHub",
 "Add autoplay attribute to <audio> tag and touchfart (er ... - GitHub",
 "Find file - File Finder · GitHub",
 "Fart app for the new Macbook Pro's Touch... #3860 on topic touchfart ..."]

Mechanize has different input fields available for you to play with. You can even upload files!

  • field_with
  • checkbox_with
  • radiobuttons_with
  • file_uploads

You can also identify radio buttons and checkboxes by their name and check them with—you guessed it—check .

some_scraper.rb

form.radiobuttons_with(:name => 'gender')[3].check

form.checkbox_with(:name => 'coder').check

Option tags offer users to select one item from a drop-down list. Again, we target them by name and select the option number we want.

some_scraper.rb

form.field_with(:name => 'countries').options[22].select

File uploads work similar to inputing text into forms by setting it like Ruby attributes. You identify the upload field and then specify the file path (file name) you want to transfer. It sounds more complicated than it is. Let’s have a look!

some_scraper.rb

form.file_uploads.first.file_name = "some-path/some-image.jpg"

Lời kết

See, no magic after all! You are now well equipped to have some fun on your own. There is certainly a bit more to learn about Nokogiri and Mechanize, but instead of spending too much time on unnecessary aspects, play around with it and look into some more documentation when you run into problems beyond the scope of a beginner article.

I hope you can see how beautifully simple this gem is and how much power it offers. As we all know from popular culture by now, this also bears responsibilities. Use it within legal frameworks and when you have no access to an API. You probably won’t have a frequent use for these tools, but boy do they come in handy when you have some real scraping needs ahead of you.

As promised, in the next article we will cover a real-world example where I will scrape data from my podcast site. I will extract it from an old Sinatra site and transfer it over to my new Middleman site that uses .markdown files for each episode. We will extract the dates, episodes numbers, interviewee names, headers, subheaders, and so on. See you there!