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 1

Rubyland có hai viên đá quý đã chiếm lĩnh thị trường tiêu điểm trong vài năm qua:Nokogiri và Mechanize. Chúng tôi dành một bài báo về từng điều này trước khi đưa chúng vào hoạt động với một ví dụ thực tế.

Chủ đề

  • Web Scraping?
  • Quyền
  • Vấn đề
  • Nokogiri
  • Trích xuất?
  • Trang
  • API
  • Điều hướng nút

Web Scraping?

Có những thuật ngữ kỳ lạ hơn so với trang web hoặc màn hình cạo. Thu hoạch web và trích xuất dữ liệu web cho bạn biết ngay điều gì đang xảy ra. Chúng tôi có thể tự động hóa việc trích xuất dữ liệu từ các trang web — và việc này cũng không quá phức tạp.

Theo một cách nào đó, những công cụ này cho phép bạn bắt chước và tự động hóa quá trình duyệt web của con người. Bạn viết một chương trình chỉ trích xuất loại dữ liệu mà bạn quan tâm. Nhắm mục tiêu dữ liệu cụ thể gần như dễ dàng như sử dụng bộ chọn CSS.

Một vài năm trước, tôi đã đăng ký một số khóa học video trực tuyến có hàng triệu video ngắn nhưng không có tùy chọn tải xuống hàng loạt. Tôi đã phải tự mình đi qua từng liên kết và tự mình thực hiện hành động ‘lưu thành’ đáng sợ. Đó là một kiểu cào web của con người — điều mà chúng ta thường cần làm khi chúng ta thiếu kiến ​​thức để tự động hóa loại nội dung đó. Bản thân khóa học vẫn ổn nhưng sau đó tôi không sử dụng dịch vụ của họ nữa. Nó chỉ là quá tẻ nhạt.

Hôm nay, tôi sẽ không quan tâm quá nhiều đến trải nghiệm người dùng làm tan chảy tâm trí như vậy. Một máy quét sẽ thực hiện việc tải xuống cho tôi sẽ khiến tôi chỉ mất vài phút để ném cùng nhau. Không có vấn đề gì!

Hãy để tôi chia nhỏ nó thật nhanh trước khi chúng ta bắt đầu. Toàn bộ điều có thể được cô đọng trong một vài bước. Đầu tiên, chúng tôi tìm nạp một trang web có dữ liệu mong muốn mà chúng tôi cần. Sau đó, chúng tôi tìm kiếm thông qua trang đó và xác định thông tin chúng tôi muốn trích xuất.

Bước cuối cùng là nhắm mục tiêu các bit này, chia nhỏ chúng nếu cần và quyết định cách thức và vị trí bạn muốn lưu trữ chúng. HTML được viết tốt thường là chìa khóa để làm cho quá trình này trở nên dễ dàng và thú vị. Đối với các ca nhổ có liên quan nhiều hơn, có thể là một điều khó khăn nếu bạn phải đối phó với đánh dấu có cấu trúc kém.

Còn về các API thì sao? Câu hỏi rất hay. Nếu bạn có quyền truy cập vào một dịch vụ có API, bạn thường không cần phải viết bảng mã của riêng mình. Cách tiếp cận này chủ yếu dành cho các trang web không mang lại sự tiện lợi như vậy. Nếu không có API, đây thường là cách duy nhất để tự động hóa việc trích xuất thông tin từ các trang web.

Bạn có thể hỏi, làm thế nào mà thứ cạo này thực sự hoạt động? Nếu không đi sâu vào phần cuối, câu trả lời ngắn gọn là, bằng cách duyệt qua các cấu trúc dữ liệu dạng cây. Nokogiri xây dựng các cấu trúc dữ liệu này từ các tài liệu bạn cung cấp và cho phép bạn nhắm mục tiêu các bit quan tâm để trích xuất. Ví dụ:CSS là một ngôn ngữ được viết để duyệt qua cây, để tìm kiếm cấu trúc dữ liệu cây và chúng ta có thể sử dụng nó để trích xuất dữ liệu.

Có rất nhiều cách tiếp cận và giải pháp để chơi. Rubyland có hai viên đá quý đã chiếm sự chú ý trong một số năm nay. Nhiều người vẫn dựa vào Nokogiri và Mechanize cho nhu cầu cắt HTML. Cả hai đều đã được thử nghiệm và chứng minh là dễ sử dụng đồng thời có khả năng cao. Chúng ta sẽ xem xét cả hai. Nhưng trước đó, tôi muốn dành một chút thời gian để giải quyết vấn đề mà chúng ta sẽ giải quyết ở phần cuối của loạt bài giới thiệu ngắn này.

Quyền

Trước khi bắt đầu loại bỏ, hãy đảm bảo rằng bạn có quyền của các trang web mà bạn đang cố gắng truy cập để trích xuất dữ liệu. Ví dụ:nếu trang web có nguồn cấp dữ liệu API hoặc RSS, thì việc lấy nội dung mong muốn đó không chỉ dễ dàng hơn mà còn có thể là tùy chọn hợp pháp được lựa chọn.

Không phải ai cũng sẽ đánh giá cao điều đó nếu bạn thực hiện việc tìm kiếm ồ ạt trên các trang web của họ — có thể hiểu được như vậy. Hãy tự học trên trang web cụ thể mà bạn quan tâm và đừng để mình gặp rắc rối. Rất ít khả năng bạn sẽ gây ra thiệt hại nghiêm trọng, nhưng vô tình gặp rắc rối không phải là cách để đi.

Vấn đề

Tôi cần tạo một podcast mới. Thiết kế không giống như tôi muốn, và tôi ghét cách xuất bản các bài viết mới. WYSIWYGs chết tiệt! Một chút bối cảnh. Khoảng hai năm trước, tôi đã xây dựng phiên bản podcast đầu tiên của mình. Ý tưởng là chơi với Sinatra và xây dựng một thứ gì đó siêu nhẹ. Tôi đã gặp phải một số vấn đề không mong muốn vì tôi đã thiết kế khá nhiều thứ.

Đến từ Rails, đó chắc chắn là một hành trình giáo dục mà tôi đánh giá cao, nhưng tôi nhanh chóng hối tiếc vì đã không sử dụng một trang web tĩnh mà tôi có thể đã triển khai thông qua GitHub thông qua các trang GitHub. Việc triển khai các tập mới và duy trì chúng thiếu sự đơn giản mà tôi đang tìm kiếm. Trong một thời gian, tôi quyết định rằng tôi có những con cá lớn hơn để chiên và tập trung vào sản xuất tài liệu podcast mới để thay thế.

Mùa hè vừa qua, tôi bắt đầu nghiêm túc và làm việc trên một trang Middleman được lưu trữ qua các trang GitHub. Đối với mùa thứ hai của chương trình, tôi muốn một cái gì đó mới mẻ. Một thiết kế mới, đơn giản hóa, Markdown để xuất bản các tập mới và không có cuộc chiến tay đôi nào với Heroku — thiên đường! Vấn đề là tôi có 139 tập phim cần được nhập và chuyển đổi trước để làm việc với Middleman.

Đối với các bài đăng, Middleman sử dụng .markdown các tệp được gọi là vật chất phía trước cho dữ liệu — về cơ bản thay thế cơ sở dữ liệu của tôi. Thực hiện chuyển nhượng bằng tay không phải là một lựa chọn cho 139 tập phim. Đó là công dụng của tính toán. Tôi cần tìm ra cách để phân tích cú pháp HTML của trang web cũ của mình, loại bỏ nội dung có liên quan và chuyển nó vào các bài đăng trên blog mà tôi sử dụng để xuất bản các tập podcast mới trên Middleman.

Do đó, trong ba bài viết tiếp theo, tôi sẽ giới thiệu cho bạn các công cụ thường được sử dụng trong Rubyland cho các tác vụ như vậy. Cuối cùng, chúng tôi cũng sẽ xem xét giải pháp của tôi để cho bạn thấy điều gì đó thực tế.

Nokogiri

Ngay cả khi bạn hoàn toàn mới sử dụng Ruby / Rails, rất có thể bạn đã nghe nói về viên ngọc nhỏ này. Tên bị bỏ thường xuyên và dính với bạn một cách dễ dàng. Tôi không chắc rằng nhiều người biết rằng nokogiri trong tiếng Nhật có nghĩa là "cưa".

Đó là một cái tên phù hợp khi bạn hiểu công cụ này làm gì. Người tạo ra viên đá quý này là Tenderlove đáng yêu, Aaron Patterson. Nokogiri chuyển đổi các tài liệu XML và HTML thành một cấu trúc dữ liệu — một cấu trúc dữ liệu cây, chính xác hơn. Công cụ này nhanh và cung cấp một giao diện đẹp. Nhìn chung, đó là một thư viện rất mạnh có thể xử lý vô số nhu cầu cắt HTML của bạn.

Bạn có thể sử dụng Nokogiri không chỉ để phân tích cú pháp HTML; XML cũng là một trò chơi công bằng. Nó cung cấp cho bạn các tùy chọn của cả ngôn ngữ đường dẫn XML và giao diện CSS để duyệt qua các tài liệu bạn tải. Đường dẫn XML, gọi tắt là XPath, là một ngôn ngữ truy vấn.

Nó cho phép chúng tôi chọn các nút từ các tài liệu XML. Bộ chọn CSS rất có thể quen thuộc hơn với người mới bắt đầu. Giống như các kiểu bạn viết, các bộ chọn CSS giúp bạn dễ dàng nhắm mục tiêu các phần cụ thể của trang mà bạn quan tâm để trích xuất. Bạn chỉ cần cho Nokogiri biết bạn đang muốn gì khi bạn nhắm mục tiêu đến một điểm đến cụ thể.

Trang


Những gì chúng tôi luôn cần bắt đầu là tìm nạp trang thực tế mà chúng tôi quan tâm. Chúng tôi chỉ định loại tài liệu Nokogiri mà chúng tôi muốn phân tích cú pháp — ví dụ:XML hoặc HTML:

Nokogiri::XML

Nokogiri::HTML

some_scraper.rb

require "nokogiri"

require "open-uri"

page = Nokogiri::XML(File.open("some.xml"))

page = Nokogiri::HTML(File.open("some.html"))

Nokogiri:XMLNokogiri:HTML có thể lấy đối tượng IO hoặc đối tượng chuỗi. Những gì xảy ra ở trên là đơn giản. Thao tác này sẽ mở và tìm nạp trang được chỉ định bằng open-uri và sau đó tải cấu trúc, XML hoặc HTML của nó vào một tài liệu Nokogiri mới. XML không phải là thứ mà người mới bắt đầu phải xử lý thường xuyên.

Do đó, tôi khuyên bạn nên tập trung vào phân tích cú pháp HTML ngay bây giờ. Tại sao lại open-uri ? Mô-đun này từ Thư viện Chuẩn Ruby cho phép chúng tôi lấy trang web mà không gặp nhiều phiền phức. Vì các đối tượng IO là trò chơi công bằng, chúng ta có thể dễ dàng sử dụng open-uri .

API

Hãy áp dụng điều này vào thực tế với một ví dụ nhỏ:

at_css

some_podcast_scraper.rb

require 'nokogiri'

require "open-uri"

url = 'https://betweenscreens.fm/'

page = Nokogiri::HTML(open(url))

header = page.at_css("h2.post-title")

title = header.text

puts "This is the raw header of the latest episode: #{header}"

puts "This is the title of the latest episode: #{title}"

Những gì chúng tôi đã làm ở đây đại diện cho tất cả các bước thường liên quan đến việc tìm kiếm trên web — chỉ ở mức vi mô. Chúng tôi quyết định URL nào chúng tôi cần và trang web nào chúng tôi cần tìm nạp, và chúng tôi tải chúng vào một tài liệu Nokogiri mới. Sau đó, chúng tôi mở trang đó và nhắm mục tiêu đến một phần cụ thể.

Ở đây tôi chỉ muốn biết tiêu đề của tập mới nhất. Sử dụng at_css và một bộ chọn CSS cho h2.post-title là tất cả những gì tôi cần để nhắm mục tiêu điểm khai thác. Tuy nhiên, với phương pháp này, chúng tôi sẽ chỉ loại bỏ phần tử số ít này. Điều này cung cấp cho chúng tôi toàn bộ bộ chọn — phần lớn thời gian không phải là chính xác những gì chúng tôi cần. Do đó, chúng tôi chỉ trích xuất phần văn bản bên trong của nút này thông qua text phương pháp. Để so sánh, bạn có thể kiểm tra kết quả đầu ra cho cả tiêu đề và văn bản bên dưới.

Đầu ra

This is the raw title of the latest episode: <h2 class="post-title"><a href="episodes/142/">David Heinemeier Hansson</a></h2>

This is the title of the latest episode: David Heinemeier Hansson

Mặc dù ví dụ này có các ứng dụng rất hạn chế, nhưng nó có tất cả các thành phần, tất cả các bước mà bạn cần hiểu. Tôi nghĩ điều này thật tuyệt làm sao. Bởi vì nó có thể không rõ ràng từ ví dụ này, tôi muốn chỉ ra công cụ này có thể mạnh mẽ như thế nào. Hãy xem chúng ta có thể làm gì khác với tập lệnh Nokogiri.

Chú ý!

Nếu bạn là người mới bắt đầu và không chắc chắn về cách nhắm mục tiêu HTML cần thiết cho việc này, tôi khuyên bạn nên tìm kiếm trực tuyến để biết cách kiểm tra nội dung của các trang web trong trình duyệt của bạn. Về cơ bản, tất cả các trình duyệt chính làm cho quá trình này thực sự dễ dàng trong những ngày này.

Trên Chrome, bạn chỉ cần nhấp chuột phải vào một phần tử trong trang web và chọn tùy chọn kiểm tra. Thao tác này sẽ mở ra một cửa sổ nhỏ ở cuối trình duyệt của bạn, cửa sổ này hiển thị cho bạn thứ gì đó giống như hình chụp X-quang DOM của trang web. Nó có nhiều tùy chọn hơn và tôi khuyên bạn nên dành một chút thời gian trên Google để tự học. Đây là thời gian được sử dụng một cách khôn ngoan!

css

css phương thức sẽ cung cấp cho chúng ta không chỉ một phần tử duy nhất mà bất kỳ phần tử nào phù hợp với tiêu chí tìm kiếm trên trang. Khá gọn gàng và đơn giản!

some_scraper.rb

require 'nokogiri'

require "open-uri"

url = 'https://betweenscreens.fm/'

page = Nokogiri::HTML(open(url))

headers = page.css("h2.post-title")

headers.each do |header|
  puts "This is the raw title of the latest episode: #{header}"
end

headers.each do |header|
  puts "This is the title of the latest episode: #{header.text}"
end

Đầu ra

This is the raw title of the latest episode: <h2 class="post-title"><a href="episodes/142/">David Heinemeier Hansson</a></h2>
This is the raw title of the latest episode: <h2 class="post-title"><a href="episodes/141/">Zach Holman</a></h2>
This is the raw title of the latest episode: <h2 class="post-title"><a href="episodes/140/">Joel Glovier</a></h2>
This is the raw title of the latest episode: <h2 class="post-title"><a href="episodes/139/">João Ferreira</a></h2>
This is the raw title of the latest episode: <h2 class="post-title"><a href="episodes/138/">Corwin Harrell</a></h2>
This is the raw title of the latest episode: <h2 class="post-title"><a href="episodes/137/">Roberto Machado</a></h2>
This is the raw title of the latest episode: <h2 class="post-title"><a href="episodes/136/">James Edward Gray II</a></h2>

This is the title of the latest episode: David Heinemeier Hansson
This is the title of the latest episode: Zach Holman
This is the title of the latest episode: Joel Glovier
This is the title of the latest episode: João Ferreira
This is the title of the latest episode: Corwin Harrell
This is the title of the latest episode: Roberto Machado
This is the title of the latest episode: James Edward Gray II

Sự khác biệt nhỏ duy nhất trong ví dụ này là tôi lặp lại trên các tiêu đề thô trước tiên. Tôi cũng trích xuất văn bản bên trong của nó bằng text phương pháp. Nokogiri tự động dừng ở cuối trang và không cố gắng tự động theo dõi việc phân trang ở bất kỳ đâu.

Giả sử chúng tôi muốn có thêm một chút thông tin, nói ngày tháng và phụ đề cho mỗi tập. Chúng ta có thể đơn giản mở rộng ví dụ trên. Dù sao cũng là một ý kiến ​​hay nếu bạn thực hiện từng bước một. Làm cho một phần nhỏ hoạt động và thêm phức tạp hơn trong quá trình thực hiện.

some_scraper.rb

require 'nokogiri'

require "open-uri"

url = 'https://betweenscreens.fm/'

page = Nokogiri::HTML(open(url))

articles = page.css("article.index-article")

articles.each do |article|
  header     = article.at_css("h2.post-title")
  date       = article.at_css(".post-date")
  subtitle   = article.at_css(".topic-list")

  puts "This is the raw header:    #{header}"
  puts "This is the raw date:      #{date}"
  puts "This is the raw subtitle:  #{subtitle}\n\n"
 
  puts "This is the text header:   #{header.text}"
  puts "This is the text date:     #{date.text}"
  puts "This is the text subtitle: #{subtitle.text}\n\n"
end

Đầu ra

This is the raw header: <h2 class="post-title"><a href="episodes/142/">David Heinemeier Hansson</a></h2>
This is the raw date: <span class="post-date">Oct 18 | 2016</span>
This is the raw subtitle: <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>

This is the text header: David Heinemeier Hansson
This is the text date: Oct 18 | 2016
This is the text subtitle: 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

This is the raw header: <h2 class="post-title"><a href="episodes/141/">Zach Holman</a></h2>
This is the raw date: <span class="post-date">Oct 12 | 2016</span>
This is the raw subtitle: <h3 class="topic-list">Getting Fired | Taboo | Transparency | Different Perspectives | Timing | Growth Stages | Employment &amp; 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>

This is the text header: Zach Holman
This is the text date: Oct 12 | 2016
This is the text subtitle: 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

This is the raw header: <h2 class="post-title"><a href="episodes/140/">Joel Glovier</a></h2>
This is the raw date: <span class="post-date">Oct 10 | 2016</span>
This is the raw subtitle: <h3 class="topic-list">Digital Product Design | Product Design @ GitHub | Loving Design | Order &amp; Chaos | Drawing | Web Design | HospitalRun | Diversity | Startup Culture | Improving Lives | CURE International | Ember | Offline First | Hospital Information System | Designers &amp; Open Source</h3>

This is the text header: Joel Glovier
This is the text date: Oct 10 | 2016
This is the text subtitle: 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

This is the raw header: <h2 class="post-title"><a href="episodes/139/">João Ferreira</a></h2>
This is the raw date: <span class="post-date">Aug 26 | 2015</span>
This is the raw subtitle: <h3 class="topic-list">Masters @ Work | Subvisual | Deadlines | Design personality | Design problems | Team | Pushing envelopes | Delightful experiences | Perfecting details | Company values</h3>

This is the text header: João Ferreira
This is the text date: Aug 26 | 2015
This is the text subtitle: Masters @ Work | Subvisual | Deadlines | Design personality | Design problems | Team | Pushing envelopes | Delightful experiences | Perfecting details | Company values

This is the raw header: <h2 class="post-title"><a href="episodes/138/">Corwin Harrell</a></h2>
This is the raw date: <span class="post-date">Aug 06 | 2015</span>
This is the raw subtitle: <h3 class="topic-list">Q&amp;A | 01 | University | Graphic design | Design setup | Sublime | Atom | thoughtbot | Working location | Collaboration &amp; pairing | Vim advocates | Daily routine | Standups | Clients | Coffee walks | Investment Fridays |</h3>

This is the text header: Corwin Harrell
This is the text date: Aug 06 | 2015
This is the text subtitle: 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 |

This is the raw header: <h2 class="post-title"><a href="episodes/137/">Roberto Machado</a></h2>
This is the raw date: <span class="post-date">Aug 03 | 2015</span>
This is the raw subtitle: <h3 class="topic-list">CEO @ Subvisual | RubyConf Portugal | Creators School | Consultancy | Company role models | Group Buddies | Portuguese startup | Rebranding | Technologies used | JS frameworks | TDD &amp; BDD | Startup mistakes | Culture of learning | Young entrepreneurs</h3>

This is the text header: Roberto Machado
This is the text date: Aug 03 | 2015
This is the text subtitle: CEO @ Subvisual | RubyConf Portugal | Creators School | Consultancy | Company role models | Group Buddies | Portuguese startup | Rebranding | Technologies used | JS frameworks | TDD & BDD | Startup mistakes | Culture of learning | Young entrepreneurs

This is the raw header: <h2 class="post-title"><a href="episodes/136/">James Edward Gray II</a></h2>
This is the raw date: <span class="post-date">Jul 30 | 2015</span>
This is the raw subtitle: <h3 class="topic-list">Screencasting | Less Code | Reading code | Getting unstuck | Rails’s codebase | CodeNewbie | Small examples | Future plans | PeepCode | Frequency &amp; pricing</h3>

This is the text header: James Edward Gray II
This is the text date: Jul 30 | 2015
This is the text subtitle: Screencasting | Less Code | Reading code | Getting unstuck | Rails’s codebase | CodeNewbie | Small examples | Future plans | PeepCode | Frequency & pricing

Tại thời điểm này, chúng tôi đã có một số dữ liệu để xử lý. Chúng tôi có thể cấu trúc hoặc bán thịt nó theo bất kỳ cách nào chúng tôi muốn. Ở trên chỉ đơn giản là hiển thị những gì chúng ta có theo cách có thể đọc được. Tất nhiên, chúng ta có thể tìm hiểu sâu hơn về từng điều này bằng cách sử dụng biểu thức chính quy với text phương pháp.

Chúng tôi sẽ xem xét vấn đề này một cách chi tiết hơn khi chúng tôi giải quyết vấn đề podcast thực tế. Đây sẽ không phải là một lớp học trên regexp, nhưng bạn sẽ thấy một số lớp học khác trong thực tế — nhưng đừng lo lắng, không quá nhiều để làm cho não của bạn chảy máu.

Các thuộc tính

Điều có thể hữu ích ở giai đoạn này là giải nén href cho các tập riêng lẻ. Nó không thể đơn giản hơn.

some_scraper.rb

require 'nokogiri'

require "open-uri"

url = 'https://betweenscreens.fm/'

page = Nokogiri::HTML(open(url))

articles = page.css("article.index-article")

articles.each do |article|
  header      = article.at_css("h2.post-title")
  date        = article.at_css(".post-date")
  subtitle    = article.at_css(".topic-list")
  link        = article.at_css("h2.post-title a")
  podcast_url = "https://betweenscreens.fm/"

  puts "This is the raw header:    #{header}"
  puts "This is the raw date:      #{date}"
  puts "This is the raw subtitle:  #{subtitle}"
  puts "This is the raw link:      #{link}\n\n"

  puts "This is the text header:   #{header.text}"
  puts "This is the text date:     #{date.text}"
  puts "This is the text subtitle: #{subtitle.text}"
  puts "This is the raw link:      #{podcast_url}#{link[:href]}\n\n"
end

Các bit quan trọng nhất cần chú ý ở đây là [:href]podcast_url . Nếu bạn gắn thẻ trên [:] bạn có thể chỉ cần trích xuất một thuộc tính từ bộ chọn được nhắm mục tiêu. Tôi đã tóm tắt thêm một chút, nhưng bạn có thể thấy rõ hơn cách nó hoạt động bên dưới.

...

href = article.at_css("h2.post-title a")[:href]

...

Để có được một URL đầy đủ và hữu ích, tôi đã lưu miền gốc trong một biến và tạo URL đầy đủ cho mỗi tập.

...

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

puts "This is the raw link: #{podcast_url}#{link[:href]}\n\n"

...

Chúng ta hãy xem nhanh kết quả đầu ra:

Đầu ra

This is the raw header:   <h2 class="post-title"><a href="episodes/143/">Jason Long</a></h2>
This is the raw date:     <span class="post-date">Oct 25 | 2016</span>
This is the raw subtitle: <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>
This is the raw link:     <a href="episodes/143/">Jason Long</a>

This is the text header: Jason Long
This is the text date:   Oct 25 | 2016
This is the text subtitle: Open source | Empathy | Lower barriers | Learning tool | Design contributions | Git website | Branding | GitHub | Neovim | Tmux | Design love | Knowing audiences | Showing work | Dribbble | Progressions | Ideas
This is the href:     https://betweenscreens.fm/episodes/143/

This is the raw header:   <h2 class="post-title"><a href="episodes/142/">David Heinemeier Hansson</a></h2>
This is the raw date:     <span class="post-date">Oct 18 | 2016</span>
This is the raw subtitle: <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>
This is the raw link:     <a href="episodes/142/">David Heinemeier Hansson</a>

This is the text header: David Heinemeier Hansson
This is the text date:   Oct 18 | 2016
This is the text subtitle: 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
This is the href:     https://betweenscreens.fm/episodes/142/

This is the raw header:   <h2 class="post-title"><a href="episodes/141/">Zach Holman</a></h2>
This is the raw date:     <span class="post-date">Oct 12 | 2016</span>
This is the raw subtitle: <h3 class="topic-list">Getting Fired | Taboo | Transparency | Different Perspectives | Timing | Growth Stages | Employment &amp; 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>
This is the raw link:     <a href="episodes/141/">Zach Holman</a>

This is the text header: Zach Holman
This is the text date:   Oct 12 | 2016
This is the text subtitle: 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
This is the href:     https://betweenscreens.fm/episodes/141/

This is the raw header:   <h2 class="post-title"><a href="episodes/140/">Joel Glovier</a></h2>
This is the raw date:     <span class="post-date">Oct 10 | 2016</span>
This is the raw subtitle: <h3 class="topic-list">Digital Product Design | Product Design @ GitHub | Loving Design | Order &amp; Chaos | Drawing | Web Design | HospitalRun | Diversity | Startup Culture | Improving Lives | CURE International | Ember | Offline First | Hospital Information System | Designers &amp; Open Source</h3>
This is the raw link:     <a href="episodes/140/">Joel Glovier</a>

This is the text header: Joel Glovier
This is the text date:   Oct 10 | 2016
This is the text subtitle: 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
This is the href:     https://betweenscreens.fm/episodes/140/

This is the raw header:   <h2 class="post-title"><a href="episodes/139/">João Ferreira</a></h2>
This is the raw date:     <span class="post-date">Aug 26 | 2015</span>
This is the raw subtitle: <h3 class="topic-list">Masters @ Work | Subvisual | Deadlines | Design personality | Design problems | Team | Pushing envelopes | Delightful experiences | Perfecting details | Company values</h3>
This is the raw link:     <a href="episodes/139/">João Ferreira</a>

This is the text header: João Ferreira
This is the text date:   Aug 26 | 2015
This is the text subtitle: Masters @ Work | Subvisual | Deadlines | Design personality | Design problems | Team | Pushing envelopes | Delightful experiences | Perfecting details | Company values
This is the href:     https://betweenscreens.fm/episodes/139/

This is the raw header:   <h2 class="post-title"><a href="episodes/138/">Corwin Harrell</a></h2>
This is the raw date:     <span class="post-date">Aug 06 | 2015</span>
This is the raw subtitle: <h3 class="topic-list">Q&amp;A | 01 | University | Graphic design | Design setup | Sublime | Atom | thoughtbot | Working location | Collaboration &amp; pairing | Vim advocates | Daily routine | Standups | Clients | Coffee walks | Investment Fridays |</h3>
This is the raw link:     <a href="episodes/138/">Corwin Harrell</a>

This is the text header: Corwin Harrell
This is the text date:   Aug 06 | 2015
This is the text subtitle: 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 |
This is the href:     https://betweenscreens.fm/episodes/138/

This is the raw header:   <h2 class="post-title"><a href="episodes/137/">Roberto Machado</a></h2>
This is the raw date:     <span class="post-date">Aug 03 | 2015</span>
This is the raw subtitle: <h3 class="topic-list">CEO @ Subvisual | RubyConf Portugal | Creators School | Consultancy | Company role models | Group Buddies | Portuguese startup | Rebranding | Technologies used | JS frameworks | TDD &amp; BDD | Startup mistakes | Culture of learning | Young entrepreneurs</h3>
This is the raw link:     <a href="episodes/137/">Roberto Machado</a>

This is the text header: Roberto Machado
This is the text date:   Aug 03 | 2015
This is the text subtitle: CEO @ Subvisual | RubyConf Portugal | Creators School | Consultancy | Company role models | Group Buddies | Portuguese startup | Rebranding | Technologies used | JS frameworks | TDD & BDD | Startup mistakes | Culture of learning | Young entrepreneurs
This is the href:     https://betweenscreens.fm/episodes/137/

Gọn gàng phải không? Bạn có thể làm tương tự để giải nén [:class] của một bộ chọn.

require 'nokogiri'

require "open-uri"

url = 'https://betweenscreens.fm/'

page = Nokogiri::HTML(open(url))

body_classes = page.at_css("body")[:class]

Nếu nút đó có nhiều hơn một lớp, bạn sẽ nhận được danh sách tất cả chúng.

Điều hướng nút

  • cha mẹ
  • trẻ em
  • before_sibling
  • next_sibling

Chúng ta đã quen với việc xử lý các cấu trúc cây trong CSS hoặc thậm chí là jQuery. Sẽ rất khó nếu Nokogiri không cung cấp một API tiện dụng để di chuyển trong những cái cây như vậy.

some_scraper.rb

require 'nokogiri'

require "open-uri"

url = 'https://betweenscreens.fm/'

page = Nokogiri::HTML(open(url))

header = page.at_css("h2.post-title")
header_children = page.at_css("h2.post-title").children
header_parent = page.at_css("h2.post-title").parent
header_prev_sibling = page.at_css("h2.post-title").previous_sibling

puts "#{header}\n\n"
puts "#{header_children}\n\n"
puts "#{header_parent}\n\n"
puts "#{header_prev_sibling}\n\n"

Đầu ra

#header
<h2 class="post-title"><a href="episodes/143/">Jason Long</a></h2>

#header_children
<a href="episodes/143/">Jason Long</a>

#header_parent
<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">  
    </div>
</article>

#header_previous_sibling
<span class="post-date">Oct 25 | 2016</span>

Như bạn có thể tự mình thấy, đây là một số công cụ khá mạnh mẽ — đặc biệt là khi bạn thấy những gì .parent đã có thể thu thập trong một lần. Thay vì xác định một loạt các nút bằng tay, bạn có thể thu thập chúng bán buôn.

Bạn thậm chí có thể xâu chuỗi chúng để có nhiều đường đi liên quan hơn. Tất nhiên, bạn có thể làm điều này phức tạp tùy thích, nhưng tôi lưu ý bạn nên giữ mọi thứ đơn giản. Nó có thể nhanh chóng trở nên hơi khó sử dụng và khó hiểu. Hãy nhớ rằng, "Hãy giữ nó đơn giản, ngu ngốc!"

...

header_parent_parent = page.at_css("h2.post-title").parent.parent
header_prev_sibling_parent_children = page.at_css("h2.post-title").previous_sibling.parent.children

...

some_scraper.rb

require 'nokogiri'

require "open-uri"

url = 'https://betweenscreens.fm/'

page = Nokogiri::HTML(open(url))

header = page.at_css("h2.post-title")
header_prev_sibling_children = page.at_css("h2.post-title").previous_sibling.children
header_parent_parent = page.at_css("h2.post-title").parent.parent
header_prev_sibling_parent = page.at_css("h2.post-title").previous_sibling.parent
header_prev_sibling_parent_children = page.at_css("h2.post-title").previous_sibling.parent.children

puts "#{header}\n\n"
puts "#{header_prev_sibling_children}\n\n"
puts "#{header_parent_parent}\n\n"
puts "#{header_prev_sibling_parent}\n\n"
puts "#{header_prev_sibling_parent_children}\n\n"

Đầu ra

#header
<h2 class="post-title"><a href="episodes/143/">Jason Long</a></h2>

#header_previous_sibling_children
Oct 25 | 2016

#header_parent_parent
<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">  
    </div>
  </article>
</li>

#header_previous_sibling_parent
<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">  
    </div>
</article>

#header_previous_sibling_parent_children
  <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">  
    </div>

Final Thoughts

Nokogiri is not a huge library, but it has a lot to offer. I recommend you play with what you have learned thus far and expand your knowledge through its documentation when you hit a wall. But don’t get yourself into trouble!

This little intro should get you well on your way to understanding what you can do and how it works. I hope you will explore it a bit more on your own and have some fun with it. As you will find out on your own, it’s a rich tool that keeps on giving.