正则表达式匹配生成部分无效输出的URL

时间:2012-05-28 14:05:36

标签: ruby regex url

我正在尝试在我的Ruby应用程序中使用以下正则表达式代码来匹配HTTP链接,但它会生成无效输出,在链接后面附加一个句点,有时是一个句点和一个单词,当在Web上测试时,变为无效。

URL_PATTERN  = Regexp.new %r{http://[\w/.%-]+}i
<input>.to_s.scan( URL_PATTERN ).uniq

以上扫描链接的代码是否有问题?

来自应用的代码:

require 'bundler/setup'
require 'twitter'

RECORD_LIMIT = 100
URL_PATTERN  = Regexp.new %r{http://[\w/.%-]+}i

def usage
  warn "Usage: ruby #{File.basename $0} <hashtag>"  
  exit 64
end

# Ensure that the hashtag has a hash symbol. This makes the leading '#'
# optional, which avoids the need to quote or escape it on the command line.
def format_hashtag(hashtag)  
  (hashtag.scan(/^#/).empty?) ? "##{hashtag}" : hashtag
end

# Return a sorted list of unique URLs found in the list of tweets.
def uniq_urls(tweets)  
  tweets.map(&:text).grep( %r{http://}i ).to_s.scan( URL_PATTERN ).uniq
end

def search(hashtag)  
  Twitter.search(hashtag, rpp: RECORD_LIMIT, result_type: 'recent')
end

if __FILE__ == $0 usage unless ARGV.size >= 1  
hashtag = format_hashtag(ARGV[0]) 
tweets = search(hashtag) 
puts uniq_urls(tweets)
end

3 个答案:

答案 0 :(得分:3)

TL; DR

人们一直发布不良链接。链接也有点腐烂。

可能的答案

您是否手动验证了推文?您确定原始推文不包含格式错误的网址吗?如果有人发帖:

  

http://foo.Any更多的吐司?

那么你肯定会得到一个无效的结果,因为正则表达式需要在URL周围有空格。如果您想修剪无效结果,那么您需要使用可以处理重定向的链接检查器来验证您找到的每个链接。

作者的免责声明

您发布的代码是我的,来自CodeGnome/twitter_url_extractor。我特意遗漏了链接检查,因为我对提取URL感兴趣,而不是验证它们。

“这对我有用;你的里程可能会有所不同。”℠

答案 1 :(得分:1)

问题是你的正则表达式会包含一个尾随句点,因为你不加区别地检查任意字符,斜线,百分号,连字符(又名“减号”)和句号。当URL位于句子末尾时,这将捕获一个实际上是标点符号的尾随句点,如果人们在句点之后省略了空格,那么之后的任何内容 - as CodeGnome correctly stated。您可以通过排除这样的尾随标点符号来部分缓解此问题(请注意,这仍然会捕获标点符号,然后是非URL内容):

http://\w+(?:[./%-]\w+)+$

但是,这仍会遗漏大部分现有网址,并会收集大量无效内容:URLs are quite complex beasts。如果你想要一个完美的匹配,John Gruber发布了a regex,它与今天用作URL的任何内容相匹配,而不仅仅是http(s)。为了更好地匹配大量仅限Web的URL(包括HTTPS变体),确保在开始时拥有格式良好的域,并捕获查询和片段标识符,正则表达式应如下所示:

https?://[\w-]+(?:\.[\w-]+)+(?:/[\w-]+)*(?:(?:[./%?=&#-]\w+)+)?

- 这仍会捕获无效的内容,并排除相当多的现有网址(以及更大比例的有效网址 - 请参阅我上面链接的RFC),但它会让您更接近。

答案 2 :(得分:1)

为什么不使用Ruby的URI.extract而不是重新发明轮子?它与Ruby捆绑在一起。

来自文档:

Synopsis

URI::extract(str[, schemes][,&blk])

Args

str     String to extract URIs from.
schemes Limit URI matching to a specific schemes.

Description

Extracts URIs from a string. If block given, iterates through all matched URIs. Returns nil if block given or array with matches.
Usage

require "uri"

URI.extract("text here http://foo.example.org/bla and here mailto:test@example.com and here also.")
# => ["http://foo.example.com/bla", "mailto:test@example.com"]

如果您只想要HTTP网址:

[3] (pry) main: 0> URI.extract("text here http://foo.example.org/bla and here mailto:test@example.com and here also.", %w[http])
=> ["http://foo.example.org/bla"]