Advertisement
  1. Code
  2. Ruby

Xây dựng Trình Thu thập Thông tin Trang Web Đầu tiên Của bạn, Phần 2

Scroll to top
Read Time: 26 min

Vietnamese (Tiếng Việt) translation by Dai Phong (you can also view the original English article)

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

Các chủ đề

  • Trang đơn và Phân trang
  • Mechanize
  • Agent
  • Page
  • Các phương thức Nokogiri
  • Liên kết
  • Nhấp chuột
  • Form

Trang đơn và Phân trang

Cho đến lúc này, chúng ta đã dành nhiều thời gian để tìm ra cách có thể thu thập thông tin màn hình của một trang đơn bằng Nokogiri. Đây là một bước đệm để chuyển lên một bước cao hơn và học cách để trích xuất nội dung từ nhiều trang.

Suy cho cùng, vấn đề mà chúng ta đang cố gắng giải quyết liên quan đến việc lấy về nội dung từ hơn 140 tập - nghĩa là toàn bộ nội dụng không thể vừa với một trang. Chúng ta phải làm việc với phân trang và cần tìm ra cách để lần theo nội dung để đến các trang tiếp theo.

Sứ mệnh của Nokogiri đến đây là kết thúc và một gem hữu ích khác được gọi là Mechanize bắt đầu sứ mệnh của nó.

Mechanize

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

Xin đừng hiểu lầm, làm việc với Nokogiri trên một trang đơn là điều tuyệt vời, nhưng để trích xuất nhiều dữ liệu hơn chúng ta cần thêm một chút sức mạnh. Về cơ bản, chúng ta có thể thu thập thông tin từ bao nhiêu trang cũng được và tương tác với các thành phần của chúng - bắt chước và tự động hóa hành vi của con người. Một công cụ khá mạnh mẽ!

Gem này cho phép bạn nhấp vào các liên kết, điền vào các trường của form và gửi dữ liệu đó - thậm chí là làm việc với cookie. Điều đó có nghĩa là bạn còn có thể bắt chước người dùng đăng nhập vào các session riêng tư và lấy nội dung từ một trang web chỉ khi bạn có thể truy cập.

Bạn điền thông tin đăng nhập bằng các thông tin xác thực của bạn và nói cho Mechanize biết phải làm thế nào. Với việc có thể nhấp vào liên kết và submit form, nên không có gì mà bạn không thể làm với công cụ này. Nó có một mối quan hệ gần gũi với Nokogiri và cũng phụ thuộc vào nó. Aaron Patterson một lần nữa là một trong những người tạo ra gem đáng yêu này.

Khởi tạo một Mechanize Agent

Trước khi chúng ta có thể bắt đầu làm việc với Mechanize, chúng ta cần phải khởi tạo một Mechanize Agent.

some_scraper.rb

1
require 'mechanize'
2
3
agent = Mechanize.new

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

some_scraper.rb

1
require 'mechanize'
2
3
agent = Mechanize.new
4
5
podcast_url = "http://betweenscreens.fm/"
6
7
page = agent.get(podcast_url)

Những gì xảy ra ở đây là Mechanize agent nhận trang podcast và các cookie của nó.

Trích xuất Nội dung của Trang

Bây giờ chúng ta có một trang sẵn sàng để trích xuất. Trước khi chúng ta làm điều đó, tôi khuyên bạn nên xem xét những gì xảy ra bên trong bằng phương thức inspect.

some_scraper.rb

1
require 'mechanize'
2
3
agent = Mechanize.new
4
5
podcast_url = "http://betweenscreens.fm/"
6
7
page = agent.get(podcast_url)
8
9
puts page.inspect

Đầu ra khá rời rạc. Hãy tự mình quan sát và xem đối tượng Mechanize::Page bao gồm những gì. Ở đây bạn có thể nhìn thấy tất cả các thuộc tính cho trang đó.

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

Đầu ra

1
#<Mechanize::Page

2
 {url #http://betweenscreens.fm/>}

3
 {meta_refresh}
4
 {title "Between | Screens "}
5
 {iframes
6
  #<Mechanize::Page::Frame

7
   nil
8
   "https://w.soundcloud.com/player/?url=https%3A//api.soundcloud.com/tracks/290328784&color=ff0000&auto...>

9
  #<Mechanize::Page::Frame

10
   nil

11
   "https://w.soundcloud.com/player/?url=https%3A//api.soundcloud.com/tracks/290126141&color=ff0000&auto...>
12
  #<Mechanize::Page::Frame

13
   nil
14
   "https://w.soundcloud.com/player/?url=https%3A//api.soundcloud.com/tracks/289018386&color=ff0000&auto...>

15
  #<Mechanize::Page::Frame

16
   nil

17
   "https://w.soundcloud.com/player/?url=https%3A//api.soundcloud.com/tracks/287425105&color=ff0000&auto...>
18
  #<Mechanize::Page::Frame

19
   nil
20
   "https://w.soundcloud.com/player/?url=https%3A//api.soundcloud.com/tracks/287105342&color=ff0000&auto...>

21
  #<Mechanize::Page::Frame

22
   nil

23
   "https://w.soundcloud.com/player/?url=https%3A//api.soundcloud.com/tracks/221003494&color=ff0000&auto...>
24
  #<Mechanize::Page::Frame

25
   nil
26
   "">https://w.soundcloud.com/player/?url=https%3A//api.soundcloud.com/tracks/218101809&color=ff0000&auto...}
27
 {frames}
28
 {links
29
  #<Mechanize::Page::Link "Logo cube" "/">

30
  #https://github.com/vis-kid/betweenscreens">

31
  #<Mechanize::Page::Link "about" "pages/about/">

32
  #<Mechanize::Page::Link "design" "design/">

33
  #<Mechanize::Page::Link "code" "code/">

34
  #<Mechanize::Page::Link "Randy J. Hunt" "episodes/144/">

35
  #<Mechanize::Page::Link "Jason Long" "episodes/143/">

36
  #<Mechanize::Page::Link "David Heinemeier Hansson" "episodes/142/">

37
  #<Mechanize::Page::Link "Zach Holman" "episodes/141/">

38
  #<Mechanize::Page::Link "Joel Glovier" "episodes/140/">

39
  #<Mechanize::Page::Link "João Ferreira" "episodes/139/">

40
  #<Mechanize::Page::Link "Corwin Harrell" "episodes/138/">

41
  #<Mechanize::Page::Link "Older Stuff »" "page/2/">

42
  #<Mechanize::Page::Link "Exercise" "/tags/exercise/">

43
  #<Mechanize::Page::Link "Company benefits" "/tags/company-benefits/">

44
  #<Mechanize::Page::Link "Tmux" "/tags/tmux/">

45
  #<Mechanize::Page::Link "FileTask" "/tags/filetask/">

46
  #<Mechanize::Page::Link "Decision making" "/tags/decision-making/">

47
  #<Mechanize::Page::Link "Favorite feature" "/tags/favorite-feature/">

48
  #<Mechanize::Page::Link "Working out" "/tags/working-out/">

49
  #<Mechanize::Page::Link "Scott Savarie" "/tags/scott-savarie/">

50
  #<Mechanize::Page::Link "Titles" "/tags/titles/">

51
  #<Mechanize::Page::Link "Erik Spiekermann" "/tags/erik-spiekermann/">

52
  #<Mechanize::Page::Link "Newbie mistakes" "/tags/newbie-mistakes/">

53
  #<Mechanize::Page::Link "Playbook" "/tags/playbook/">

54
  #<Mechanize::Page::Link "Delegation" "/tags/delegation/">

55
  #<Mechanize::Page::Link "Heat maps" "/tags/heat-maps/">

56
  #<Mechanize::Page::Link "Europe" "/tags/europe/">

57
  #<Mechanize::Page::Link "Sizing type" "/tags/sizing-type/">

58
  #<Mechanize::Page::Link "Focus" "/tags/focus/">

59
  #<Mechanize::Page::Link "Virtual assistants" "/tags/virtual-assistants/">

60
  #<Mechanize::Page::Link "Writing" "/tags/writing/">

61
  #<Mechanize::Page::Link "Hacking" "/tags/hacking/">

62
  #<Mechanize::Page::Link "Joel Glovier" "/tags/joel-glovier/">

63
  #<Mechanize::Page::Link "Corwin Harrell" "/tags/corwin-harrell/">

64
  #<Mechanize::Page::Link "Mario C. Delgado" "/tags/mario-c-delgado/">

65
  #<Mechanize::Page::Link "Tom Dale" "/tags/tom-dale/">

66
  #<Mechanize::Page::Link "Obie Fernandez" "/tags/obie-fernandez/">

67
  #<Mechanize::Page::Link "Chad Pytel" "/tags/chad-pytel/">

68
  #<Mechanize::Page::Link "Zach Holman" "/tags/zach-holman/">

69
  #<Mechanize::Page::Link "Max Luster" "/tags/max-luster/">

70
  #<Mechanize::Page::Link "Kyle Fiedler" "/tags/kyle-fiedler/">

71
  #<Mechanize::Page::Link "Roberto Machado" "/tags/roberto-machado/">}

72
 {forms}>

Nếu bạn muốn quan sát bản thân trang HTML, bạn có thể gắn các phương thức body hoặc content.

some_scraper.rb

1
...
2
3
print page.body
4
5
...

Đầu ra

1
<!doctype html>
2
3
<html>
4
  <head>
5
    <meta charset="utf-8" />
6
    <meta http-equiv='X-UA-Compatible' content='IE=edge;chrome=1' />
7
    <meta content="IE=edge,chrome=1" http-equiv="X-UA-Compatible">
8
    <meta name="viewport" content="initial-scale=1">
9
    <title>Between | Screens </title>
10
    <link rel="alternate" type="application/atom+xml" title="Atom Feed" href="/feed.xml" />
11
    <link href="stylesheets/all-11b45acc.css" rel="stylesheet" />
12
    <script src="javascripts/all-4c20da82.js"></script>
13
  </head>
14
15
  <body>
16
    <header>
17
      <div id="logo">
18
        <a href="/"><img src="images/Between_Screens_Logo_Cube_Up-539d6997.svg" alt="Logo cube" /></a>
19
      </div>
20
      <nav class="navigation">
21
        <ul class="nav-list"> 
22
fork">https://github.com/vis-kid/betweenscreens">fork!
23
          <li><a href="pages/about/">about</a></li>
24
          <li><a href="design/">design</a></li>
25
          <li><a href="code/">code</a></li>
26
        </ul>
27
      </nav>
28
    </header>
29
30
    <div id="main" role="main">
31
      <div class='posts'>
32
        <ul>
33
          <li>
34
            <article class="index-article">
35
              <span class='post-date'>Oct 27 | 2016</span><h2 class='post-title'><a href="episodes/144/">Randy J. Hunt</a></h2>
36
              <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>
37
              <div class='soundcloud-player-small'>
38
                <iframe width="100%"
39
                  height="166"
40
                  scrolling="no"
41
                  frameborder="no"
42
                  src="https://w.soundcloud.com/player/?url=https%3A//api.soundcloud.com/tracks/290328784&color=ff0000&...>

43
              </div>

44
            </article>

45
          </li>

46


47
          <li>

48
            <article class="index-article">
49
              <span class='post-date'>Oct 25 | 2016</span><h2 class='post-title'><a href="episodes/143/">Jason Long</a></h2>
50
              <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>
51
              <div class='soundcloud-player-small'>
52
                <iframe width="100%"
53
                height="166"
54
                scrolling="no"
55
                frameborder="no"
56
                src="https://w.soundcloud.com/player/?url=https%3A//api.soundcloud.com/tracks/290126141&color=ff0000&...>

57
              </div>

58
            </article>

59
          </li>

60


61
          <li>

62
            <article class="index-article">
63
              <span class='post-date'>Oct 18 | 2016</span><h2 class='post-title'><a href="episodes/142/">David Heinemeier Hansson</a></h2>
64
              <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>
65
              <div class='soundcloud-player-small'>
66
                <iframe width="100%"
67
                height="166"
68
                scrolling="no"
69
                frameborder="no"
70
                src="https://w.soundcloud.com/player/?url=https%3A//api.soundcloud.com/tracks/289018386&color=ff0000&...>

71
              </div>

72
            </article>

73
          </li>

74


75
          <li>

76
            <article class="index-article">
77
              <span class='post-date'>Oct 12 | 2016</span><h2 class='post-title'><a href="episodes/141/">Zach Holman</a></h2>
78
              <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>
79
              <div class='soundcloud-player-small'>  
80
                <iframe width="100%"
81
                  height="166"
82
                  scrolling="no"
83
                  frameborder="no"
84
                  src="https://w.soundcloud.com/player/?url=https%3A//api.soundcloud.com/tracks/287425105&color=ff0000&...>

85
              </div>

86
            </article>

87
          </li>

88


89
          <li>

90
            <article class="index-article">
91
              <span class='post-date'>Oct 10 | 2016</span><h2 class='post-title'><a href="episodes/140/">Joel Glovier</a></h2>
92
              <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>
93
              <div class='soundcloud-player-small'>
94
                <iframe width="100%"
95
                  height="166"
96
                  scrolling="no"
97
                  frameborder="no"
98
                  src="https://w.soundcloud.com/player/?url=https%3A//api.soundcloud.com/tracks/287105342&color=ff0000&...>

99
              </div>

100
            </article>

101
          </li>

102


103
          <li>

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

113
              </div>

114
            </article>

115
          </li>

116


117
          <li>

118
            <article class="index-article">
119
              <span class='post-date'>Aug 06 | 2015</span><h2 class='post-title'><a href="episodes/138/">Corwin Harrell</a></h2>
120
              <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>
121
              <div class='soundcloud-player-small'>
122
                <iframe width="100%"
123
                height="166"
124
                scrolling="no"
125
                frameborder="no"
126
                src="https://w.soundcloud.com/player/?url=https%3A//api.soundcloud.com/tracks/218101809&color=ff0000&...>

127
              </div>

128
            </article>

129
          </li>

130
        </ul>

131
      </div>

132


133
      <section>

134
        <div class='pagination-link'><a href="page/2/">Older Stuff »</a></div>
135
      </section>
136
    </div>
137
138
    <footer>
139
      <div class='footer-tags'>
140
        <h3>Random Tags</h3>
141
        <ul class='random-tag-list'>
142
          <li><a href="/tags/exercise/">Exercise</a></li>
143
          <li><a href="/tags/company-benefits/">Company benefits</a></li>
144
          <li><a href="/tags/tmux/">Tmux</a></li>
145
          <li><a href="/tags/filetask/">FileTask</a></li>
146
          <li><a href="/tags/decision-making/">Decision making</a></li>
147
          <li><a href="/tags/favorite-feature/">Favorite feature</a></li>
148
          <li><a href="/tags/working-out/">Working out</a></li>
149
          <li><a href="/tags/scott-savarie/">Scott Savarie</a></li>
150
          <li><a href="/tags/titles/">Titles</a></li>
151
          <li><a href="/tags/erik-spiekermann/">Erik Spiekermann</a></li>
152
          <li><a href="/tags/newbie-mistakes/">Newbie mistakes</a></li>
153
          <li><a href="/tags/playbook/">Playbook</a></li>
154
          <li><a href="/tags/delegation/">Delegation</a></li>
155
          <li><a href="/tags/heat-maps/">Heat maps</a></li>
156
          <li><a href="/tags/europe/">Europe</a></li>
157
          <li><a href="/tags/sizing-type/">Sizing type</a></li>
158
          <li><a href="/tags/focus/">Focus</a></li>
159
          <li><a href="/tags/virtual-assistants/">Virtual assistants</a></li>
160
          <li><a href="/tags/writing/">Writing</a></li>
161
          <li><a href="/tags/hacking/">Hacking</a></li>
162
        </ul>
163
      </div>
164
165
      <div class='recent-posts'>
166
        <h3>Random Interviewees</h3>
167
        <ul>
168
          <li><a href="/tags/joel-glovier/">Joel Glovier</a></li>
169
          <li><a href="/tags/corwin-harrell/">Corwin Harrell</a></li>
170
          <li><a href="/tags/mario-c-delgado/">Mario C. Delgado</a></li>
171
          <li><a href="/tags/tom-dale/">Tom Dale</a></li>
172
          <li><a href="/tags/obie-fernandez/">Obie Fernandez</a></li>
173
          <li><a href="/tags/chad-pytel/">Chad Pytel</a></li>
174
          <li><a href="/tags/zach-holman/">Zach Holman</a></li>
175
          <li><a href="/tags/max-luster/">Max Luster</a></li>
176
          <li><a href="/tags/kyle-fiedler/">Kyle Fiedler</a></li>
177
          <li><a href="/tags/roberto-machado/">Roberto Machado</a></li>
178
        </ul>
179
      </div>
180
    </footer>
181
  </body>
182
</html>

Vì podcast này chỉ có một số lượng nhỏ các phần tử khác nhau trên trang, nên đây là những gì Mechanize::Page lấy về từ github.com. Nó còn có nhiều loại nội dung. Tôi nghĩ rằng đây là điều quan trọng để có được một cái nhìn ban đầu.

Đầu ra github.com

1
#<Mechanize::Page

2
 {url #https://github.com/>}

3
 {meta_refresh}
4
 {title "How people build software · GitHub"}
5
 {iframes}
6
 {frames}
7
 {links
8
  #<Mechanize::Page::Link "Skip to content" "#start-of-content">

9
  #https://github.com/">

10
  #<Mechanize::Page::Link "\n          Personal\n" "/personal">

11
  #<Mechanize::Page::Link "\n          Open source\n" "/open-source">

12
  #<Mechanize::Page::Link "\n          Business\n" "/business">

13
  #<Mechanize::Page::Link "\n          Explore\n" "/explore">

14
  #<Mechanize::Page::Link "Sign up" "/join?source=header-home">

15
  #<Mechanize::Page::Link "Sign in" "/login">

16
  #<Mechanize::Page::Link "Pricing" "/pricing">

17
  #<Mechanize::Page::Link "Blog" "/blog">

18
  #https://help.github.com">

19
  #https://github.com/search">

20
  #https://help.github.com/terms">

21
  #https://help.github.com/privacy">

22
  #<Mechanize::Page::Link "Sign up for GitHub" "/join?source=button-home">

23
  #<Mechanize::Page::Link

24
   "\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    "
25
   "/universe-2016">
26
  #<Mechanize::Page::Link "Individuals " "/personal">

27
  #<Mechanize::Page::Link "Communities " "/open-source">

28
  #<Mechanize::Page::Link "Businesses " "/business">

29
  #<Mechanize::Page::Link "NASA" "//github.com/nasa">

30
  #<Mechanize::Page::Link "Sign up for GitHub" "/join?source=button-home">

31
  #https://github.com/contact">

32
  #https://developer.github.com">

33
  #https://training.github.com">

34
  #https://shop.github.com">

35
  #https://github.com/blog">

36
  #https://github.com/about">

37
  #https://github.com">

38
  #https://github.com/site/terms">

39
  #https://github.com/site/privacy">

40
  #https://github.com/security">

41
  #https://status.github.com/">

42
  #https://help.github.com">

43
  #<Mechanize::Page::Link "Reload" "">

44
  #<Mechanize::Page::Link "Reload" "">}

45
 {forms
46
  #<Mechanize::Form

47
   {name nil}
48
   {method "GET"}
49
   {action "/search"}
50
   {fields
51
    [hidden:0x3feb90f8297c type: hidden name: utf8 value: ✓]
52
    [text:0x3feb90f827d8 type: text name: q value: ]}
53
   {radiobuttons}
54
   {checkboxes}
55
   {file_uploads}
56
   {buttons}>
57
  #<Mechanize::Form

58
   {name nil}
59
   {method "POST"}
60
   {action "/join"}
61
   {fields
62
    [hidden:0x3feb90f7be38 type: hidden name: utf8 value: ✓]
63
    [hidden:0x3feb90f7bbb8 type: hidden name: authenticity_token value: vjRATKj7smXreq6Lt02r+MzW+ewWoi+fRzQXPedFAlOZgwzxQ0dZnChirhDfd7vyWZZZBO+ZFydLNedjIEDsrQ==]
64
    [text:0x3feb90f7b9d8 type: text name: user[login] value: ]
65
    [text:0x3feb90f7b7f8 type: text name: user[email] value: ]
66
    [field:0x3feb90f7b654 type: password name: user[password] value: ]
67
    [hidden:0x3feb90f7b474 type: hidden name: source value: form-home]}
68
   {radiobuttons}
69
   {checkboxes}
70
   {file_uploads}
71
   {buttons [button:0x3feb90f7a038 type: submit name:  value: ]}>}>

Quay trở lại podcast, bạn cũng có thể nhìn thấy các nội dung như mã hóa, code phản hồi HTTP, URI hoặc header của phản hồi.

some_scraper.rb

1
require 'mechanize'
2
3
agent = Mechanize.new
4
5
podcast_url = "http://betweenscreens.fm/"
6
7
page = agent.get(podcast_url)
8
9
puts 'Encodings'
10
puts page.encodings
11
puts 'Repsonse Headers'
12
puts page.response
13
puts 'HTTP response code'
14
puts page.code
15
puts 'URI'
16
puts page.uri

Đầu ra

1
Encodings
2
EUC-JP
3
utf-8
4
utf-8
5
6
Repsonse Headers
7
{"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"}
8
9
HTTP response code
10
200
11
12
URI
13
http://betweenscreens.fm/

Còn có rất nhiều thứ khác nếu bạn muốn đi sâu hơn nữa. Tôi sẽ để lại nó cho bạn.

Các Phương thức Nokogiri

  • at
  • search

Mechanize sử dụng Nokogiri để thu thập dữ liệu từ các trang. Bạn có thể áp dụng những gì bạn đã học được về Nokogiri ở trong bài viết đầu tiên và đồng thời sử dụng nó trên các trang Mechanize. Có nghĩa là bạn sử dụng Mechanize để điều hướng các trang và dùng các phương thức Nokogiri cho nhu cầu truy xuất dữ liệu của bạn.

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

some_scraper.rb

1
require 'mechanize'
2
3
agent = Mechanize.new
4
5
podcast_url = "http://betweenscreens.fm/"
6
7
page = agent.get(podcast_url)
8
9
first_title = page.at('h2.post-title')
10
11
all_titles = page.search('h2.post-title')
12
13
all_titles.each do |title|
14
  puts title
15
end
16
17
puts " * "*33
18
19
puts first_title

Đầu ra

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

Làm việc với Liên kết

  • links
  • link_with
  • links_with

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

some_scraper.rb

1
require 'mechanize'
2
3
agent = Mechanize.new
4
5
podcast_url = "http://betweenscreens.fm/"
6
7
page = agent.get(podcast_url)
8
9
puts "#{page.links}"

Đầu ra

1
[#<Mechanize::Page::Link "Logo cube" "/">

2
, #https://github.com/vis-kid/betweenscreens">

3
, #<Mechanize::Page::Link "about" "pages/about/">

4
, #<Mechanize::Page::Link "design" "design/">

5
, #<Mechanize::Page::Link "code" "code/">

6
, #<Mechanize::Page::Link "Randy J. Hunt" "episodes/144/">

7
, #<Mechanize::Page::Link "Jason Long" "episodes/143/">

8
, #<Mechanize::Page::Link "David Heinemeier Hansson" "episodes/142/">

9
, #<Mechanize::Page::Link "Zach Holman" "episodes/141/">

10
, #<Mechanize::Page::Link "Joel Glovier" "episodes/140/">

11
, #<Mechanize::Page::Link "João Ferreira" "episodes/139/">

12
, #<Mechanize::Page::Link "Corwin Harrell" "episodes/138/">

13
, #<Mechanize::Page::Link "Older Stuff »" "page/2/">

14
, #<Mechanize::Page::Link "Exercise" "/tags/exercise/">

15
, #<Mechanize::Page::Link "Company benefits" "/tags/company-benefits/">

16
, #<Mechanize::Page::Link "Tmux" "/tags/tmux/">

17
, #<Mechanize::Page::Link "FileTask" "/tags/filetask/">

18
, #<Mechanize::Page::Link "Decision making" "/tags/decision-making/">

19
, #<Mechanize::Page::Link "Favorite feature" "/tags/favorite-feature/">

20
, #<Mechanize::Page::Link "Working out" "/tags/working-out/">

21
, #<Mechanize::Page::Link "Scott Savarie" "/tags/scott-savarie/">

22
, #<Mechanize::Page::Link "Titles" "/tags/titles/">

23
, #<Mechanize::Page::Link "Erik Spiekermann" "/tags/erik-spiekermann/">

24
, #<Mechanize::Page::Link "Newbie mistakes" "/tags/newbie-mistakes/">

25
, #<Mechanize::Page::Link "Playbook" "/tags/playbook/">

26
, #<Mechanize::Page::Link "Delegation" "/tags/delegation/">

27
, #<Mechanize::Page::Link "Heat maps" "/tags/heat-maps/">

28
, #<Mechanize::Page::Link "Europe" "/tags/europe/">

29
, #<Mechanize::Page::Link "Sizing type" "/tags/sizing-type/">

30
, #<Mechanize::Page::Link "Focus" "/tags/focus/">

31
, #<Mechanize::Page::Link "Virtual assistants" "/tags/virtual-assistants/">

32
, #<Mechanize::Page::Link "Writing" "/tags/writing/">

33
, #<Mechanize::Page::Link "Hacking" "/tags/hacking/">

34
, #<Mechanize::Page::Link "Joel Glovier" "/tags/joel-glovier/">

35
, #<Mechanize::Page::Link "Corwin Harrell" "/tags/corwin-harrell/">

36
, #<Mechanize::Page::Link "Mario C. Delgado" "/tags/mario-c-delgado/">

37
, #<Mechanize::Page::Link "Tom Dale" "/tags/tom-dale/">

38
, #<Mechanize::Page::Link "Obie Fernandez" "/tags/obie-fernandez/">

39
, #<Mechanize::Page::Link "Chad Pytel" "/tags/chad-pytel/">

40
, #<Mechanize::Page::Link "Zach Holman" "/tags/zach-holman/">

41
, #<Mechanize::Page::Link "Max Luster" "/tags/max-luster/">

42
, #<Mechanize::Page::Link "Kyle Fiedler" "/tags/kyle-fiedler/">

43
, #<Mechanize::Page::Link "Roberto Machado" "/tags/roberto-machado/">

44
]

Thật là vi diệu, hãy chia nhỏ nó ra để dễ quan sát hơn. Vì chúng ta vẫn chưa nói với Mechanize tìm ở nơi khác, nên chúng ta nhận được một loạt các liên kết chỉ từ trang đầu tiên đó. Mechanize đi xuyên qua trang đó theo thứ tự giảm dần và trả về danh sách các liên kết này từ trên xuống dưới. Tôi đã tạo ra một hình minh hoạ nhỏ với các con trỏ màu xanh trỏ đến các liên kết khác nhau mà bạn có thể nhìn thấy trong đầu ra.

Xin nói thêm là, việc này đã cho bạn thấy kết quả cuối cùng của trang podcast đã được thiết kế lại của tôi. Tôi nghĩ rằng phiên bản này tốt hơn một chút cho mục đích minh hoạ. Bạn cũng có được một cái nhìn sơ qua về kết quả sau cùng và lý do tại sao tôi cần phải trích xuất trang web Sinatra cũ của tôi.

Ảnh chụp màn hình

Podcast LinksPodcast LinksPodcast Links

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

some_scraper.rb

1
require 'mechanize'
2
3
agent = Mechanize.new
4
5
podcast_url = "http://betweenscreens.fm/"
6
7
page = agent.get(podcast_url)
8
9
page.links.each do |link|
10
  puts link.text
11
end

Đầu ra

1
Logo cube
2
fork!
3
about
4
design
5
code
6
Randy J. Hunt
7
Jason Long
8
David Heinemeier Hansson
9
Zach Holman
10
Joel Glovier
11
João Ferreira
12
Corwin Harrell
13
Older Stuff »
14
Exercise
15
Company benefits
16
Tmux
17
FileTask
18
Decision making
19
Favorite feature
20
Working out
21
Scott Savarie
22
Titles
23
Erik Spiekermann
24
Newbie mistakes
25
Playbook
26
Delegation
27
Heat maps
28
Europe
29
Sizing type

30
Focus
31
Virtual assistants
32
Writing
33
Hacking
34
Joel Glovier
35
Corwin Harrell
36
Mario C. Delgado
37
Tom Dale
38
Obie Fernandez
39
Chad Pytel
40
Zach Holman
41
Max Luster
42
Kyle Fiedler
43
Roberto Machado

Lấy về tất cả các liên kết này với số lượng lớn có thể là rất hữu ích hoặc cũng có thể là rất nhàm chán. May mắn thay, chúng ta có một vài công cụ sẵn có để điều chỉnh những gì chúng ta cần.

some_scraper.rb

1
require 'mechanize'
2
3
agent = Mechanize.new
4
5
podcast_url = "http://betweenscreens.fm/"
6
7
page = agent.get(podcast_url)
8
9
focus_link = agent.page.links.find { |link| link.text == 'Focus' }
10
11
puts focus_link

Đầu ra

1
Focus

Bùm! Bây giờ chúng ta đã có được một kết quả khác! Chúng ta có thể tìm ra các liên kết cụ thể giống như vậy. Chúng ta có thể chọn các liên kết khớp với một tiêu chí nhất định, ví dụ như văn bản của nó - với một API tuyệt vời links_with hoặc link_with. Ngoài ra, nếu chúng ta có nhiều liên kết Focus, chúng ta có thể trích ra một số lượng cụ thể trên trang bằng cặp dấu ngoặc vuông [].

some_scraper.rb

1
require 'mechanize'
2
3
agent = Mechanize.new
4
5
podcast_url = "http://betweenscreens.fm/"
6
7
page = agent.get(podcast_url)
8
9
focus_link = agent.page.links_with(:text => 'Focus')[2]
10
11
puts focus_link

Nếu bạn không tìm theo văn bản của liên kết mà là chính bản thân liên kết, thì bạn chỉ cần chỉ định một href cụ thể để tìm liên kết đó. Mechanize sẽ không gây khó dễ cho bạn. Thay vì text, bạn đưa vào các phương thức bằng href.

some_scraper.rb

1
page = agent.page.link_with(href: '/episodes/95/')
2
3
page = agent.page.links_with(href: '/episodes/95/')

Nếu bạn chỉ muốn tìm ra liên kết đầu tiên ứng với phần 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à khá dễ đọc.

some_scraper.rb

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

Muốn nhấp vào liên kết Focus và xem nó dẫn đến đâu thì làm thế nào? Hãy click vào nó!

Nhấp chuột

some_scraper.rb

1
require 'mechanize'
2
3
agent = Mechanize.new
4
5
podcast_url = "http://betweenscreens.fm/"
6
7
page = agent.get(podcast_url)
8
9
focus_links = agent.page.links.find { |link| link.text == 'Focus' }.click.links
10
11
puts focus_links

Điều này sẽ mang lại cho chúng ta một danh sách dài các liên kết giống như trước. Hãy xem dễ dàng làm sao khi kết hợp với .click.links. Mechanize nhấp vào liên kết cho bạn và đi đến trang mới. Vì chúng ta cũng yêu cầu một danh sách các liên kết, nên  chúng ta 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ẻ (tag) và một liên kết đến một tập gần đây - và tôi muốn lấy về các liên kết từ mỗi trang này.

some_scraper.rb

1
require 'mechanize'
2
3
agent = Mechanize.new
4
5
podcast_url = "http://betweenscreens.fm/"
6
7
page = agent.get(podcast_url)
8
9
links = agent.page.links_with(text: "Some interviewee")
10
11
links.each do |link|
12
  puts link.click.links
13
end

Điều này sẽ mang lại cho bạn một danh sách các liên kết cho cả hai trang. Bạn lặp qua mỗi liên kết cho người được phỏng vấn và Mechanize đi theo liên kết được 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. Bên dưới bạn có thể thấy một vài ví dụ trong đó bạn có thể so sánh những sự kết hợp khác nhau.

some_scraper.rb

1
agent.page.links.find { |l| l.text == 'Focus' }
2
agent.page.links.find { |l| l.text == 'Focus' }.click
3
agent.page.link_with(text: 'Focus')
4
agent.page.links_with(text: 'Focus')[0]
5
agent.page.links_with(text: 'Focus')[1].click
6
agent.page.links_with(text: 'Focus')[2].click.links
7
agent.page.link_with(href: '/some-href')
8
agent.page.link_with(href: '/some-href').click
9
agent.page.links_with(href: '/some-href')
10
agent.page.links_with(href: '/some-href').click

Form

  • submit
  • field_with
  • checkbox_with
  • radiobuttons_with
  • file_uploads

Nào cùng tìm hiểu về form!

some_scraper.rb

1
require 'mechanize'
2
3
agent = Mechanize.new
4
5
google_url = "http://google.com/"
6
7
page = agent.get(google_url)
8
9
forms = page.forms
10
11
puts forms.inspect

Đầu ra

1
[#<Mechanize::Form

2
# Attention!!

3
 {name "f"}
4
# Attention!!

5
 {method "GET"}
6
 {action "/search"}
7
 {fields
8
  [hidden:0x3fea91d2eb08 type: hidden name: ie value: ISO-8859-1]
9
  [hidden:0x3fea91d2e964 type: hidden name: hl value: es]
10
  [hidden:0x3fea91d2e7e8 type: hidden name: source value: hp]
11
  [hidden:0x3fea91d2e5f4 type: hidden name: biw value: ]
12
  [hidden:0x3fea91d2e428 type: hidden name: bih value: ]
13
# Attention!!

14
  [text:0x3fea91d2e248 type:  name: q value: ]
15
# Attention!!

16
  [hidden:0x3fea91d2bcb4 type: hidden name: gbv value: 1]}
17
 {radiobuttons}
18
 {checkboxes}
19
 {file_uploads}
20
 {buttons
21
  [submit:0x3fea91d2e0f4 type: submit name: btnG value: Buscar con Google]
22
  [submit:0x3fea91d2be80 type: submit name: btnI value: Voy a tener suerte]}>
23
]

Bởi vì chúng ta sử dụng phương thức forms, nên chúng ta nhận được một mảng - ngay cả khi chỉ có một form được trả về. Bây giờ chúng ta đã biết rằng, form này có tên là "f", chúng ta có thể sử dụng phiên bản số ít form để làm việc với nó.

1
...
2
3
{name "f"}
4
5
...

some_scraper.rb

1
require 'mechanize'
2
3
agent = Mechanize.new
4
5
google_url = "http://google.com/"
6
7
page = agent.get(google_url)
8
9
search_form = page.form('f')
10
11
puts search_form.inspect

Sử dụng form('f'), chúng ta chỉ ra một form cụ thể mà chúng ta muốn làm việc. Kết quả là, chúng ta sẽ không còn nhận được một mảng.

Đầu ra

1
#<Mechanize::Form

2
# Attention!!

3
 {name "f"}
4
# Attention!!

5
 {method "GET"}
6
 {action "/search"}
7
 {fields
8
  [hidden:0x3ffe9ce85ba4 type: hidden name: ie value: ISO-8859-1]
9
  [hidden:0x3ffe9ce859d8 type: hidden name: hl value: es]
10
  [hidden:0x3ffe9ce857bc type: hidden name: source value: hp]
11
  [hidden:0x3ffe9ce85618 type: hidden name: biw value: ]
12
  [hidden:0x3ffe9ce853e8 type: hidden name: bih value: ]
13
# Attention!!

14
  [text:0x3ffe9ce851cc type:  name: q value: ]
15
# Attention!!

16
  [hidden:0x3ffe9ce84bdc type: hidden name: gbv value: 1]}
17
 {radiobuttons}
18
 {checkboxes}
19
 {file_uploads}
20
 {buttons
21
  [submit:0x3ffe9ce85078 type: submit name: btnG value: Buscar con Google]
22
  [submit:0x3ffe9ce84e48 type: submit name: btnI value: Voy a tener suerte]}>

Chúng ta còn có thể xác định tên của trường văn bản (q).

1
...
2
3
[text:0x3ffe9ce851cc type:  name: q value: ]
4
5
...

Chúng ta có thể chọn nó bằng tên đó và thiết lập giá trị của nó giống như các thuộc tính Ruby. Tất cả những gì chúng ta cần làm là cung cấp cho nó một giá trị mới. Bạn có thể thấy trong đầu ra ở ví dụ trên rằng mặc định là nó rỗng.

some_scraper.rb

1
require 'mechanize'
2
3
agent = Mechanize.new
4
5
google_url = "http://google.com/"
6
7
page = agent.get(google_url)
8
9
search_form = page.form('f')
10
search_form.q = 'New Google Search'
11
12
puts search_form.inspect

Đầu ra

1
#<Mechanize::Form

2
 {name "f"}
3
 {method "GET"}
4
 {action "/search"}
5
 {fields
6
  [hidden:0x3fcb85b6a784 type: hidden name: ie value: ISO-8859-1]
7
  [hidden:0x3fcb85b6a57c type: hidden name: hl value: es]
8
  [hidden:0x3fcb85b6a3b0 type: hidden name: source value: hp]
9
  [hidden:0x3fcb85b6a16c type: hidden name: biw value: ]
10
  [hidden:0x3fcb85b67f20 type: hidden name: bih value: ]
11
# Attention!!

12
  [text:0x3fcb85b67d18 type:  name: q value: New Google Search]
13
# Attention!!

14
  [hidden:0x3fcb85b67728 type: hidden name: gbv value: 1]}
15
 {radiobuttons}
16
 {checkboxes}
17
 {file_uploads}
18
 {buttons
19
  [submit:0x3fcb85b67b9c type: submit name: btnG value: Buscar con Google]
20
  [submit:0x3fcb85b67994 type: submit name: btnI value: Voy a tener suerte]}>

Như bạn có thể quan sát thấy ở trên, giá trị cho trường văn bản đã thay đổi thành New Google Search. Bây giờ chúng ta chỉ cần submit form đó và thu thập kết quả từ trang mà Google trả về. Không thể đơn giản hơn. Lần này chúng ta hãy tìm kiếm cái gì đó khác!

some_scraper.rb

1
require 'mechanize'
2
3
agent = Mechanize.new
4
5
google_url = "http://google.com/"
6
page = agent.get(google_url)
7
8
search_form = page.form('f')
9
search_form.q = 'GitHub TouchFart'
10
11
page = agent.submit(search_form)
12
13
pp page.search('h3.r').map(&:text)

Ở đây tôi đã xác định được tiêu đề của kết quả tìm kiếm bằng một bộ chọn CSS h3.r, ánh xạ text của nó và in ra kết quả. Không có gì khó khăn cả, đúng không? Đó là một ví dụ đơn giản, tất nhiên rồi, nhưng hãy nghĩ về những khả năng vô bạn mà bạn có thể làm với nó!

Đầu ra

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

Mechanize có các trường input khác nhau để bạn sử dụng. Bạn thậm chí có thể tải lên các tập tin!

  • field_with
  • checkbox_with
  • radiobuttons_with
  • file_uploads

Bạn cũng có thể xác định các radio button và checkbox theo tên của chúng và tích chúng bằng - bạn có thể đoán - đó là check.

some_scraper.rb

1
form.radiobuttons_with(:name => 'gender')[3].check
2
3
form.checkbox_with(:name => 'coder').check

Các thẻ option cung cấp cho người dùng khả năng chọn một mục từ danh sách sổ xuống. Một lần nữa, chúng ta chọn chúng theo tên và chọn số tùy chọn mà chúng ta muốn.

some_scraper.rb

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

File_uploads hoạt động tương tự như việc nhập văn bản vào form bằng cách thiết lập nó như các thuộc tính của Ruby. Bạn xác định trường tải lên và sau đó chỉ định đường dẫn tập tin (tên tập tin) mà bạn muốn tải lên. Nghe thì có vẻ hơi phức tạp vậy thôi. Hãy xem!

some_scraper.rb

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

Phần Tóm lược

Hãy xem, không có gì là ma thuật cả! Bạn đã có được một công cụ thú vị. Chắc chắn sẽ cần phải tìm hiểu thêm về Nokogiri và Mechanize, nhưng thay vì dành quá nhiều thời gian vào các khía cạnh không cần thiết, hãy làm việc với nó và xem thêm một số tài liệu hướng dẫn khi bạn gặp khó khăn.

Tôi hy vọng bạn có thể thấy gem này đơn giản như thế nào và sức mạnh mà nó cung cấp cho bạn. Bây giờ, như tất cả chúng ta đều biết từ văn hoá quần chúng, điều này cũng đi kèm với trách nhiệm. Hãy sử dụng nó trong các khuôn khổ pháp lý và khi bạn không thể truy cập vào một API. Có thể bạn sẽ không thường xuyên sử dụng những công cụ này, nhưng nó sẽ có ích khi bạn thật sự có nhu cầu trích xuất thông tin trang web.

Như đã hứa, trong phần tiếp theo, chúng ta sẽ tìm hiểu một ví dụ thực tế, nơi tôi sẽ trích xuất dữ liệu từ trang podcast của tôi. Tôi sẽ trích xuất nó từ một trang web Sinatra cũ và chuyển nó sang trang web Middleman mới của tôi sử dụng các tập tin .markdown cho mỗi tập. Chúng ta sẽ trích xuất ngày, số tập, tên người được phỏng vấn, tiêu đề, tiêu đề phụ và vân vân. Hẹn gặp bạn ở đó!

Advertisement
Did you find this post useful?
Want a weekly email summary?
Subscribe below and we’ll send you a weekly email summary of all new Code tutorials. Never miss out on learning about the next big thing.
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.