【问题标题】:Regex to find URL parameters in HTML (Ruby)正则表达式在 HTML (Ruby) 中查找 URL 参数
【发布时间】:2016-11-01 16:34:03
【问题描述】:

我正在尝试用动态创建的电子邮件模板中的缩略图替换嵌入的 YouTube 视频。我试图从每个嵌入式 URL 中找到每个 YouTube ID,然后用自定义 HTML 替换整个块。如果只有一个带有以下正则表达式的嵌入视频,我就可以正常工作:

<span contenteditable="false" draggable="true" fr-original-class="fr-video\sfr-dvb\sfr-draggable"\s.*\ssrc="[a-z:]*?\/\/w{3}?.?youtube.com\/embed\/([a-zA-Z\d\-]*).*<\/iframe><\/span>

问题是,如果有多个视频,它只会从最后一个视频中找到 ID。我觉得我可能过于复杂了。

请注意,嵌入视频所在的 span 的属性将始终相同 (contenteditable="false" draggable="true" fr-original-class="fr-video)。

下面是一个示例电子邮件模板,上面的 RegEx 仅从中提取第二个 ID,而不是第一个。我想两个都拉。

这是在 Ruby 中完成的。

编辑:我意识到我正在使用的 RegEx 可能有点矫枉过正,但我​​需要一个复杂的 RegEx 来替换 gsub,以便我只替换视频及其容器,而不是它周围的任何东西。

<!DOCTYPE html>
<html>
  <head>
    <meta content='text/html; charset=UTF-8' http-equiv='Content-Type'>
  </head>
  <body style='margin: 0px; font-family: Helvetica Neue,Helvetica,Arial,sans-serif; font-size: 18px;'>
    <table border='0' cellpadding='0' cellspacing='0' style='font-family: Helvetica Neue,Helvetica,Arial,sans-serif; width: 600px;' width='600'>
      <tr>
        <td>
          FooBar
          <br>
          <br>
          <span contenteditable="false" draggable="true" fr-original-class="fr-video fr-dvb fr-draggable" fr-original-style="-webkit-user-select: none;" style="-webkit-user-select: none; text-align: center; position: relative; display: block; clear: both;">
            <iframe src="//cdn.embedly.com/widgets/media.html?src=https://www.youtube.com/embed/e7zCqsjK1Vg?feature=oembed&amp;url=http://www.youtube.com/watch?v=e7zCqsjK1Vg&amp;image=https://i.ytimg.com/vi/e7zCqsjK1Vg/hqdefault.jpg&amp;key=2aa3c4d5f3de4f5b9120b660ad850dc9&amp;type=text/html&amp;schema=youtube" width="600" height="338" scrolling="no" frameborder="0" allowfullscreen="" style="box-sizing: content-box; max-width: 100%; border: 0px;" fr-original-style="box-sizing: content-box; max-width: 100%; border: 0px;" fr-original-class="embedly-embed"></iframe>
          </span>
          <br>
          Foo Bar
          <br>
          <br>
          <span contenteditable="false" draggable="true" fr-original-class="fr-video fr-dvb fr-draggable" fr-original-style="-webkit-user-select: none;" style="-webkit-user-select: none; text-align: center; position: relative; display: block; clear: both;">
            <iframe src="//cdn.embedly.com/widgets/media.html?src=https://www.youtube.com/embed/skLz87ixE48?feature=oembed&amp;url=http://www.youtube.com/watch?v=skLz87ixE48&amp;image=https://i.ytimg.com/vi/skLz87ixE48/hqdefault.jpg&amp;key=2aa3c4d5f3de4f5b9120b660ad850dc9&amp;type=text/html&amp;schema=youtube" width="600" height="338" scrolling="no" frameborder="0" allowfullscreen="" style="box-sizing: content-box; max-width: 100%; border: 0px;" fr-original-style="box-sizing: content-box; max-width: 100%; border: 0px;" fr-original-class="embedly-embed"></iframe>
          </span>
          <br>
        </td>
      </tr>
      <tr style='font-family: Helvetica Neue,Helvetica,Arial,sans-serif; font-size: 12px; color: #656565; text-align: center;'>
        <td style='padding: 10px 0px;'>
        </td>
      </tr>
    </table>
  </body>
</html>

【问题讨论】:

  • 所以如果我理解正确的话,你想用正则表达式做两件事吗?其中之一是删除包含 YouTube 嵌入的 &lt;span&gt;...&lt;/span&gt;s?第二个是捕获这些 YouTube 嵌入的 ID?
  • @wcarroll 是正确的。分开做这两个操作很好。我想匹配嵌入的 ID,对于我找到的每个 ID,用我生成的自定义 HTML 替换 YouTube 嵌入及其容器。我当前的 RegEx 找到了第一个嵌入的开头 (&lt;span&gt;) 并与第二个嵌入的结尾 (&lt;/span&gt;) 匹配,这显然不是我想要的。
  • 强烈建议您在处理 HTML 或 XML 时使用解析器而不是正则表达式。有关历史讨论,请参阅 stackoverflow.com/questions/1732348/…。 Ruby 的实际解析器是Nokogiri。 Nokogiri 无需使用subgsub 即可轻松查找特定节点、提取信息和修改DOM。
  • @theTinMan 这绝对比使用gsub 更有意义。感谢您的提醒。

标签: html ruby regex replace


【解决方案1】:

不要为此使用正则表达式。有一些现有的工具可以让它变得更容易:

require 'nokogiri'

doc = Nokogiri::HTML(<<EOT)
<!DOCTYPE html>
<html>
  <body>
    <table>
      <tr>
        <td>
          <span>
            <iframe src="//cdn.embedly.com/widgets/media.html?src=https://www.youtube.com/embed/e7zCqsjK1Vg?feature=oembed&amp;url=http://www.youtube.com/watch?v=e7zCqsjK1Vg&amp;image=https://i.ytimg.com/vi/e7zCqsjK1Vg/hqdefault.jpg&amp;key=2aa3c4d5f3de4f5b9120b660ad850dc9&amp;type=text/html&amp;schema=youtube" width="600" height="338" scrolling="no" frameborder="0" allowfullscreen="" style="box-sizing: content-box; max-width: 100%; border: 0px;" fr-original-style="box-sizing: content-box; max-width: 100%; border: 0px;" fr-original-class="embedly-embed"></iframe>
          </span>
          <span>
            <iframe src="//cdn.embedly.com/widgets/media.html?src=https://www.youtube.com/embed/skLz87ixE48?feature=oembed&amp;url=http://www.youtube.com/watch?v=skLz87ixE48&amp;image=https://i.ytimg.com/vi/skLz87ixE48/hqdefault.jpg&amp;key=2aa3c4d5f3de4f5b9120b660ad850dc9&amp;type=text/html&amp;schema=youtube" width="600" height="338" scrolling="no" frameborder="0" allowfullscreen="" style="box-sizing: content-box; max-width: 100%; border: 0px;" fr-original-style="box-sizing: content-box; max-width: 100%; border: 0px;" fr-original-class="embedly-embed"></iframe>
          </span>
        </td>
      </tr>
    </table>
  </body>
</html>
EOT

此时很容易将search 用于&lt;span&gt; 标签。这是第一个:

doc.search('span').first.to_html
# => "<span>\n            <iframe src=\"//cdn.embedly.com/widgets/media.html?src=https://www.youtube.com/embed/e7zCqsjK1Vg?feature=oembed&amp;url=http://www.youtube.com/watch?v=e7zCqsjK1Vg&amp;image=https://i.ytimg.com/vi/e7zCqsjK1Vg/hqdefault.jpg&amp;key=2aa3c4d5f3de4f5b9120b660ad850dc9&amp;type=text/html&amp;schema=youtube\" width=\"600\" height=\"338\" scrolling=\"no\" frameborder=\"0\" allowfullscreen=\"\" style=\"box-sizing: content-box; max-width: 100%; border: 0px;\" fr-original-style=\"box-sizing: content-box; max-width: 100%; border: 0px;\" fr-original-class=\"embedly-embed\"></iframe>\n          </span>"

last 或常规数组索引可用于在必要时查找特定实例。

我们可以使用at,而不是使用searchfirst,这已经在内部实现了:

doc.at('span').to_html
# => "<span>\n            <iframe src=\"//cdn.embedly.com/widgets/media.html?src=https://www.youtube.com/embed/e7zCqsjK1Vg?feature=oembed&amp;url=http://www.youtube.com/watch?v=e7zCqsjK1Vg&amp;image=https://i.ytimg.com/vi/e7zCqsjK1Vg/hqdefault.jpg&amp;key=2aa3c4d5f3de4f5b9120b660ad850dc9&amp;type=text/html&amp;schema=youtube\" width=\"600\" height=\"338\" scrolling=\"no\" frameborder=\"0\" allowfullscreen=\"\" style=\"box-sizing: content-box; max-width: 100%; border: 0px;\" fr-original-style=\"box-sizing: content-box; max-width: 100%; border: 0px;\" fr-original-class=\"embedly-embed\"></iframe>\n          </span>"

我们可以挖掘一个节点来获取它的参数:

doc.at('iframe')['src']
# => "//cdn.embedly.com/widgets/media.html?src=https://www.youtube.com/embed/e7zCqsjK1Vg?feature=oembed&url=http://www.youtube.com/watch?v=e7zCqsjK1Vg&image=https://i.ytimg.com/vi/e7zCqsjK1Vg/hqdefault.jpg&key=2aa3c4d5f3de4f5b9120b660ad850dc9&type=text/html&schema=youtube"

一旦你有了一个 URL,我们也有处理它们的工具:

require 'uri'
iframe = doc.at('iframe')
uri = URI.parse('http:' + iframe['src'])

我们可以提取查询:

uri.query # => "src=https://www.youtube.com/embed/e7zCqsjK1Vg?feature=oembed&url=http://www.youtube.com/watch?v=e7zCqsjK1Vg&image=https://i.ytimg.com/vi/e7zCqsjK1Vg/hqdefault.jpg&key=2aa3c4d5f3de4f5b9120b660ad850dc9&type=text/html&schema=youtube"

我们可以把它解析成一个哈希,这样就很容易把它分开:

URI::decode_www_form(uri.query).to_h['src']
# => "https://www.youtube.com/embed/e7zCqsjK1Vg?feature=oembed"

...或修改它:

query = URI::decode_www_form(uri.query).to_h
query['src'] = 'http://example.com'

uri.query = URI::encode_www_form(query)

uri.to_s
# => "http://cdn.embedly.com/widgets/media.html?src=http%3A%2F%2Fexample.com&url=http%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3De7zCqsjK1Vg&image=https%3A%2F%2Fi.ytimg.com%2Fvi%2Fe7zCqsjK1Vg%2Fhqdefault.jpg&key=2aa3c4d5f3de4f5b9120b660ad850dc9&type=text%2Fhtml&schema=youtube"

一旦你在那里,如有必要,很容易修改 HTML:

iframe['src'] = uri.to_s
iframe.to_html
# => "<iframe src=\"http://cdn.embedly.com/widgets/media.html?src=http%3A%2F%2Fexample.com&amp;url=http%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3De7zCqsjK1Vg&amp;image=https%3A%2F%2Fi.ytimg.com%2Fvi%2Fe7zCqsjK1Vg%2Fhqdefault.jpg&amp;key=2aa3c4d5f3de4f5b9120b660ad850dc9&amp;type=text%2Fhtml&amp;schema=youtube\" width=\"600\" height=\"338\" scrolling=\"no\" frameborder=\"0\" allowfullscreen=\"\" style=\"box-sizing: content-box; max-width: 100%; border: 0px;\" fr-original-style=\"box-sizing: content-box; max-width: 100%; border: 0px;\" fr-original-class=\"embedly-embed\"></iframe>"

和:

doc.to_html
# => "<!DOCTYPE html>\n<html>\n  <body>\n    <table>\n      <tr>\n        <td>\n          <span>\n            <iframe src=\"http://cdn.embedly.com/widgets/media.html?src=http%3A%2F%2Fexample.com&amp;url=http%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3De7zCqsjK1Vg&amp;image=https%3A%2F%2Fi.ytimg.com%2Fvi%2Fe7zCqsjK1Vg%2Fhqdefault.jpg&amp;key=2aa3c4d5f3de4f5b9120b660ad850dc9&amp;type=text%2Fhtml&amp;schema=youtube\" width=\"600\" height=\"338\" scrolling=\"no\" frameborder=\"0\" allowfullscreen=\"\" style=\"box-sizing: content-box; max-width: 100%; border: 0px;\" fr-original-style=\"box-sizing: content-box; max-width: 100%; border: 0px;\" fr-original-class=\"embedly-embed\"></iframe>\n          </span>\n          <span>\n            <iframe src=\"//cdn.embedly.com/widgets/media.html?src=https://www.youtube.com/embed/skLz87ixE48?feature=oembed&amp;url=http://www.youtube.com/watch?v=skLz87ixE48&amp;image=https://i.ytimg.com/vi/skLz87ixE48/hqdefault.jpg&amp;key=2aa3c4d5f3de4f5b9120b660ad850dc9&amp;type=text/html&amp;schema=youtube\" width=\"600\" height=\"338\" scrolling=\"no\" frameborder=\"0\" allowfullscreen=\"\" style=\"box-sizing: content-box; max-width: 100%; border: 0px;\" fr-original-style=\"box-sizing: content-box; max-width: 100%; border: 0px;\" fr-original-class=\"embedly-embed\"></iframe>\n          </span>\n        </td>\n      </tr>\n    </table>\n  </body>\n</html>\n"

这并不完全是如何解决您所询问的问题的示例,而是提醒我们存在基于规格且经过良好测试的现有车轮,我们应该使用它们。

【讨论】:

  • 我可能不得不同时使用这两种方法,我只想拉取已嵌入 YouTube 视频的 &lt;span&gt; 节点。
  • 不,可以不用复杂的正则表达式,使用 Nokogiri 和 URI。阅读 CSS 选择器以及如何搜索内部参数,或了解 XPath。这些已经在 SO 和互联网上讨论过很多次了。
  • 好吧,你绝对正确。只需使用 Nokogiri 就可以非常优雅地完成这项工作。非常感谢!
  • 我很高兴它有帮助。使用解析器的好处直到您编写了几个爬虫或蜘蛛并看到在 DOM 中扎根是多么容易,或者您正在解析或操作 XML 时才会真正发挥作用。正则表达式很容易崩溃,尤其是对 HTML 或 XML 进行微小更改时,并且必须支持脆弱的解决方案足以让任何人尖叫。
【解决方案2】:

要获取 YouTube ID,我认为最好的方法是使用环视。以下应该可以工作。

(?<=embed\/)(.+?)(?=\?)

这是 regex101.com 上的link to a demonstration

打开“全局”标志,以便正则表达式引擎在找到第一个匹配项后不会停止。此正则表达式使用后视,(?&lt;=embed\/);后跟一个以非贪婪方式匹配通配符的捕获组(.+?);随后是断言文字问号(?=\?) 的前瞻。

这应该足以获取视频 ID。

至于替换 HTML,这里有一个匹配 &lt;span&gt;...&lt;/span&gt; 块的正则表达式:

<span.*?>\s*<iframe.+?>.*?<\/iframe>\s*<\/span>

为此,请将s 标志应用于正则表达式引擎,以便. 通配符可以匹配\/n 换行符。也应用g 标志,原因与前面提到的相同。

注意:这将捕获将&lt;iframe&gt;s 作为直接子代的任何&lt;span&gt; 组。根据您正在处理的内容,您可能需要向正则表达式添加更多特异性以扫描那些&lt;iframe&gt;s 上的属性。但是,对于您提供给这个问题的内容,它似乎有效。

如果您需要任何说明或附加功能,请告诉我。

这是 regex101.com 上的 link to a demonstration

【讨论】:

  • 太棒了,非常感谢你。第一个正则表达式似乎非常适合我的目的,尽管第二个似乎不适用于我发布的示例。我确实将其更改为 &lt;span.+?&gt;\s*&lt;iframe.+?&gt;.*?&lt;\/iframe&gt;\s*&lt;\/span&gt; 以说明 &lt;span&gt; 属性,但它似乎仍然无法正常工作。
  • 让我再看看。
  • 这个怎么样? &lt;span.*?&gt;\s*&lt;iframe.+?&gt;.*?&lt;\/iframe&gt;\s*&lt;\/span&gt; 如果这对你有用,我会编辑我的答案。确保标志设置为gs。这是在这里工作。 regex101.com/r/nF0bQ6/1您是否还有其他失败的内容?
  • 太棒了。我将编辑我的回复,然后您可以将其标记为正确吗?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2016-12-31
  • 1970-01-01
  • 2011-08-06
  • 1970-01-01
  • 2011-07-24
  • 2013-11-10
  • 2014-07-17
相关资源
最近更新 更多