在本教程中,我们展示了如何使用标准的Ruby Net::HTTP模块工作。我们抓取数据、发布数据、处理JSON,并连接到一个安全网页。该教程在几个例子中使用了Sinatra应用程序。Zetcode也有一个简明的 Ruby tutorial 。
超文本传输协议( HTTP ) 是一个用于分布式、协作式、超媒体信息系统的应用协议。HTTP是万维网的数据通信的基础。
Ruby Net::HTTP
提供了一个丰富的库,可以用来建立HTTP客户端。
响应的 code
和 message
方法给出其状态。
#!/usr/bin/ruby require 'net/http' uri = URI 'http://webcode.me' res = Net::HTTP.get_response uri puts res.message puts res.code uri = URI 'http://www.example.com/news/' res = Net::HTTP.get_response uri puts res.message puts res.code uri = URI 'http://www.urbandicionary.com/define.php?term=Dog' res = Net::HTTP.get_response uri puts res.message puts res.code
我们用 get_response
方法执行三个HTTP请求并检查返回的状态。
uri = URI 'http://www.example.com/news/' res = Net::HTTP.get_response uri puts res.message puts res.code
用 message
和 code
方法检查HTTP响应的状态。
$ ./status_code.rb OK 200 Not Found 404 Found 302
200是HTTP请求成功的标准响应,404告诉人们无法找到请求的资源,302告诉人们资源被暂时重定向。
HEAD请求是一个没有信息体的HTTP GET请求。请求/响应的头包含元数据,如HTTP协议版本或内容类型。
head
方法检索的是文件头。头信息由字段组成,包括日期、服务器、内容类型或最后修改时间。
#!/usr/bin/ruby require 'net/http' uri = URI "http://www.webcode.me" http = Net::HTTP.new uri.host, uri.port res = http.head '/' puts res['server'] puts res['date'] puts res['last-modified'] puts res['content-type'] puts res['content-length']
该例子打印了 www.something.com
网页的服务器、日期、最后修改时间、内容类型和内容长度。
$ ./http_head.rb nginx/1.6.2 Wed, 03 Feb 2021 10:30:30 GMT Sat, 20 Jul 2019 11:49:25 GMT text/html 348
HTTP GET方法请求一个指定资源的表示。
#!/usr/bin/ruby require 'net/http' uri = URI 'http://www.webcode.me' content = Net::HTTP.get uri puts content
该脚本抓取webcode.me网页的内容。 net/http
的设计是为了与 uri
模块紧密合作。
require 'net/http'
这也会要求 uri
,所以我们不需要单独要求它。
content = Net::HTTP.get uri
get
方法产生了一个对指定资源的GET请求。
$ ./http_get.rbMy html page Today is a beautiful day. We go swimming and fishing.
Hello there. How are you?
下面的程序得到一个小的网页,并剥离其HTML标签。
#!/usr/bin/ruby require 'net/http' uri = URI "http://example.com" doc = Net::HTTP.get uri puts doc.gsub %r{?[^>]+?>}, ''
脚本剥离了 example
网页的HTML标签。
puts doc.gsub %r{?[^>]+?>}, ''
用一个简单的正则表达式来剥离HTML标签。
在下面的例子中,我们提出了多个异步请求。
#!/usr/bin/ruby require 'async' require 'net/http' require 'uri' urls = [ 'http://webcode.me', 'https://example.com', 'http://httpbin.org', 'https://www.ruby-lang.org' ] Async do urls.each do |url| Async do res = Net::HTTP.get_response(URI url) puts "#{res.message} #{res.code}" end end end
我们向四个网站发出无阻塞的网络请求,并打印它们的状态代码和信息。
$ ./async_req.rb OK 200 OK 200 OK 200 OK 200
Sinatra 是一个流行的Ruby Web应用框架。它很容易安装和设置。我们的一些例子也将使用Sinatra应用程序。
$ sudo gem install sinatra $ sudo gem install thin
我们安装Sinatra和Thin网络服务器。如果安装了Thin,Sinatra会自动选择Thin而不是默认的WEBrick服务器。
#!/usr/bin/ruby require 'sinatra' get '/' do "First application" end
应用程序对 /
路线作出反应。它发送一个简单的消息回给客户端。
$ ruby main.rb == Sinatra (v2.1.0) has taken the stage on 4567 for development with backup from Thin 2021-02-03 11:36:49 +0100 Thin web server (v1.8.0 codename Possessed Pickle) 2021-02-03 11:36:49 +0100 Maximum connections set to 1024 2021-02-03 11:36:49 +0100 Listening on localhost:4567, CTRL+C to stop
用 ruby main.rb
命令启动应用程序。服务器被启动;它在4567端口监听。
$ curl localhost:4567 First application
用 curl
命令行工具,我们连接到服务器并访问 /
路线。控制台上出现了一条信息。
get
方法向服务器发出一个GET请求。GET方法请求一个指定资源的表示。
#!/usr/bin/ruby require 'sinatra' get '/greet' do "Hello #{params[:name]}" end
这是Sinatra应用程序的文件。在收到 /greet
路线时,它返回一个包含由客户发送的名称的消息。
#!/usr/bin/ruby require 'net/http' uri = URI "http://localhost:4567/greet" params = { :name => 'Peter' } uri.query = URI.encode_www_form params puts Net::HTTP.get uri
脚本向Sinatra应用程序发送一个带有数值的变量。该变量是直接在URL中指定的。
params = { :name => 'Peter' }
这是我们发送给服务器的参数。
uri.query = URI.encode_www_form params
我们用 encode_www_form
方法将参数编码到URL中。
puts Net::HTTP.get uri
get
方法向服务器发送了一个GET请求。它返回响应,并打印到控制台。
$ ./client_get.rb Hello Peter
这是该例子的输出。
::1 - - [03/Feb/2021:11:51:46 +0100] "GET /greet?name=Peter HTTP/1.1" 200 11 0.0046
在Thin服务器的这份日志中,我们可以看到参数被编码到了URL中。
我们可以直接把参数放到URL字符串中。
#!/usr/bin/ruby require 'net/http' uri = URI "http://localhost:4567/greet?name=Peter" puts Net::HTTP.get uri
这是另一种发布GET消息的方式;它与前面的例子基本相同。
在这一部分,我们指定了用户代理的名称。
#!/usr/bin/ruby require 'sinatra' get '/agent' do request.user_agent end
Sinatra应用程序返回客户端发送的用户代理。
#!/usr/bin/ruby require 'net/http' uri = URI "http://localhost:4567" http = Net::HTTP.new uri.host, uri.port res = http.get '/agent', {'User-Agent' => 'Ruby script'} puts res.body
这个脚本创建了一个简单的GET请求到Sinatra应用程序。
res = http.get '/agent', {'User-Agent' => 'Ruby script'}
用户代理在 get
方法的第二个参数中被指定。
$ ./client_agent.rb Ruby script
服务器响应了我们随请求发送的代理名称。
post
方法在给定的URL上派发一个POST请求,为填写表单内容提供键/值对。
#!/usr/bin/ruby require 'sinatra' post '/target' do "Hello #{params[:name]}" end
Sinatra应用程序在 /target
路线上返回一个问候语。它从 params
哈希中取值。
#!/usr/bin/ruby require 'net/http' uri = URI "http://localhost:4567/target" params = { :name => 'Peter' } res = Net::HTTP.post_form uri, params puts res.body
脚本发送了一个具有 name
键和 Peter
值的请求。POST请求是用 Net::HTTP.post_form
方法发出的。
$ ./client_post.rb Hello Peter
::1 - - [03/Feb/2021:12:00:54 +0100] "POST /target HTTP/1.1" 200 11 0.0015
用POST方法,值不在请求的URL中发送。
在下面的例子中,我们在www.dictionary.com,找到一个术语的定义。
$ sudo gem install nokogiri
为了解析HTML,我们使用 nokogiri
宝石。
#!/usr/bin/ruby require 'net/http' require 'nokogiri' term = 'cat' uri = URI "https://www.dictionary.com/browse/#{term}" res = Net::HTTP.get uri doc = Nokogiri::HTML res doc.css("span.one-click-content").map do |node| s = node.text.strip! s.gsub!(/\s{3,}/, " ") unless (s == nil) puts s unless (s == nil) end
在这个脚本中,我们在 www.dictionary.com
上找到猫这个词的定义。 Nokogiri::HTML
是用来解析HTML代码的。
uri = URI "https://www.dictionary.com/browse/#{term}"
为了进行搜索,我们在URL的末尾附加该词。
doc = Nokogiri::HTML res doc.css("span.one-click-content").map do |node| s = node.text.strip! s.gsub!(/\s{3,}/, " ") unless (s == nil) puts s unless (s == nil) end
我们用 Nokogiri::HTML
类来解析内容。我们通过删除过多的空白来改善格式。
JSON (JavaScript Object Notation)是一种轻型的数据交换格式。它便于人类阅读和书写,也便于机器解析和生成。
$ sudo gem install json
如果我们以前没有安装过 json
gem,我们必须安装。
require 'sinatra' require 'json' get '/example.json' do content_type :json { :name => 'Jane', :age => 17 }.to_json end
Sinatra应用程序发送JSON数据。它使用 to_json
方法来完成这项工作。
#!/usr/bin/ruby require 'net/http' require 'json' uri = URI 'http://localhost:4567/example.json' res = Net::HTTP.get uri data = JSON.parse res puts data["name"] puts data["age"]
该例子读取Sinatra应用程序发送的JSON数据。
$ ./parse_json.rb Jane 17
接下来,我们从Ruby脚本向Sinatra应用程序发送JSON数据。
require 'sinatra' require 'json' post '/readjson' do data = JSON.parse request.body.read "#{data["name"]} is #{data["age"]} years old" end
这个应用程序读取JSON数据,并将解析后的值发回一个消息。
#!/usr/bin/ruby require 'net/http' require 'json' uri = URI 'http://localhost:4567/readjson' req = Net::HTTP::Post.new uri.path, initheader = {'Content-Type' =>'application/json'} req.body = {:name => 'Jane', :age => 17}.to_json res = Net::HTTP.start(uri.hostname, uri.port) do |http| http.request req end puts res.body
该脚本向Sinatra应用程序发送JSON数据并读取其响应。
req = Net::HTTP::Post.new uri.path, initheader = {'Content-Type' =>'application/json'}
'application/json'
内容类型必须在请求的标题中指定。
$ ./post_json.rb Jane is 17 years old
重定向是将一个URL转发到另一个URL的过程。HTTP响应状态代码302用于临时的URL重定向。
require 'sinatra' get "/oldpage" do redirect to("/files/newpage.html"), 302 end
在Sinatra应用程序中,我们使用 redirect
命令来重定向到一个不同的位置。
New page This is a new page
这是位于 public/files
子目录下的 newpage.html
文件。
#!/usr/bin/ruby require 'net/http' uri = URI 'http://localhost:4567/oldpage' res = Net::HTTP.get_response uri if res.code == "302" res = Net::HTTP.get_response URI res.header['location'] end puts res.body
这个脚本访问旧的页面并跟随重定向。请注意,这只适用于单个重定向。
res = Net::HTTP.get_response URI res.header['location']
头部的location字段包含文件被重定向到的地址。
$ ./redirect.rbNew page This is a new page
::1 - - [03/Feb/2021:13:59:52 +0100] "GET /oldpage HTTP/1.1" 302 - 0.0012 ::1 - - [03/Feb/2021:13:59:52 +0100] "GET /files/newpage.html HTTP/1.1" 200 112 0.0051
从日志中我们看到,请求被重定向到一个新的文件名。通信由两条GET信息组成。
在这个教程中,我们使用了Ruby net/http
模块。在ZetCode上也有类似的 Ruby HTTPClient 和 Ruby Faraday 。