检测文本字符串中的(顽皮或漂亮)URL或链接

时间:2009-03-31 05:46:31

标签: language-agnostic url sanitization spam-prevention

如何检测(使用正则表达式或启发式方法)文本字符串中的网站链接,例如评论?

目的是防止垃圾邮件。 HTML被剥离,所以我需要检测复制和粘贴的邀请。 垃圾邮件发送者发布链接应该不经济,因为大多数用户无法成功访问该页面。我希望有关最佳实践的建议,参考或讨论。

一些目标:

  • 结构良好的网址(http://some-fqdn/some/valid/path.ext
  • 等低调的结果
  • 网址但没有http://前缀(即有效的FQDN +有效的HTTP路径)
  • 任何其他有趣的事业

当然,我阻止垃圾邮件,但可以使用相同的过程自动链接文字。

以下是我正在考虑的一些事情。

  • 内容是母语散文,所以我可以在检测中触发快乐
  • 我应该首先删除所有空格,以便捕获“www .example.com”吗?普通用户是否知道自己删除空间,或者做任何浏览器“做什么意思”并为你剥离它?
  • 也许多次通过是一个更好的策略,扫描:
    • 格式正确的网址
    • 所有非空格后跟'。'随后是任何有效的TLD
    • 还有别的吗?

相关问题

我已经阅读了这些内容,现在它们已在此处进行了记录,因此您可以根据需要引用这些问题中的正则表达式。

更新和摘要

哇,我这里列出了一些很好的启发式方法!对我来说,最好的砰砰声是以下综合:

  1. @Jon Bright检测TLD的技术(良好的防御性阻塞点)
  2. 对于那些可疑的字符串,请根据@capar
  3. 将点替换为带点的字符
  4. 一个好看的小点字符是@ Sharkey的下标& middot; (即“·”)。 &安培; middot;也是一个单词边界,因此随便复制&糊。
  5. 这应该使垃圾邮件发送者的CPM足够低,以满足我的需求; “旗帜不合适”的用户反馈应该抓住其他任何东西。列出的其他解决方案也非常有用:

    • 删除所有虚线四边形(@ Sharkey对自己答案的评论)
    • @ Sporkmonger要求客户端Javascript在表单中插入必需的隐藏字段。
    • Ping URL服务器端以确定它是否是网站。 (也许我可以按照@Nathan通过SpamAssassin或其他贝叶斯过滤器运行HTML。)
    • 查看Chrome的智能地址栏来源,了解Google使用哪些聪明的技巧
    • 呼叫OWASP AntiSAMY或其他网络服务以检测垃圾邮件/恶意软件。

14 个答案:

答案 0 :(得分:19)

我正在集中力量避免垃圾邮件发送者。这导致两个子假设:因此使用该系统的人将积极尝试违反您的检查,并且您的目标只是检测URL的存在,而不是提取完整的URL。如果您的目标是其他目标,那么此解决方案看起来会有所不同。

我认为你最好的选择是TLD。有两个字母的ccTLD和(目前)比较小的其他名单。这些需要以点为前缀,并以斜杠或某些单词边界为后缀。正如其他人所指出的那样,这并不完美。没有办法获得“buyfunkypharmaceuticals。它”,而不会放弃合法的“我再试一次。它不起作用”或类似的东西。所有这些都说,这将是我的建议:

[^\b]\.([a-zA-Z]{2}|aero|asia|biz|cat|com|coop|edu|gov|info|int|jobs|mil|mobi|museum|name|net|org|pro|tel|travel)[\b/]

这会得到的结果:

  • buyfunkypharmaceutical的 s.it
  • GOOGL的 e.com
  • http://stackoverflo ** w.com / **问题/ 700163 /

当人们开始混淆他们的网址时,它会立即中断,取而代之的是“。”用“点”。但是,再次假设垃圾邮件发送者是你的目标,如果他们开始做这样的事情,他们的点击率将下降几个数量级的零。一组人告知足以对网址进行反混淆处理,而且我认为,无法通知足够访问垃圾网站的人群是一个微不足道的交叉点。此解决方案应该允许您检测可复制并粘贴到地址栏的所有URL,同时将附带损害保持在最低限度。

答案 1 :(得分:13)

答案 2 :(得分:6)

我不确定检测带有正则表达式的网址是否是解决此问题的正确方法。通常你会错过某种晦涩难懂的边缘案例,如果他们有足够的动力,垃圾邮件制造者就可以利用它。

如果您的目标只是过滤掉评论中的垃圾邮件,那么您可能需要考虑Bayesian过滤。事实证明,将电子邮件标记为垃圾邮件非常准确,它也可以为您执行相同操作,具体取决于您需要过滤的文本量。

答案 3 :(得分:6)

我知道这对自动链接文本没有帮助但是如果你搜索并用一个看起来像是同一个东西的字符替换所有句号,例如希伯来语hiriq的unicode字符(U + 05B4) )?

以下段落是一个例子:

这可能会起作用ִ这个时期看起来有点奇怪,但它仍然可读ִ当然的好处是任何复制和粘贴wwwִgoogleִcom的人都不会太过ִ:)

答案 4 :(得分:4)

嗯,显然,低悬的水果是以http://和www开头的东西。试图过滤掉诸如“www.g.mail.com”之类的内容会导致有关您想要走多远的有趣哲学问题。你想下一步并过滤掉“www dot gee mail dot com”吗?如何对URL进行抽象描述,例如“万维网的缩写后跟一个点,后跟字母g,后跟单词mail后跟一个点,最后用TLD缩写为商业”。

在继续尝试设计算法之前,绘制一系列您要尝试过滤的内容非常重要。我认为该行应该在“gmail.com”被认为是url的级别绘制,但“gmail.com”不是。否则,每当有人未能将句子中的第一个字母大写时,您就可能会得到误报。

答案 5 :(得分:3)

由于您主要是在寻找复制和粘贴到浏览器地址栏的邀请,因此可能需要查看开源浏览器(例如Chrome或Mozilla)中使用的代码,以确定是否将文本输入到“地址栏等效”是搜索查询或URL导航尝试。

答案 6 :(得分:2)

Ping可能的网址

如果你不介意一点服务器端计算,那么这样的事情怎么样?

urls = []
for possible_url in extracted_urls(comment):
    if pingable(possible_url):
       urls.append(url)  #you could do this as a list comprehension, but OP may not know python

下面:

  1. extracted_urls接受评论并使用保守的正则表达式来提取可能的候选人

  2. pingable实际上使用系统调用来确定主机名是否存在于Web上。您可以使用简单的包装器解析ping的输出。

    [ramanujan:〜/ base] $ ping -c 1 www.google.com

    PING www.l.google.com(74.125.19.147):56个数据字节 来自74.125.19.147的64字节:icmp_seq = 0 ttl = 246 time = 18.317 ms

    --- www.l.google.com ping statistics --- 1个包传输,1个包接收,0%包丢失 往返min / avg / max / stddev = 18.317 / 18.317 / 18.317 / 0.000 ms

    [ramanujan:〜/ base] $ ping -c 1 fooalksdflajkd.com

    ping:无法解析fooalksdflajkd.com:未知主机

  3. 缺点是,如果主机提供404,你将无法检测到它,但这是一个非常好的第一次削减 - 验证地址是网站的最终方法是尝试导航到它。您也可以尝试wget'ing该URL,但这更重要。

答案 7 :(得分:1)

我已经多次尝试编写这段确切的代码,我可以毫不含糊地说,你无法以绝对可靠的方式做到这一点,而且你肯定无法检测到所有允许的URI形式。 RFC。幸运的是,由于您有一组非常有限的URL,您可以使用上述任何技术。

然而,我可以非常肯定地说,另一件事是,如果你真的想要击败垃圾邮件发送者,那么最好的办法就是使用JavaScript。发送执行一些计算的JavaScript块,并在服务器端重复计算。 JavaScript应该将计算结果复制到隐藏字段,以便在提交注释时,也会提交计算结果。在服务器端验证计算是否正确。围绕此技术的唯一方法是让垃圾邮件发送者手动输入注释或让他们开始为您运行JavaScript引擎。我使用这种技术将我网站上的垃圾邮件从100 + /天减少到每年一到两次。现在,我所获得的唯一垃圾邮件是人工手动输入的。获取主题垃圾邮件很奇怪。

答案 8 :(得分:1)

当然,您会意识到,如果垃圾邮件发送者决定使用tinuyrl或此类服务来缩短其网址,那么问题就会变得更糟。在这种情况下,您可能需要编写一些代码来查找实际的URL,使用TinyURL解码器等服务

答案 9 :(得分:1)

考虑合并OWASP AntiSAMY API ......

答案 10 :(得分:1)

到目前为止,我最喜欢 capar 的答案,但是处理unicode字体可能有点令人担忧,旧版浏览器经常显示一个有趣的东西或一个小盒子......和位置U + 05B4有点奇怪...对我来说,它出现在管道外面这里|ִ|即使它们在它们之间。

有一个方便的· (·)虽然以同样的方式打破了剪切和粘贴。其垂直对齐可以通过< sub-ing;来校正,例如:

计算器<子>·玉米

反常,但在FF3中有效,它不能作为URL剪切和粘贴。 &lt; sub&gt;实际上非常好,因为它使视觉上显而易见无法粘贴URL。

不在可疑网址中的点可以单独留下,例如你可以

s/\b\.\b/<sub>&middot;<\/sub>/g

另一种选择是在可疑点旁边插入某种零宽度实体,但是像&amp; zwj;和&amp; zwnj;和&amp; zwsp;似乎没有在FF3中工作。

答案 11 :(得分:1)

这里已经有了一些很好的答案,所以我不会发布更多。我会给几个陷阱。首先,确保测试已知的协议,其他任何东西都可能是顽皮的。作为其爱好关注telnet链接的人,您可能希望在搜索中包含多个http,但可能希望防止说出目标:或其他一些网址。其次,许多人会在角括号(gt / lt)中划分他们的链接,例如&lt; http://theroughnecks.net&gt;或者在parens“(url)”并且没有什么比点击链接更糟糕了,并且关闭&gt;或者与网址的其余部分一起去。

P.S。对不起自我参考插头;)

答案 12 :(得分:0)

我只需要检测简单的http urls with / out协议,假设协议是给定的或'www'前缀。我发现提到的above link非常有帮助,但最后我得出了这个:

http(s?)://(\S+\.)+\S+|www\d?\.(\S+\.)+\S+

显然,这不符合dns标准的测试要求。

答案 13 :(得分:0)

考虑到我在Disqus评论垃圾邮件中看到的“其他有趣的事情”一团糟,它们看起来相似,所以要做的第一件事就是处理该问题。

幸运的是,Unicode人员已经为您服务。在您选择的编程语言中挖掘出适用于Unicode Confusables的TR39骨架算法的实现,并将其与一些Unicode规范化和可识别Unicode的上/下壳体配对。

最简单的算法使用Unicode人维护的lookup table来完成概念上类似于大小写折叠的操作。

(输出可能不使用明智的字符,但是,如果将其应用于比较的两面,则如果字符在视觉上足够相似以使人能够理解意图,则将获得匹配。)

下面是this Java implementation的示例:

// Skeleton representations of unicode strings containing 
// confusable characters are equal 
skeleton("paypal").equals(skeleton("paypal")); // true
skeleton("paypal").equals(skeleton("??ỿ??ℓ")); // true
skeleton("paypal").equals(skeleton("ρ⍺у??ן")); // true
skeleton("ρ⍺у??ן").equals(skeleton("??ỿ??ℓ")); // true
skeleton("ρ⍺у??ן").equals(skeleton("??ỿ??ℓ")); // true

// The skeleton representation does not transform case
skeleton("payPal").equals(skeleton("paypal")); // false

// The skeleton representation does not remove diacritics
skeleton("paypal").equals(skeleton("pàỳpąl")); // false

(如您所见,您将首先要进行其他一些标准化。)

鉴于您要进行URL检测是为了判断某些内容是否为垃圾邮件,这很可能是其中uncommon的情况之一,通过将Unicode标准化为NFKD然后剥离代码点可以安全地开始声明要合并字符。

(然后,您需要先对案例进行归一化,然后再将其输入骨架算法。)

我建议您执行以下操作之一:

  1. 编写代码以在字符分解之前和之后运行可混淆性检查,以防在分解之前将事物视为可混淆性,而不是在分解之后,并检查大写和小写字符串,以防可混淆性表之间不对称大写和小写形式。
  2. 通过编写一个小的脚本来检查Unicode表并识别分解或降低/大写一对字符的代码点,来调查#1是否实际上是一个问题(如果不是,则不需要浪费CPU时间)他们被认为容易混淆。