<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>vito (vito)</title>
    <link>https://ruby-china.org/vito</link>
    <description></description>
    <language>en-us</language>
    <item>
      <title>基于 Ruby + Selenium 的第三方广告检测</title>
      <description>&lt;p&gt;博客地址：&lt;a href="http://www.jianshu.com/p/9540e7566192" rel="nofollow" target="_blank"&gt;http://www.jianshu.com/p/9540e7566192&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;本文的工程目的是使用 ruby 编写一个脚本文件，实现对网页中第三方广告的检测和统计。
项目源代码：&lt;a href="https://github.com/vito0705/selenium_vito" rel="nofollow" target="_blank"&gt;https://github.com/vito0705/selenium_vito&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="本文主要内容"&gt;本文主要内容&lt;/h2&gt;
&lt;hr&gt;

&lt;ul&gt;
&lt;li&gt;一。项目分析

&lt;ul&gt;
&lt;li&gt;项目目的&lt;/li&gt;
&lt;li&gt;项目要求&lt;/li&gt;
&lt;li&gt;项目解决思路&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;二。环境配置

&lt;ul&gt;
&lt;li&gt;Linux 下环境配置&lt;/li&gt;
&lt;li&gt;Windows 下环境配置&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;三。程序编写

&lt;ul&gt;
&lt;li&gt;项目设计思路&lt;/li&gt;
&lt;li&gt;代码实现

&lt;ul&gt;
&lt;li&gt;（一）加载库文件&lt;/li&gt;
&lt;li&gt;（二）初始化部分&lt;/li&gt;
&lt;li&gt;（三）网页检测部分&lt;/li&gt;
&lt;li&gt;（四）代码执行部分&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;四。脚本使用&lt;/li&gt;
&lt;li&gt;五。总结&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="一.项目分析"&gt;一。项目分析&lt;/h2&gt;
&lt;hr&gt;
&lt;h2 id="项目目的"&gt;项目目的&lt;/h2&gt;
&lt;p&gt;对页面中的第三方广告进行检测，找出其中隐藏的广告网页并将数据记录下来。&lt;/p&gt;
&lt;h2 id="项目要求"&gt;项目要求&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;检测所有广告及广告的域并记录下来&lt;/li&gt;
&lt;li&gt;统计所有广告的数目及其中隐藏广告的数目&lt;/li&gt;
&lt;li&gt;以表格形式保存数据&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="项目解决思路"&gt;项目解决思路&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;第三方广告都在网页中的&lt;code&gt;iframe&lt;/code&gt;标签中，需要从&lt;code&gt;iframe&lt;/code&gt;标签中获取所需的数据&lt;/li&gt;
&lt;li&gt;根据需要，我们选择&lt;code&gt;selenium&lt;/code&gt;作为 web 自动化测试工具&lt;/li&gt;
&lt;li&gt;数据需要保存在表格中，我们选择&lt;code&gt;spreadsheet&lt;/code&gt;这个 gem 来实现相关功能&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="二.环境配置"&gt;二。环境配置&lt;/h2&gt;
&lt;hr&gt;

&lt;p&gt;Linux 和 windows 下均可以使用这个脚本，但对于环境配置略有不同。&lt;/p&gt;
&lt;h2 id="Linux下环境配置"&gt;Linux 下环境配置&lt;/h2&gt;&lt;h3 id="1.安装ruby"&gt;1.安装 ruby&lt;/h3&gt;
&lt;p&gt;可以参考&lt;a href="https://blog.zengrong.net/post/1933.html" rel="nofollow" target="_blank" title=""&gt;这篇文章&lt;/a&gt;中使用 rvm 管理 ruby 的方式安装，要求 ruby 版本大于等于 2.0，具体安装不作更多说明。&lt;/p&gt;
&lt;h3 id="2.安装ruby版本的selenium"&gt;2.安装 ruby 版本的 selenium&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;在&lt;code&gt;terminal&lt;/code&gt;中执行：&lt;/strong&gt;&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;gem&lt;/span&gt; &lt;span class="n"&gt;install&lt;/span&gt; &lt;span class="n"&gt;selenium&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;webdriver&lt;/span&gt;  
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;a href="https://github.com/SeleniumHQ/selenium/tree/master/rb" rel="nofollow" target="_blank" title=""&gt;selenium-webdriver 的 Github 源码地址&lt;/a&gt;&lt;/p&gt;
&lt;h3 id="3.安装spreadsheet:"&gt;3.安装 spreadsheet:&lt;/h3&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;gem&lt;/span&gt; &lt;span class="n"&gt;install&lt;/span&gt; &lt;span class="n"&gt;spreadsheet&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;a href="https://github.com/zdavatz/spreadsheet" rel="nofollow" target="_blank" title=""&gt;spreadsheet 的 GitHub 源码地址&lt;/a&gt;&lt;/p&gt;
&lt;h3 id="4.安装selenium浏览器驱动driver"&gt;4.安装 selenium 浏览器驱动 driver&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Chrome

&lt;ul&gt;
&lt;li&gt;版本对应：&lt;a href="http://blog.csdn.net/goblinintree/article/details/47335563" rel="nofollow" target="_blank" title=""&gt;ChromeDriver 与 Chrome 版本的对应关系&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;driver 下载：&lt;a href="https://sites.google.com/a/chromium.org/chromedriver/downloads" rel="nofollow" target="_blank" title=""&gt;ChromeDriver - WebDriver for Chrome&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Firefox

&lt;ul&gt;
&lt;li&gt;driver 下载及版本对应：&lt;a href="https://github.com/mozilla/geckodriver/releases" rel="nofollow" target="_blank"&gt;https://github.com/mozilla/geckodriver/releases&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;根据自己的浏览器版本，选择对应的 selenium 浏览器驱动版本 driver 进行下载解压，&lt;strong&gt;将下载解压好的 driver 文件移动到&lt;code&gt;/usr/bin/&lt;/code&gt;文件夹下即可。&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;以上四步，是 linux 下运行程序必要的环境配置，务必保证每一步的正确安装。&lt;/p&gt;
&lt;h2 id="Windows下环境配置"&gt;Windows 下环境配置&lt;/h2&gt;
&lt;p&gt;windows 下的环境配置与 Linux 下略有不同，但思路是相通的。&lt;/p&gt;
&lt;h3 id="1.安装ruby"&gt;1.安装 ruby&lt;/h3&gt;
&lt;p&gt;按照这篇文章&lt;a href="http://www.runoob.com/ruby/ruby-installation-windows.html" rel="nofollow" target="_blank" title=""&gt;《Ruby 安装 - Windows》&lt;/a&gt;安装 ruby 即可，记得勾选&lt;code&gt;Add Ruby executables to your PATH&lt;/code&gt;这一项。同样，要求 ruby 版本大于等于 2.0。&lt;/p&gt;
&lt;h3 id="2.安装ruby版本的selenium"&gt;2.安装 ruby 版本的 selenium&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;在&lt;code&gt;cmd&lt;/code&gt;中执行：&lt;/strong&gt;&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;gem&lt;/span&gt; &lt;span class="n"&gt;install&lt;/span&gt; &lt;span class="n"&gt;selenium&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;webdriver&lt;/span&gt;  
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;a href="https://github.com/SeleniumHQ/selenium/tree/master/rb" rel="nofollow" target="_blank" title=""&gt;selenium-webdriver 的 Github 源码地址&lt;/a&gt;&lt;/p&gt;
&lt;h3 id="3.安装spreadsheet:"&gt;3.安装 spreadsheet:&lt;/h3&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;gem&lt;/span&gt; &lt;span class="n"&gt;install&lt;/span&gt; &lt;span class="n"&gt;spreadsheet&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;a href="https://github.com/zdavatz/spreadsheet" rel="nofollow" target="_blank" title=""&gt;spreadsheet 的 GitHub 源码地址&lt;/a&gt;ß&lt;/p&gt;
&lt;h3 id="4.安装selenium浏览器驱动driver"&gt;4.安装 selenium 浏览器驱动 driver&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Chrome

&lt;ul&gt;
&lt;li&gt;版本对应：&lt;a href="http://blog.csdn.net/goblinintree/article/details/47335563" rel="nofollow" target="_blank" title=""&gt;ChromeDriver 与 Chrome 版本的对应关系&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;driver 下载：&lt;a href="https://sites.google.com/a/chromium.org/chromedriver/downloads" rel="nofollow" target="_blank" title=""&gt;ChromeDriver - WebDriver for Chrome&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Firefox

&lt;ul&gt;
&lt;li&gt;driver 下载及版本对应：&lt;a href="https://github.com/mozilla/geckodriver/releases" rel="nofollow" target="_blank"&gt;https://github.com/mozilla/geckodriver/releases&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;根据自己的浏览器版本，选择对应的 selenium 浏览器驱动版本 driver 进行下载解压，&lt;strong&gt;将下载解压好的 driver 文件放在对应的浏览器安装目录下，之后需要对 Windows 环境变量进行配置。&lt;/strong&gt;
Windows 下需要在系统变量的 path 变量中添加 exe 文件的位置，配置环境变量可参考这篇文章：&lt;a href="https://jingyan.baidu.com/article/d5a880eb6aca7213f047cc6c.html" rel="nofollow" target="_blank" title=""&gt;Win7 怎样添加环境变量&lt;/a&gt;，注意路径中不要有中文。&lt;/p&gt;

&lt;p&gt;同样，这四步也是 Windows 下必备的环境配置。但在自己的测试过程中，由于一些安全问题，Windows 下的 chrome 始终没有调通，但 Firefox 是可以使用的。&lt;/p&gt;
&lt;h2 id="三.程序编写"&gt;三。程序编写&lt;/h2&gt;
&lt;hr&gt;
&lt;h2 id="项目设计思路"&gt;项目设计思路&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;为了能使脚本检测大量网站，我们使用三个文件，一个 txt 文件，一个 xls 表格文件和包含所有逻辑功能的 ruby 文件。

&lt;ul&gt;
&lt;li&gt;weburl.txt：在文件中，每个网址占一行，ruby 文件会依次按行读取此文件中的网址进行检测&lt;/li&gt;
&lt;li&gt;ad_file.xls：用于保存数据，最终的数据会写入这个文件&lt;/li&gt;
&lt;li&gt;detection_ad.rb：所有的数据逻辑处理均包含在这个文件中，负责检测页面中的第三方广告。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;第三方广告都在&lt;code&gt;iframe&lt;/code&gt;标签中，我们的目的是找到这些&lt;code&gt;iframe&lt;/code&gt;标签中的&lt;code&gt;src&lt;/code&gt;，即就是第三方广告的网址。因此我们可以将思路转变为：首先通过 selenium 获取网页的源代码，之后通过 ruby 正则表达式来实现对关键信息的提取。&lt;/li&gt;
&lt;li&gt;对于 selenium 和 spreadsheet 两个 gem 的使用，我们不作过多解释，可以参考以下两篇文章，给出了两个 gem 的基本使用方法。

&lt;ul&gt;
&lt;li&gt;&lt;a href="http://www.51testing.com/html/83/14994683-866551.html" rel="nofollow" target="_blank" title=""&gt;selenium 对浏览器的简单操作（webdriver+ruby）&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://hlee.iteye.com/blog/356510" rel="nofollow" target="_blank" title=""&gt;rails 导出 excel 插件 spreadsheet 的标准使用&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="代码实现"&gt;代码实现&lt;/h2&gt;
&lt;p&gt;代码内容我们分成将四部分来分别说明。&lt;/p&gt;
&lt;h3 id="（一）加载库文件"&gt;（一）加载库文件&lt;/h3&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'rubygems'&lt;/span&gt;  
&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'selenium-webdriver'&lt;/span&gt; 
&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'spreadsheet'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="（二）初始化部分"&gt;（二）初始化部分&lt;/h3&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# 存放网址的文件&lt;/span&gt;
&lt;span class="n"&gt;web_file&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"weburl.txt"&lt;/span&gt;

&lt;span class="c1"&gt;# 创建excel表格实例&lt;/span&gt;
&lt;span class="no"&gt;Spreadsheet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;client_encoding&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"UTF-8"&lt;/span&gt; 
&lt;span class="n"&gt;excel_fil&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Spreadsheet&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Workbook&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;  
&lt;span class="n"&gt;sheet&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;excel_fil&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create_worksheet&lt;/span&gt; &lt;span class="ss"&gt;:name&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"ads_show"&lt;/span&gt;

&lt;span class="c1"&gt;# 创建浏览器driver实例&lt;/span&gt;
&lt;span class="c1"&gt;# driver = Selenium::WebDriver.for :chrome&lt;/span&gt;
&lt;span class="n"&gt;driver&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Selenium&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;WebDriver&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;for&lt;/span&gt; &lt;span class="ss"&gt;:firefox&lt;/span&gt;

&lt;span class="c1"&gt;# 创建三个全局变量&lt;/span&gt;
&lt;span class="c1"&gt;# web_num：excel表单中的行数&lt;/span&gt;
&lt;span class="c1"&gt;# all_ads_num：所有网页的广告总数&lt;/span&gt;
&lt;span class="c1"&gt;# hide_ads_num：所有网页的隐藏广告总数&lt;/span&gt;
&lt;span class="vg"&gt;$web_num&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="vg"&gt;$all_ads_num&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="vg"&gt;$hide_ads_num&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="（三）网页检测部分"&gt;（三）网页检测部分&lt;/h3&gt;
&lt;p&gt;这部分的功能是检测一个网页中的所有第三方广告，找到广告的域并统计广告的数量，进一步需要分离出页面中隐藏的第三方广告。
我们将这部分定义为一个方法：&lt;code&gt;search_ads(driver, web_url_para, sheet)&lt;/code&gt;，这个方法要求三个参数：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;driver：已经创建的浏览器 driver 实例，如&lt;code&gt;driver = Selenium::WebDriver.for :firefox&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;web_url_para：待检测网页网址 url&lt;/li&gt;
&lt;li&gt;sheet：已经创建的 excel 表单实例，如&lt;code&gt;sheet = excel_fil.create_worksheet :name =&amp;gt; "ads_show"&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;接下来会从多个模块来介绍这一部分内容。&lt;/p&gt;
&lt;h4 id="功能块一"&gt;功能块一&lt;/h4&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;web_url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;web_url_para&lt;/span&gt;
&lt;span class="c1"&gt;#--------------------------------------------------------&lt;/span&gt;
&lt;span class="c1"&gt;#web_url_domain:the domian of the web page&lt;/span&gt;
&lt;span class="c1"&gt;#--------------------------------------------------------&lt;/span&gt;
&lt;span class="n"&gt;web_url_domain_raw&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;web_url&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/https?\:\/\/(.*?)\/.*?/&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;web_url_domain&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;web_url_domain_raw&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这部分使用正则匹配获得待检测网址的域，有两个重要的点需要说明。&lt;/p&gt;
&lt;h5 id="1.不同的域"&gt;1.不同的域&lt;/h5&gt;
&lt;p&gt;所谓第三方，指的是在&lt;code&gt;iframe&lt;/code&gt;中嵌入的网页的域与当前网页的域不同。那么什么是域呢？在我之前&lt;a href="http://www.jianshu.com/p/c54a1dbaab24" rel="nofollow" target="_blank" title=""&gt;介绍跨域解决方案 rack-cors 文章里&lt;/a&gt;，举了这样一个例子：&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;那么什么是同源？我们知道，URL 由协议、域名、端口和路径组成，如果两个 URL 的协议、域名和端口相同，则表示他们同源。&lt;br&gt;
我们用一个例子来说明：&lt;br&gt;
    URL: &lt;a href="http://www.example.com:8080/script/jquery.js" rel="nofollow" target="_blank"&gt;http://www.example.com:8080/script/jquery.js&lt;/a&gt;&lt;br&gt;
    在这个 url 中，各个字段分别代表的含义：&lt;br&gt;
    http://——协议&lt;br&gt;
    www——子域名&lt;br&gt;
    example.com——主域名&lt;br&gt;
    8080——端口号&lt;br&gt;
    script/jquery.js——请求的地址&lt;br&gt;
    当协议、子域名、主域名、端口号中任意一各不相同时，都算不同的“域”。不同的域之间相互请求资源，就叫跨域。  &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;因此，需要获得当前网页的域，来和&lt;code&gt;iframe&lt;/code&gt;中的网址作对比，来判断是否属于第三方。&lt;/p&gt;
&lt;h5 id="2.MatchData对象的分组捕获"&gt;2.MatchData 对象的分组捕获&lt;/h5&gt;
&lt;p&gt;这里不对 ruby 中的正则表达式的语法进行详述，仅对其 MatchData 对象中的分组捕获相关的几点做简单的说明。&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;match 方法

&lt;ul&gt;
&lt;li&gt;可以双向使用 match 方法，即正则表达式和字符串对象均可以响应 match 方法。match 方法会将字符串参数转换为正则表达式&lt;/li&gt;
&lt;li&gt;match 与&lt;code&gt;=~&lt;/code&gt;的区别：正则表达式匹配后返回值不同，&lt;code&gt;=~&lt;/code&gt;返回字符串匹配中匹配的开始位置的数字索引，而 match 则返回 MatchData 实例：&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="mf"&gt;2.2&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mo"&gt;017&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"The alphabet starts with abc"&lt;/span&gt; &lt;span class="o"&gt;=~&lt;/span&gt; &lt;span class="sr"&gt;/abc/&lt;/span&gt;  
     &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;25&lt;/span&gt;  
&lt;span class="mf"&gt;2.2&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mo"&gt;01&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="sr"&gt;/abc/&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"The alphabet starts with abc"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  
     &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="c1"&gt;#&amp;lt;MatchData "abc"&amp;gt;  &lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;MatchData 对象

&lt;ul&gt;
&lt;li&gt;当正则表达式通过 match 方法匹配时，返回一个 MatchData 对象；当正则表达式不匹配时，返回&lt;code&gt;nil&lt;/code&gt;&lt;br&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="mf"&gt;2.2&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mo"&gt;01&lt;/span&gt;&lt;span class="mi"&gt;9&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="sr"&gt;/abc/&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"abcd"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;   
 &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="c1"&gt;#&amp;lt;MatchData "abc"&amp;gt;  &lt;/span&gt;
&lt;span class="mf"&gt;2.2&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mo"&gt;020&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="sr"&gt;/abc/&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"bcd"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  
 &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kp"&gt;nil&lt;/span&gt;  
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;分组捕获

&lt;ul&gt;
&lt;li&gt;正则表达式通过&lt;strong&gt;圆括号&lt;/strong&gt;指定捕获（capture）。当一个字符串和模式之间进行正则匹配测试时，通常是想使用字符串，或者更常见的是用字符串的一部分完成一些操作。捕获表示法让用户可以从能够匹配特殊子模式的字符串中，抽取和保存字符子串。&lt;/li&gt;
&lt;li&gt;从 MatchData 对象中得到捕获结果的一个方式是直接通过&lt;strong&gt;数组&lt;/strong&gt;的方式索引对象：&lt;code&gt;0&lt;/code&gt;索引会返回匹配的整个字符串；从&lt;code&gt;1&lt;/code&gt;开始往后，&lt;code&gt;n&lt;/code&gt;的索引会基于从左边的括号开始计数，返回第&lt;code&gt;n&lt;/code&gt;个捕获结果。关于“从左开始计数圆括号”的周期性，用一个例子来说明：&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sr"&gt;/((a)((b)c)(d)?)/&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"abce"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  
    &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="c1"&gt;#&amp;lt;MatchData "abc" 1:"abc" 2:"a" 3:"bc" 4:"b" 5:nil&amp;gt;   &lt;/span&gt;
&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;                &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"abc"&lt;/span&gt;  
&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;                &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"abc"&lt;/span&gt;   
&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;                &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"a"&lt;/span&gt;   
&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;                &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"bc"&lt;/span&gt;   
&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;                &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"b"&lt;/span&gt;   
&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;                &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kp"&gt;nil&lt;/span&gt;        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;不匹配&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  
&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;                &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kp"&gt;nil&lt;/span&gt;        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;超出范围&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  
&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;               &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"b"&lt;/span&gt;   
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;可以肯定的是，上式中，从左边开始计数的成对圆括号之间匹配的结果，与结果严格对应。&lt;/p&gt;
&lt;h4 id="功能块二"&gt;功能块二&lt;/h4&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;driver&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt; &lt;span class="n"&gt;web_url&lt;/span&gt;
&lt;span class="nb"&gt;sleep&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;
&lt;span class="c1"&gt;#--------------------------------------------------------&lt;/span&gt;
&lt;span class="c1"&gt;#get &amp;lt;iframe ...&amp;gt;...&amp;lt;\iframe&amp;gt;&lt;/span&gt;
&lt;span class="c1"&gt;#--------------------------------------------------------&lt;/span&gt;
&lt;span class="n"&gt;html_source&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;driver&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;page_source&lt;/span&gt;
&lt;span class="n"&gt;match_iframe&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;html_source&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;scan&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/(&amp;lt;\s*iframe\s.*?&amp;gt;.*?&amp;lt;\s*\/\s*iframe\s*&amp;gt;)/&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这部分功能是访问目标网页，获取网页源代码，并获得源代码中所有的&lt;code&gt;iframe&lt;/code&gt;标签中的数据。&lt;/p&gt;
&lt;h4 id="功能块三"&gt;功能块三&lt;/h4&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;#--------------------------------------------------------&lt;/span&gt;
&lt;span class="c1"&gt;#select the third party hide ads url from iframe.src&lt;/span&gt;
&lt;span class="c1"&gt;#iframe_src_hide:hide ad url&lt;/span&gt;
&lt;span class="c1"&gt;#ad_hide_num: number&lt;/span&gt;
&lt;span class="c1"&gt;#--------------------------------------------------------&lt;/span&gt;
&lt;span class="n"&gt;iframe_src_hide_raw&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;match_iframe&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;ifr&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;src_match&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ifr&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;to_s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/(&amp;lt;\s*iframe\s.*?(src=\"(.*?)\".*?&amp;gt;))/&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;src_matched_hide&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;src_match&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;gsub&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/\&amp;amp;amp\;/&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s2"&gt;"&amp;amp;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;hide_condition_1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;src_matched_hide&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/.*?\swidth\s*\=\s*\"\s*0\s*px\s*\"\s.*?height\s*=\s*\"\s*0\s*px\s*\".*/&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;hide_condition_2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;src_matched_hide&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/.*?\sheight\s*\=\s*\"\s*0\s*px\s*\"\s.*?width\s*=\s*\"\s*0\s*px\s*\".*/&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;hide_condition_3&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;src_matched_hide&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/.*?style\s*=\s*\".*?width\s*:\s*0\s*px\s*;.*?height\s*:\s*0\s*px.*?\"/&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;hide_condition_4&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;src_matched_hide&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/.*?style\s*=\s*\".*?height\s*:\s*0\s*px\s*;.*?width\s*:\s*0\s*px.*?\"/&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;hide_condition_5&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;src_matched_hide&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/.*?\sdisplay\s*=\s*\"\s*none\s*\"\s*/&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;hide_condition_6&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;src_matched_hide&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/.*?style\s*=\s*\".*?display\s*:\s*none\s*.*?\"/&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;hide_condition_1&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;hide_condition_2&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;hide_condition_3&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;hide_condition_4&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;hide_condition_5&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;hide_condition_6&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="c1"&gt;# alert("123");&lt;/span&gt;
            &lt;span class="n"&gt;src_matched&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;src_match&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;gsub&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/\&amp;amp;amp\;/&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s2"&gt;"&amp;amp;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;src_matched&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;src_matched&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/https?\:\/\/(.*)\/.*/&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;src_matched&lt;/span&gt;
                &lt;span class="n"&gt;domain_judge_raw&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;src_matched&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;to_s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/https?\:\/\/(.*?)\/.*?/&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="n"&gt;domain_judge&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;domain_judge_raw&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
                &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;domain_judge&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_s&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;web_url_domain&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_s&lt;/span&gt;
                    &lt;span class="c1"&gt;#the same domain&lt;/span&gt;
                    &lt;span class="n"&gt;src_matched&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;nil&lt;/span&gt;
                &lt;span class="k"&gt;else&lt;/span&gt;
                    &lt;span class="c1"&gt;#not the same domain&lt;/span&gt;
                    &lt;span class="n"&gt;src_matched&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
                &lt;span class="k"&gt;end&lt;/span&gt;
            &lt;span class="k"&gt;end&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt;
            &lt;span class="n"&gt;src_matched&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;nil&lt;/span&gt;
        &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="n"&gt;iframe_src_hide&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;iframe_src_hide_raw&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;compact&lt;/span&gt;
&lt;span class="n"&gt;ad_hide_num&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;iframe_src_hide&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;size&lt;/span&gt;
&lt;span class="vg"&gt;$hide_ads_num&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="vg"&gt;$hide_ads_num&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;ad_hide_num&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这部分功能是：检测页面中所有的第三方隐藏广告。所谓隐藏广告，就是其&lt;code&gt;iframe&lt;/code&gt;标签中的&lt;code&gt;height&lt;/code&gt;和&lt;code&gt;width&lt;/code&gt;属性的值均为&lt;code&gt;0px&lt;/code&gt;，或者&lt;code&gt;display&lt;/code&gt;属性的值为&lt;code&gt;none&lt;/code&gt;，此时在页面中并不显示这个第三方广告。
这部分代码中，有一个点需要说明：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;src_matched_hide&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;src_match&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;gsub&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/\&amp;amp;amp\;/&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s2"&gt;"&amp;amp;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这句代码的功能是将得到的&lt;code&gt;src&lt;/code&gt;网址中的&lt;code&gt;&amp;amp;amp;&lt;/code&gt;替换为&lt;code&gt;&amp;amp;&lt;/code&gt;。这是因为，在 HTML 中，预留字符必须被替换为字符实体。&lt;a href="http://www.w3school.com.cn/html/html_entities.asp" rel="nofollow" target="_blank" title=""&gt;这里&lt;/a&gt;对 HTML 字符实体进行了较为详细的介绍。
在本例中，我们通过正则表达式得到的 url 中，最常用的&lt;code&gt;&amp;amp;&lt;/code&gt;被转义成了&lt;code&gt;&amp;amp;amp;&lt;/code&gt;，因此需要对其进行修正。而其他的字符实体因为在 url 中使用较少，此处没有进行更多的校验。&lt;/p&gt;
&lt;h4 id="功能块四"&gt;功能块四&lt;/h4&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;#--------------------------------------------------------&lt;/span&gt;
&lt;span class="c1"&gt;#select the third party ads url from iframe.src&lt;/span&gt;
&lt;span class="c1"&gt;#iframe_src:ad url&lt;/span&gt;
&lt;span class="c1"&gt;#ad number&lt;/span&gt;
&lt;span class="c1"&gt;#--------------------------------------------------------&lt;/span&gt;
&lt;span class="n"&gt;iframe_src_show_raw&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;match_iframe&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;ifr&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; 
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;src_match&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ifr&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;to_s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/(&amp;lt;\s*iframe\s.*?(src=\"(.*?)\".*?&amp;gt;))/&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;src_matched_hide&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;src_match&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;gsub&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/\&amp;amp;amp\;/&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s2"&gt;"&amp;amp;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="n"&gt;hide_condition_1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;src_matched_hide&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/.*?\swidth\s*\=\s*\"\s*0\s*px\s*\"\s.*?height\s*=\s*\"\s*0\s*px\s*\".*/&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;hide_condition_2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;src_matched_hide&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/.*?\sheight\s*\=\s*\"\s*0\s*px\s*\"\s.*?width\s*=\s*\"\s*0\s*px\s*\".*/&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;hide_condition_3&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;src_matched_hide&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/.*?style\s*=\s*\".*?width\s*:\s*0\s*px\s*;.*?height\s*:\s*0\s*px.*?\"/&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;hide_condition_4&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;src_matched_hide&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/.*?style\s*=\s*\".*?height\s*:\s*0\s*px\s*;.*?width\s*:\s*0\s*px.*?\"/&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;hide_condition_5&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;src_matched_hide&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/.*?\sdisplay\s*=\s*\"\s*none\s*\"\s*/&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;hide_condition_6&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;src_matched_hide&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/.*?style\s*=\s*\".*?display\s*:\s*none\s*.*?\"/&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="k"&gt;unless&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;hide_condition_1&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;hide_condition_2&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;hide_condition_3&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;hide_condition_4&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;hide_condition_5&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;hide_condition_6&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;src_matched&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;src_match&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;gsub&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/\&amp;amp;amp\;/&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s2"&gt;"&amp;amp;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;src_matched&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;src_matched&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/https?\:\/\/(.*)\/.*/&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;src_matched&lt;/span&gt;
                &lt;span class="n"&gt;domain_judge_raw&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;src_matched&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;to_s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/https?\:\/\/(.*?)\/.*?/&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="n"&gt;domain_judge&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;domain_judge_raw&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
                &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;domain_judge&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_s&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;web_url_domain&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_s&lt;/span&gt;
                    &lt;span class="c1"&gt;#the same domain&lt;/span&gt;
                    &lt;span class="n"&gt;src_matched&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;nil&lt;/span&gt;
                &lt;span class="k"&gt;else&lt;/span&gt;
                    &lt;span class="c1"&gt;#not the same domain&lt;/span&gt;
                    &lt;span class="n"&gt;src_matched&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
                &lt;span class="k"&gt;end&lt;/span&gt;
            &lt;span class="k"&gt;end&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt;
            &lt;span class="n"&gt;src_matched&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;nil&lt;/span&gt;
        &lt;span class="k"&gt;end&lt;/span&gt; 

    &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="n"&gt;iframe_src_show&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;iframe_src_show_raw&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;compact&lt;/span&gt;
&lt;span class="n"&gt;ad_show_num&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;iframe_src_show&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;size&lt;/span&gt;

&lt;span class="c1"&gt;#--------------------------------------------------------&lt;/span&gt;
&lt;span class="c1"&gt;#all ads &lt;/span&gt;
&lt;span class="c1"&gt;#--------------------------------------------------------&lt;/span&gt;
&lt;span class="n"&gt;iframe_src&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;iframe_src_hide&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;iframe_src_show&lt;/span&gt;
&lt;span class="n"&gt;ad_num&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;iframe_src&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;size&lt;/span&gt;

&lt;span class="vg"&gt;$all_ads_num&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="vg"&gt;$all_ads_num&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;ad_num&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这部分功能是：获得所有非隐藏的第三方广告的数据，计算其数量；之后与隐藏的广告数据整合，得到全部广告的数据。&lt;/p&gt;
&lt;h4 id="功能块五"&gt;功能块五&lt;/h4&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;#--------------------------------------------------------&lt;/span&gt;
&lt;span class="c1"&gt;#select ad domian &lt;/span&gt;
&lt;span class="c1"&gt;#src_domain:ad url domain&lt;/span&gt;
&lt;span class="c1"&gt;#--------------------------------------------------------&lt;/span&gt;
&lt;span class="n"&gt;src_domain_raw&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;iframe_src&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;sr&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; 
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;domain_match&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/https?\:\/\/(.*?)\/.*?/&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;domain_matched&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;domain_match&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="n"&gt;src_domain&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;src_domain_raw&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;compact&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这部分功能是：根据获得的所有广告数据，获得这些广告的域。&lt;/p&gt;
&lt;h4 id="功能块六"&gt;功能块六&lt;/h4&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;#--------------------------------------------------------&lt;/span&gt;
&lt;span class="c1"&gt;#file operation&lt;/span&gt;
&lt;span class="c1"&gt;#--------------------------------------------------------&lt;/span&gt;
&lt;span class="n"&gt;sheet&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="vg"&gt;$web_num&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Web url"&lt;/span&gt;
&lt;span class="n"&gt;sheet&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="vg"&gt;$web_num&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;web_url&lt;/span&gt;
&lt;span class="n"&gt;sheet&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="vg"&gt;$web_num&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"The num of ads"&lt;/span&gt;
&lt;span class="n"&gt;sheet&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="vg"&gt;$web_num&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ad_num&lt;/span&gt;
&lt;span class="n"&gt;sheet&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="vg"&gt;$web_num&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"The num of hide ads"&lt;/span&gt;
&lt;span class="n"&gt;sheet&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="vg"&gt;$web_num&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ad_hide_num&lt;/span&gt;
&lt;span class="n"&gt;sheet&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="vg"&gt;$web_num&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"The url domain of ads"&lt;/span&gt;
&lt;span class="n"&gt;sheet&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="vg"&gt;$web_num&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"The url of ads"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s2"&gt;"(The top "&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="no"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ad_hide_num&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s2"&gt;" are hidden ads)"&lt;/span&gt;
&lt;span class="n"&gt;ad_num&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;times&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
    &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="vg"&gt;$web_num&lt;/span&gt;
    &lt;span class="n"&gt;sheet&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;src_domain&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;sheet&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;iframe_src&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="vg"&gt;$web_num&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="vg"&gt;$web_num&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;ad_num&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"This page has searched successfully: &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;web_url_para&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这部分功能是文件操作，负责将得到的数据写入表格中。&lt;/p&gt;

&lt;p&gt;至此，这个方法的内容已经全部介绍完了。尽管我们将这部分内容全部放在一个方法中，但由于全局变量的引入，这部分内容并不能完全的独立。&lt;/p&gt;
&lt;h3 id="（四）代码执行部分"&gt;（四）代码执行部分&lt;/h3&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;File&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;web_file&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;fil&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;fil&lt;/span&gt;
        &lt;span class="n"&gt;fil&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;each&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
            &lt;span class="k"&gt;begin&lt;/span&gt;
                &lt;span class="n"&gt;search_ads&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;driver&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sheet&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;rescue&lt;/span&gt;
                &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"This page has searched unsuccessfully: &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
                &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"Please waiting process..."&lt;/span&gt;
                &lt;span class="n"&gt;driver&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;quit&lt;/span&gt;
                &lt;span class="c1"&gt;# driver = Selenium::WebDriver.for :chrome&lt;/span&gt;
                &lt;span class="n"&gt;driver&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Selenium&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;WebDriver&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;for&lt;/span&gt; &lt;span class="ss"&gt;:firefox&lt;/span&gt;
                &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"Start the next web url"&lt;/span&gt;
                &lt;span class="k"&gt;next&lt;/span&gt;
            &lt;span class="k"&gt;end&lt;/span&gt;
        &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;


&lt;span class="n"&gt;sheet&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"The ads number of all pages"&lt;/span&gt;
&lt;span class="n"&gt;sheet&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="vg"&gt;$all_ads_num&lt;/span&gt;
&lt;span class="n"&gt;sheet&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"The hide ads number of all pages"&lt;/span&gt;
&lt;span class="n"&gt;sheet&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="vg"&gt;$hide_ads_num&lt;/span&gt;

&lt;span class="n"&gt;excel_fil&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;write&lt;/span&gt; &lt;span class="s2"&gt;"ad_file.xls"&lt;/span&gt;

&lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"Detection is complete!"&lt;/span&gt;

&lt;span class="n"&gt;driver&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;quit&lt;/span&gt;  
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这部分负责读取 txt 文件中的网址，并依次执行上述方法，获得我们所需的数据后将其写入表格中。
这里，我们加入了异常处理。对于我们的功能，一些网站会禁止通过 selenium 访问，有时由于网络原因也会导致访问时间过长而失败，因此需要添加异常处理，从而保证程序能够正确地运行下去。&lt;/p&gt;
&lt;h2 id="四.脚本使用"&gt;四。脚本使用&lt;/h2&gt;
&lt;hr&gt;

&lt;p&gt;已经完成的脚本文件在环境配置成功后，可以直接使用，整个工程中共有三个文件：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;detection_ad.rb:
可执行文件，在终端 terminal 中（windows 下为 cmd）执行命令：
&lt;code&gt;ruby
&amp;gt;  ruby detection_ad.rb
&lt;/code&gt;
工程即可正常运行。&lt;/li&gt;
&lt;li&gt;weburl.txt:
这个文件用来放置待检测的网页网址，每一行仅能放置一个网址。程序运行后，脚本会打开&lt;code&gt;weburl.txt&lt;/code&gt;文件，并依次对文件中的所有网址进行检测。
当需要修改此文件名称时，需要在脚本中修改相关代码，将&lt;code&gt;weburl.txt&lt;/code&gt;修改成自己需要的名称：
&lt;code&gt;ruby
web_file = "weburl.txt"
&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;ad_file.xls:
这个文件用于保存数据，脚本运行后处理得到的所有数据会全部写入这个文件中。如果需要将最终数据写入到其他名称的&lt;code&gt;xls&lt;/code&gt;文件中，只需要修改&lt;code&gt;detection_ad.rb&lt;/code&gt;文件中相关代码，将&lt;code&gt;ad_file.xls&lt;/code&gt;改为自己需要的名称：
&lt;code&gt;ruby
excel_fil.write "ad_file.xls"
&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="五.总结"&gt;五。总结&lt;/h2&gt;
&lt;hr&gt;

&lt;p&gt;到这里，我们的整个工程就全部完成了，我们希望达到的目的也都实现了。但这里还有一些疑问或是问题有待解决：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;效率问题：尽管可以实现第三方网页检测，但程序执行的速度相对很慢&lt;/li&gt;
&lt;li&gt;代码并没有封装的很好，且引入了全局变量，当这个脚本用在比较大型的且复杂的工程中时，很可能出现问题&lt;/li&gt;
&lt;li&gt;代码质量有待提高&lt;/li&gt;
&lt;li&gt;由于时间比较急，对代码中变量的名称没有使用的很准确，注释也不是很清晰，需要修正&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;暂时想到这么多，后续需要认真纠正和学习。&lt;/p&gt;</description>
      <author>vito</author>
      <pubDate>Wed, 20 Dec 2017 16:48:56 +0800</pubDate>
      <link>https://ruby-china.org/topics/34741</link>
      <guid>https://ruby-china.org/topics/34741</guid>
    </item>
    <item>
      <title>理解本质的 REST</title>
      <description>&lt;p&gt;REST 本身是一个高度抽象化的架构风格，因而总是很难对它有一个比较深入且印象深刻的理解。写这篇文章的目的，是自己对学习 REST 的一个总结，也希望可以通过这篇文章，能够让读者真正的理解 REST。&lt;/p&gt;
&lt;h2 id="本文主要内容"&gt;本文主要内容&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;什么是 REST

&lt;ul&gt;
&lt;li&gt;REST 概念&lt;/li&gt;
&lt;li&gt;REST 的由来&lt;/li&gt;
&lt;li&gt;REST 的理解&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;REST 的架构约束条件

&lt;ul&gt;
&lt;li&gt;客户/服务器模型&lt;/li&gt;
&lt;li&gt;无状态&lt;/li&gt;
&lt;li&gt;缓存&lt;/li&gt;
&lt;li&gt;统一接口&lt;/li&gt;
&lt;li&gt;分层系统&lt;/li&gt;
&lt;li&gt;小结&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;总结&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="什么是REST"&gt;什么是 REST&lt;/h2&gt;&lt;h2 id="REST的概念"&gt;REST 的概念&lt;/h2&gt;
&lt;p&gt;先来看看百度对 REST 的定义：&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;REST 即表述性状态传递（英文：Representational State Transfer，简称 REST）是 Roy Fielding 博士在 2000 年他的博士论文中提出来的一种&lt;a href="https://baike.baidu.com/item/%E8%BD%AF%E4%BB%B6%E6%9E%B6%E6%9E%84" rel="nofollow" target="_blank" title=""&gt;软件架构&lt;/a&gt;风格。它是一种针对&lt;a href="https://baike.baidu.com/item/%E7%BD%91%E7%BB%9C%E5%BA%94%E7%94%A8" rel="nofollow" target="_blank" title=""&gt;网络应用&lt;/a&gt;的设计和开发方式，可以降低开发的复杂性，提高系统的可伸缩性。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ul&gt;
&lt;li&gt;我们更多的将 REST 称为&lt;strong&gt;表述性状态转移&lt;/strong&gt;。&lt;/li&gt;
&lt;li&gt;所谓的表述性状态转移，是对什么的表述？——&lt;strong&gt;资源&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;REST 省略了主语 Resource（资源），全称是 Resource Representational State Transfer，即&lt;strong&gt;资源表述性状态转移&lt;/strong&gt;。通俗来讲就是：资源在网络中以某种表现形式进行状态转移。&lt;/li&gt;
&lt;li&gt;如果一个架构符合 REST 原则，就称它为 RESTful 架构。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;在对 REST 更深一步的解释之前，我们先来看看 REST 的由来，而这对于 REST 的理解至关重要。&lt;/p&gt;
&lt;h2 id="REST的由来"&gt;REST 的由来&lt;/h2&gt;
&lt;p&gt;首先简单了解一下作者——&lt;strong&gt;Roy Thomas Fielding&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;HTTP/1.0协议专家组成员&lt;/li&gt;
&lt;li&gt;HTTP/1.1协议专家组&lt;strong&gt;负责人&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Apache HTTP 服务器的核心开发者&lt;/li&gt;
&lt;li&gt;Apache 软件基金会合作创始人&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src="http://upload-images.jianshu.io/upload_images/3167335-a1348c2707bd8d19.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" title="" alt="Roy Thomas Fielding"&gt;&lt;/p&gt;

&lt;p&gt;一张图说明 REST 的由来：&lt;/p&gt;

&lt;p&gt;&lt;img src="http://upload-images.jianshu.io/upload_images/3167335-30125b593acff726.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" title="" alt="REST的由来"&gt;&lt;/p&gt;

&lt;p&gt;好吧，这是一张很简陋的图，不过用来解释 REST 的由来足够了。
故事得从远古时期的 HTTP/1.0 协议说起，随着 web 技术的发展，沿用多年且面向静态文档的 HTTP/1.0 协议无法满足 web 应用的开发需求，作为 HTTP/1.0 协议专家组成员之一的 Roy Fielding 脱颖而出，成为了 HTTP/1.1 协议专家组的负责人，负责统筹制定新版本的协议。
Roy Fielding 和他的同事们在制定 HTTP/1.1 协议的过程中，从技术架构层面对 web 之所以能获得巨大成功做了一番深入的研究和总结，之后将这些总结纳入到一套理论框架之中，并利用这套理论框架中的指导原则，指导 HTTP/1.1 协议的设计方向。经过三年的修订，HTTP/1.1 协议于 1999 年 6 月正式成为规范。HTTP/1.1 协议设计取得了极大的成功，在发布之后的十年里，都没有多少人认为有修订的必要。
Fielding 在完成 HTTP/1.1 协议的设计工作之后，回到了加州大学欧文分校继续攻读自己的博士学位。第二年（2000 年）在他的博士学位论文 Architectural Styles and the Design of Network-based Software Architectures 中（中文版名为《架构风格与基于网络的软件架构设计》），Fielding 更为系统、严谨地阐述了这套理论框架，并且使用这套理论框架推导出了一种新的架构风格，并且为这种架构风格取了一个令人轻松愉快的名字“REST”——Representational State Transfer（表述性状态转移）的缩写。
这便是 REST 的由来，可以看出，REST 架构风格，是通过推导 web 的技术架构因素层面而总结出来的，总结出来的理论框架被用来指导 HTTP/1.1 协议的设计方向。那么我们可以这样理解，REST 是 Web 自身的架构风格，REST 是 HTTP/1.1 协议等 Web 规范的设计指导原则，HTTP/1.1 协议正是为实现 REST 风格的架构而设计的。&lt;/p&gt;
&lt;h2 id="REST的理解"&gt;REST 的理解&lt;/h2&gt;&lt;h3 id="什么是web"&gt;什么是 web&lt;/h3&gt;
&lt;p&gt;从 REST 的来源中我们发现，要想深刻理解 REST，首先得了解 web。
先来看一些 web 的相关知识：&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;百度百科
web（World Wide Web）即全球广域网，也称为万维网，它是一种基于超文本和 HTTP 的、全球性的、动态交互的、跨平台的分布式图形信息系统。是建立在 Internet 上的一种网络服务，为浏览者在 Internet 上查找和浏览信息提供了图形化的、易于访问的直观界面，其中的文档及超级链接将 Internet 上的信息节点组织成一个互为关联的网状结构。&lt;/p&gt;

&lt;p&gt;维基百科
&lt;strong&gt;万维网&lt;/strong&gt;（英语：&lt;em&gt;World Wide Web&lt;/em&gt;），亦作“WWW”、“Web”，是一个由许多互相链接的超文本组成的系统，通过互联网访问。
万维网并不等同互联网，万维网只是互联网所能提供的服务其中之一，是靠着互联网运行的一项服务。
&lt;strong&gt;互联网&lt;/strong&gt;和&lt;strong&gt;万维网&lt;/strong&gt;用语经常被使用且没有太多区别。然而，两者是不一样的。互联网是电脑网络互相连接的全球系统。相较之下，万维网是全球收集的文件和其他资源，通过超链接和 URIs 连接。万维网资源通常使用 HTTP 访问，这是互联网通信协议的一种。
万维网的核心部分是由三个标准构成的：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;统一资源标识符（URI），这是一个统一的为资源定位的系统。&lt;/li&gt;
&lt;li&gt;超文本传送协议（HTTP），它负责规定客户端和服务器怎样互相交流。&lt;/li&gt;
&lt;li&gt;超文本标记语言（HTML），作用是定义超文本文档的结构和格式。&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;p&gt;总结来说，web 是一个由许多相互链接的超文本组成的系统，它使用 URI 来定位系统中的每一个资源，并通过 HTTP 协议进行数据的交互。
更抽象的说，Web 是一个分布式信息系统，为超文本文件和其他对象（资源）提供访问接口和访问机制。
理解了什么是 web，我们便可以更好地理解什么是 REST 了。作为 web 自身的架构风格，我们直接给出结论：&lt;strong&gt;REST 本质上是一种分布式超媒体系统的应用层解决方案，它为资源互通和资源管理的分离提出了一系列架构约束和原则，得到一个功能强、性能好、适宜通信的以网络为基础的应用软件架构。&lt;/strong&gt;
这个结论依然很难理解，但我们需要对此有一个概念了解。接下来我们会对 REST 进行更为详细的介绍。&lt;/p&gt;
&lt;h3 id="REST词组"&gt;REST 词组&lt;/h3&gt;
&lt;p&gt;要理解 REST，首先需要理解（Resource）Representational State Transfer 这个词组。&lt;/p&gt;
&lt;h4 id="资源（Resource）"&gt;资源（Resource）&lt;/h4&gt;
&lt;blockquote&gt;
&lt;p&gt;REST 对于信息的核心抽象是资源。任何能被命名的信息都能作为一个资源：一份文档、一个与时间相关的服务（例如，“洛杉矶今日的天气”），一个其他资源的集合、一个非虚拟的对象（例如，人）等等。
换句话说，可以作为创作者的超文本引用的目标（the target of an author's hypertext reference）的任何概念都必须符合资源的定义。资源是到一组实体的概念性映射（a conceptual mapping），而不是在任何特定时刻与该映射相关联的实体本身。
更精确地说，资源 R 是一个随时间变化的成员函数&lt;img src="http://upload-images.jianshu.io/upload_images/3167335-456ecbc50b23f137.latex?imageMogr2/auto-orient/strip" title="" alt=""&gt;该函数根据时间 t 将资源映射到一个实体或值的集合，集合中的值可能是&lt;strong&gt;资源表述&lt;/strong&gt;（resource representations）和/或&lt;strong&gt;资源标识符&lt;/strong&gt;（resource identifiers）（两者是等价的）。
对于一个资源来说，唯一必须静态的是映射的语义，因为语义才是区别资源的关键。
正是资源的这个抽象定义，使得 Web 架构的核心功能得以实现。首先，它包含了很多信息的来源，并没有人为地通过类型或实现对它们加以区分，从而实现了通用性。其次，它允许引用到表述的延迟绑定，从而支持基于请求的性质来进行内容协商。最后，它允许创作者引用一个概念而不是引用此概念的某个单独的表述，从而使得当表述改变时无需修改所有的现有链接（假设创作者使用了正确地标识符）&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;如何来理解“对于一个资源来说，唯一必须静态的是映射的语义，因为语义才是区别资源的关键”这句话呢？举一个简单的例子来说明一下：
“一个 APP 的当前版本”是一个资源，而“一个 APP 的最稳定版本”也是一个资源，尽管这两个资源在某个时刻上可能会映射到相同的值，但它们是是截然不同的，且两个资源能够被单独地标识和引用。&lt;/p&gt;
&lt;h5 id="资源标识符"&gt;资源标识符&lt;/h5&gt;
&lt;blockquote&gt;
&lt;p&gt;REST 使用&lt;strong&gt;资源标识符&lt;/strong&gt;来表示组件之间交互所涉及的特定资源。REST 连接器提供了访问和操作资源的值集合的一个通用的接口，而无须关心其成员函数（membership function）是如何定义的，或者处理请求的软件是何种类型。由命名权威（naming authority）来为资源分配资源标识符，使得引用资源成为可能，映射的语义有效性也由同样的命名权威来负责维护（例如，确保成员函数不会改变）。
传统的超文本系统通常只在一个封闭的或局部的环境中运行，它们使用随信息的变化而改变的唯一节点或文档标识符，并依赖链接服务器（link server）以独立于内容的方式来维护引用。因为集中式的链接服务器完全无法满足 Web 的超大规模和跨越多个组织领域的需求，所以 REST 采用了其他的方式——以来资源的创作者来选择最符合被标识的概念本质的资源标识符。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;资源标识符为访问和操作资源的值集合提供了一个通用的接口。换句话说，我们抽象出来的资源都应该是可标识的，都应该拥有一个明显的 ID——在 Web 中，代表 ID 的统一概念是：URI（统一资源标识符）。URI 构成了一个全局命名空间，使用 URI 标识关键资源意味着这些资源获得了一个唯一、全局的 ID。
举个简单的例子：如果在一个类似于 Amazon.com 的在线商城中，没有用唯一的 ID（一个 URI）标识它的每一件商品，可想而知这将是多么可怕的业务决策。&lt;/p&gt;
&lt;h4 id="表述（Representations）"&gt;表述（Representations）&lt;/h4&gt;
&lt;p&gt;资源的表述是一段对于资源在某个特定时刻的状态的描述。
资源在外界的具体呈现，可以有多种表述 (或称为表现、表示) 形式，在客户端和服务端之间传送的也是资源的表述，而不是资源本身。例如文本资源可以采用 html、xml、json 等格式，图片可以使用 PNG 或 JPG 展现出来。资源的表述包括数据和描述数据的元数据，例如，HTTP 头“Content-Type”就是这样一个元数据属性。
更确切的说：&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;REST 组件使用&lt;strong&gt;表述&lt;/strong&gt;来捕获某个资源的当前状态或预期状态，随后在组件之间移交该表述，同过这种方式在资源上执行各种动作（perform actions on a resource）。表述（representation）有一个字节序列和描述这些字节的&lt;strong&gt;表述元数据&lt;/strong&gt;（representation metadata）构成。表述的其他常用但不精确的名称包括：文档、文件、HTTP 消息实体、实例或变量。
表述由数据、描述数据的元数据、以及（有时候存在的）描述元数据的元数据组成（通常用来验证消息的完整性）。
表述的数据格式被称为媒体类型（media type）。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;简单总结一下：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;资源总是以某种表述为载体显示的，即序列化的信息&lt;/li&gt;
&lt;li&gt;资源的表述是 REST 架构的表现层&lt;/li&gt;
&lt;li&gt;资源可以有多重表述&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="状态转移"&gt;状态转移&lt;/h4&gt;
&lt;p&gt;状态转移：在客户端和服务器端之间转移（transfer）代表资源状态的表述。通过转移和操作资源的表述，来间接实现操作资源的目的。
访问一个网站，就代表了客户端和服务器的一个互动过程。在这个过程中，势必涉及到数据和状态的变化。
互联网通信协议 HTTP 协议，是一个无状态协议。这意味着，所有的资源状态都保存在服务器端。因此，如果客户端想要操作服务器中的资源，必须通过某种手段，让服务器端的资源发生"状态转移"（State Transfer）。而这种转化是建立在表述之上的，所以就是"表述性状态转移"。
客户端使用的手段，在 web 中就是 HTTP 协议。具体来说，就是 HTTP 协议里面，四个表示操作方式的动词：GET、POST、PUT、DELETE。它们分别对应四种基本操作：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;GET——获取资源&lt;/li&gt;
&lt;li&gt;POST——新建资源（也可以用于更新资源）&lt;/li&gt;
&lt;li&gt;PUT——更新资源&lt;/li&gt;
&lt;li&gt;DELETE——删除资源&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="小结"&gt;小结&lt;/h4&gt;
&lt;p&gt;Resource Representational State Transfer，资源表述性状态转移，即就是：根据数据抽象出来的资源，以某种表现形式，通过某种手段，在网络中发生状态转移，以此来间接实现操作资源的目的。表述性状态转移（REST）架构风格是对分布式超媒体系统中的架构元素的一种抽象。
在 web 中，具体而言：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;每一个 URI 代表一种资源；&lt;/li&gt;
&lt;li&gt;客户端和服务器之间，传递这种资源的某种表现层；&lt;/li&gt;
&lt;li&gt;客户端通过四个 HTTP 动词，对服务器端资源进行操作，实现"表现层状态转化"。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;我们再来换一个角度，以搭建系统的角色来思考这个问题：
在 web 中，为了获取我们需要的分布在不同地域的超媒体资源，我们该如何设计这个系统？显然，web 中有着大量的，分布在不同地方的各种类型的资源。我们需要提供的是一个大型分布式超媒体系统的应用层解决方案。
首先我们需要为所需的数据设定唯一标识，因此我们将数据进行抽象为资源，并使用统一资源标识符（URI）为每个资源设定 ID，这样我们就有办法来操作每个资源。
那么该如何操作资源呢？换句话说，当我们看到一个 URI 并将它输入到浏览器中是，为何浏览器知道该怎样处理这个 URI？事实上，浏览器知道如何去处理 URI 的原因在于：所有的资源都支持同样的接口（URI），支持一套同样的方法（HTTP 动词）。这样，当我们自己按照这种方式来定义我们自己的资源时，web 中的其他人便可以轻松的获取这些资源。
获取资源时，我们可能需要不同的呈现方式或是需求，因此我们需要对资源进行表述，使其表现为我们需要的形式。&lt;/p&gt;

&lt;p&gt;从分布式系统的角度来看 REST，我们发现以资源为核心的 REST 确实提供了一种解决大型分布式资源系统的解决方案，而 web 的成功也确实验证了这套理论的正确性。&lt;/p&gt;
&lt;h2 id="REST的架构约束条件"&gt;REST 的架构约束条件&lt;/h2&gt;
&lt;p&gt;REST 作为一种组织 web 服务的架构风格，提出了一系列架构级约束。如果一个系统满足这些约束，那该系统就被称为是 RESTful 的。接下来，我们会逐条说明 REST 的五条必要约束。&lt;/p&gt;
&lt;h2 id="客户/服务器模型"&gt;客户/服务器模型&lt;/h2&gt;
&lt;p&gt;通信只能由客户端单方面发起，表现为请求 - 响应的形式。&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;客户 - 服务器约束背后的原则是分离关注点。通过分离用户界面和数据存储这两个关注点，我们改善了用户界面跨多个平台的可移植性；同时通过简化服务器组件，改善了系统的可伸缩性。然而，对于 Web 来说，最重要的是这种关注点的分离使得组件可独立地进化，从而支持多个组织领域的互联网规则的需求。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;img src="http://upload-images.jianshu.io/upload_images/3167335-db1f5adb20e1be1b.PNG?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" title="" alt=""&gt;&lt;/p&gt;
&lt;h2 id="无状态"&gt;无状态&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;我们接下来在为客户 - 服务器交互添加一个架构约束：通信必须在本质上是无状态的，从客户到服务器的每个请求都必须包含理解该请求所必需的所有信息，不能利用任何存储在服务器端的上下文，会话状态因此要全部保存在客户端。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;img src="http://upload-images.jianshu.io/upload_images/3167335-b0da5cd568cf2bff.PNG?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;前面我们分析 REST 词组时，提到了资源的状态转移，而在这里，REST 约束中又包含了无状态通信原则，看起来仿佛是矛盾了：既然“无状态”，又怎么能说“状态转移”呢？
  其实，这里说的无状态通信原则，并不是说客户端应用不能有状态，而是指服务端不应该保存客户端状态。&lt;/p&gt;
&lt;h3 id="应用状态与资源状态"&gt;应用状态与资源状态&lt;/h3&gt;
&lt;p&gt;状态应该区分应用状态和资源状态，客户端负责维护应用状态，而服务端维护资源状态。客户端与服务端的交互必须是无状态的，并在每一次请求中包含处理该请求所需的一切信息。服务端不需要在请求间保留应用状态，只有在接受到实际请求的时候，服务端才会关注应用状态。 
这种无状态通信原则，使得服务端和中介能够理解独立的请求和响应。在多次请求中，同一客户端也不再需要依赖于同一服务器，方便实现高可扩展和高可用性的服务端。&lt;/p&gt;
&lt;h3 id="优点"&gt;优点&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;可见性——监视系统不必为了确定一个请求的全部性质去查看该请求之外的多个请求&lt;/li&gt;
&lt;li&gt;可靠性——减轻了从局部故障中恢复的任务量&lt;/li&gt;
&lt;li&gt;可伸缩性——不必在多个请求之间保存状态，从而允许服务器组件迅速释放资源，并进一步简化其实现，因为服务器不必跨多个请求管理资源的使用情况&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="缺点"&gt;缺点&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;由于不能将状态数据保存在服务器的共享上下文中，因此增加了一系列请求中发送的重复数据（每次交互的开销），可能会降低网络性能。此外，将应用状态放在客户端还降低了服务器对于一致的应用行为的控制能力，因为这样一来，应用就得依赖多个客户端版本的语义的正确实现。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="缓存"&gt;缓存&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;为了改善网络的效率，我们添加了缓存这个架构约束。缓存架构要求一个请求的响应中的数据被隐式地或显式地标记为可缓存的或不可缓存的。如果响应是缓存的，那么客户端缓存就可以为以后的相同请求重用这个响应的数据。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;img src="http://upload-images.jianshu.io/upload_images/3167335-1d401e39421189d3.PNG?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" title="" alt=""&gt;&lt;/p&gt;
&lt;h3 id="优点"&gt;优点&lt;/h3&gt;
&lt;p&gt;添加缓存可能部分或全部消除一些交互，从而通过减少一系列交互的平均延迟时间，来提高效率、可伸缩性和用户感知的性能。&lt;/p&gt;
&lt;h3 id="缺点"&gt;缺点&lt;/h3&gt;
&lt;p&gt;如果缓存中陈旧的数据与将请求直接发送到服务器得到的数据差别极大，那么缓存会降低可靠性。&lt;/p&gt;
&lt;h2 id="统一接口"&gt;统一接口&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;使 REST 架构风格区别于其他基于网络的架构风格的核心特征是，它强调组件之间要有一个统一的接口。通过在组件接口上应用通用性的软件工程原则，简化了正特的系统架构，也改善了交互的可见性。实现与它们所提供的服务是解耦的的，这促进了独立地可进化性。
然而，需要的付出的代价是，统一接口降低了效率，因为信息都使用标准化的形式来移交，而不能使用特定于应用的需求的形式。REST 接口被设计为可以高效地移交大粒度的超媒体数据，并针对 Web 的常见情况做了优化，但是这也导致该接口对于其他形式的架构交互而言不是最优的。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;img src="http://upload-images.jianshu.io/upload_images/3167335-8eabfb3fb5f5b65e.PNG?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;为了获得统一的接口，需要有多个架构约束来指导组件的行为。REST 由四个接口架构约束来定义：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;资源的识别（identification of resources）&lt;/li&gt;
&lt;li&gt;通过表述来操作资源（manipulation of resources through representations）&lt;/li&gt;
&lt;li&gt;自描述的信息（self-descriptive messages）&lt;/li&gt;
&lt;li&gt;超媒体作为应用状态引擎（hypermedia as the engine of application state，简称 HATEOAS）&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="资源的识别"&gt;资源的识别&lt;/h3&gt;
&lt;p&gt;每个资源都拥有一个资源标识。每个资源的资源标识可以用来唯一地标明该资源。&lt;/p&gt;
&lt;h3 id="通过表述来操作资源"&gt;通过表述来操作资源&lt;/h3&gt;
&lt;p&gt;这里说的是资源的自描述性。一个 REST 系统所返回的资源需要能够描述自身，并提供足够的用于操作该资源的信息，比如如何对资源进行添加，删除以及修改等操作。也就是说，一个典型的 REST 服务不需要额外的文档对如何操作资源进行说明。&lt;/p&gt;
&lt;h3 id="自描述的信息"&gt;自描述的信息&lt;/h3&gt;
&lt;p&gt;消息的自描述性。在 REST 系统中所传递的消息需要能够提供自身如何被处理的足够信息。例如该消息所使用的 MIME 类型，是否可以被缓存等。&lt;/p&gt;
&lt;h3 id="超媒体作为应用状态引擎"&gt;超媒体作为应用状态引擎&lt;/h3&gt;
&lt;p&gt;即客户只可以通过服务端所返回各结果中所包含的信息来得到下一步操作所需要的信息，如到底是向哪个 URL 发送请求等。也就是说，一个典型的 REST 服务不需要额外的文档标示通过哪些 URL 访问特定类型的资源，而是通过服务端返回的响应来标示到底能在该资源上执行什么样的操作。一个 REST 服务的客户端也不需要知道任何有关哪里有什么样的资源这种信息。&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;这个描述的核心是超媒体概念，换句话说：是链接的思想。链接是我们在 HTML 中常见的概念，但是它的用处绝不局限于此（用于人们网络浏览）。考虑一下下面这个虚构的 XML 片段：&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;order self="http://example.com/customers/1234"&amp;gt; 
   &amp;lt;amount&amp;gt;23&amp;lt;/amount&amp;gt; 
   &amp;lt;product ref="http://example.com/products/4554"&amp;gt; 
   &amp;lt;customer ref="http://example.com/customers/1234"&amp;gt; 
&amp;lt;/customer&amp;gt; &amp;lt;/product&amp;gt;&amp;lt;/order&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如果你观察文档中 product 和 customer 的链接，就可以很容易地想象到，应用程序（已经检索过文档）如何“跟随”链接检索更多的信息。当然，如果使用一个遵守专用命名规范的简单“id”属性作为链接，也是可行的——但是仅限于应用环境之内。使用 URI 表示链接的优雅之处在于，链接可以指向由不同应用、不同服务器甚至位于另一个大陆上的不同公司提供的资源——因为 URI 命名规范是全球标准，构成 Web 的所有资源都可以互联互通。
超媒体原则还有一个更重要的方面——应用“状态”。简而言之，实际上服务器端（如果你愿意，也可以叫服务提供者）为客户端（服务消费者）提供一组链接，使客户端能通过链接将应用从一个状态改变为另一个状态。目前，只需要记住：链接是构成动态应用的非常有效的方式。
对此原则总结如下：任何可能的情况下，使用链接指引可以被标识的事物（资源）。也正是超链接造就了现在的 Web。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="分层系统"&gt;分层系统&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;为了进一步改善与互联网规模这个需求相关的行为，我们添加了分层系统架构约束。分层系统风格通过限制组件的行为（即，每个组件只能“看到”与其相交互的相邻层），将架构分解为若干层级。通过将组件对系统的知识限制在单一层级内，为整个系统的复杂性设置了边界，并且提高了底层独立性。我们能够使用层级来封装遗留服务，使新的服务免受遗留客户端的影响，做法是将不常用功能转移到一个共享中间组件中，从而简化组件的实现。中间组件还能够通过支持跨多个网络和处理器的负载均衡，来改善系统的可伸缩性。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;img src="http://upload-images.jianshu.io/upload_images/3167335-7fc2560ab12b5361.PNG?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;中间件： 
中间件是一种独立的系统软件或服务程序，能够连接两个独立软件或系统。分布式应用软件借助于中间件能够在不同的技术之间共享资源。即：中间件使得若干个相互独立的系统，在各自都拥有着不同的接口的情况下，仍然能通过中间件来实现通信。执行中间件的一个关键途径是信息的传递。通过中间件，应用程序可以工作在多个平台及 OS 环境中。简而言之，中间件即桥梁。&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;分层系统的主要缺点：增加了数据处理的开销和延迟，因此降低了用户感知的性能。对于一个支持缓存架构约束的基于网络的系统来说，可以通过在中间层使用共享缓存所获得的好处来弥补这一缺点。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="小结"&gt;小结&lt;/h2&gt;
&lt;p&gt;REST 架构风格由一组经过选择的架构约束组成，通过这些架构约束在候选架构上产生所期待的架构属性。尽管能够独立考虑其中每一个架构约束，但是根据它们在公共架构风格（common architectural styles）中的来源来对它们进行描述，使得我们理解选择它们背后的基础理论更加容易。&lt;/p&gt;
&lt;h2 id="总结"&gt;总结&lt;/h2&gt;
&lt;p&gt;本文试图从本质上来理解什么是 REST。
我们首先从 REST 的起源说起，发现 REST 与 Web 之间的本质关系，并从 Web 的特性，得到 REST 本质上是一个分布式超媒体系统的应用层解决方案这一结论。接着我们对 REST，即（Resource）Representational State Transfer（资源表述性状态转移）这个词组进行了详细分析，进一步得到了 REST 以资源为核心的架构风格。最后，我们对 REST 架构的五条必要约束条件进行进一步的阐述和说明，以便读者能够更为深刻地理解 REST。
这篇文章到这里就算是结束了，笔者在写下这些内容的时候依然时时感到自己知识的匮乏，以致无法更为深刻地理解 REST。笔者的这篇博客，既是希望能对自己所学做一个总结，也希望能给其他初学者带来一点帮助。文中若有理解不当的地方，欢迎批评指点。&lt;/p&gt;
&lt;h2 id="参考资料"&gt;参考资料&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://www.ics.uci.edu/~fielding/pubs/dissertation/top.htm" rel="nofollow" target="_blank" title=""&gt;Roy Fielding 博士论文英文版&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Roy Fielding 博士论文中文版&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.infoq.com/cn/articles/rest-introduction/" rel="nofollow" target="_blank" title=""&gt;深入浅出 REST&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.cnblogs.com/loveis715/p/4669091.html" rel="nofollow" target="_blank" title=""&gt;REST 简介&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.ruanyifeng.com/blog/2011/09/restful" rel="nofollow" target="_blank" title=""&gt;理解 RESTful 架构 ( 阮一峰)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.infoq.com/cn/articles/understanding-restful-style" rel="nofollow" target="_blank" title=""&gt;理解本真的 REST 架构风格&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://kb.cnblogs.com/page/512047/" rel="nofollow" target="_blank" title=""&gt;RESTful 架构详解&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://blog.igevin.info/posts/restful-architecture-in-general/" rel="nofollow" target="_blank" title=""&gt;RESTful 架构风格概述&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <author>vito</author>
      <pubDate>Mon, 09 Oct 2017 22:23:51 +0800</pubDate>
      <link>https://ruby-china.org/topics/34341</link>
      <guid>https://ruby-china.org/topics/34341</guid>
    </item>
    <item>
      <title>关于站内没有博客系统的一点疑问</title>
      <description>&lt;p&gt;各位大佬好，小弟半年前开始学习 rails，学习过程中，根据自己对 rails 的理解以及遇到的一些问题，写了一些博客。不久前才加入本站，原本希望可以把自己写的 blog 也迁到本站，但发现本站似乎没有适合直接放置自己 blog 的地方。&lt;/p&gt;

&lt;p&gt;小弟在学习的过程中从本站受益匪浅，解决了很多自己学习中的困惑。小弟看到在站点中也有关于分享的站点，虽然同是对知识的普及，但博客似乎更适合记录一些自己学习中的问题及其解决方案，而分享更偏向更深层次的思考与学习。譬如对于自己这样的新手，可能会写对一些基础知识的理解性的博客，但这样的内容似乎不适合用做分享。&lt;/p&gt;

&lt;p&gt;因此小弟在这里有一点疑惑：作为一个很大的站点，应该很多人都考虑过这个问题，我想这应该是思虑良久之后做出的决定，但小弟确实没有想明白，所以很想知道为什么 ruby china 中没用使用 blog 系统，而主要以 topics 的形式来进行，还请各位大佬可以指点一下。&lt;/p&gt;

&lt;p&gt;另外，在没有 blog 系统的情况下，自己该如何把自己写的一些博客迁到本站，毕竟作为一个新手，很希望能有大佬对自己的学习知识和方法给出一点意见和建议，也希望自己能为后面学习 rails 的人提供一点点帮助。（目前自己的 blog 主要写在简书和 csdn 上，但毕竟不是 Rubyist 的聚集地，很难得到相关知识的讨论）&lt;/p&gt;</description>
      <author>vito</author>
      <pubDate>Sat, 24 Dec 2016 16:46:27 +0800</pubDate>
      <link>https://ruby-china.org/topics/31995</link>
      <guid>https://ruby-china.org/topics/31995</guid>
    </item>
  </channel>
</rss>
