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

Posted

技术标签:

【中文标题】检测文本字符串中的(淘气或漂亮)URL 或链接【英文标题】:Detecting a (naughty or nice) URL or link in a text string 【发布时间】:2010-10-16 13:20:39 【问题描述】:

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

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

一些目标:

格式良好的 URL (http://some-fqdn/some/valid/path.ext) 等低调的果实 网址但不带 http:// 前缀(即有效的 FQDN + 有效的 HTTP 路径) 任何其他有趣的事情

当然,我会阻止垃圾邮件,但同样的过程可以用于自动链接文本。

想法

这是我在想的一些事情。

内容是母语散文,因此我可以很高兴地进行检测 我应该先去掉所有空格,以捕捉“www .example.com”吗?普通用户会知道自己删除空间,还是让任何浏览器“按我的意思做”并为您剥离空间? 也许多次传递是更好的策略,扫描: 格式正确的网址 所有非空格后跟“.”后跟任何有效的 TLD 还有别的吗?

相关问题

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

replace URL with HTML Links javascript What is the best regular expression to check if a string is a valid URL Getting parts of a URL (Regex)

更新与总结

哇,我在这里列出了一些非常好的启发式方法!对我来说,最划算的是以下几点的综合:

    @Jon Bright 的 TLD 检测技术(一个很好的防御瓶颈) 对于那些可疑的字符串,按照@capar 将点替换为类似点的字符 一个好看的点字符是@Sharkey的下标· (即“·”)。 ·也是单词边界,因此更难随意复制和粘贴。

这应该会使垃圾邮件发送者的每千次展示费用足够低以满足我的需求; “标记为不适当”的用户反馈应该可以捕捉到其他任何东西。列出的其他解决方案也非常有用:

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

【问题讨论】:

查看这些文章:-The Problem With URLs-Detecting URLs in a Block of Text 【参考方案1】:

鉴于我在 Disqus 评论垃圾邮件中看到的类似字符形式的“其他有趣业务”的混乱,你要做的第一件事就是处理它。

幸运的是,Unicode 人员为您服务。在您选择的编程语言中挖掘用于 Unicode 易混淆的 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,然后剥离声明为的代码点正在组合字符。

(然后,您需要在将案例输入到骨架算法之前对其进行规范化。)

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

    编写代码以在字符分解之前和之后运行可混淆检查,以防在分解之前而不是之后将事物视为可混淆,并检查大写和小写字符串以防可混淆表之间不对称大写和小写形式。 通过编写一个小脚本来检查 Unicode 表并识别分解或小写/大写一对字符是否会改变的任何代码点,从而调查 #1 是否真的是一个问题(如果不是,则无需浪费 CPU 时间)它们被认为是相互混淆的。

【讨论】:

【参考方案2】:

我只需要检测带有/out 协议的简单 http url,假设协议是给定的或“www”前缀。我发现above 提到的link 很有帮助,但最后我得出了这个结论:

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

显然,这并不能测试是否符合 dns 标准。

【讨论】:

【参考方案3】:

这里已经有一些很好的答案,所以我不会再发布更多。不过,我会给出几个问题。首先,确保测试 known 协议,其他任何东西都可能是淘气的。作为一个爱好 telnet 链接的人,您可能希望在搜索中包含更多的 http(s),但可能希望阻止说 aim: 或其他一些 url。其次,很多人会用尖括号 (gt/lt) 分隔他们的链接,例如 http://theroughnecks.net> 或括号“(url)”,没有什么比点击链接更糟糕的了连同网址的其余部分。

附:对不起,自引用插头;)

【讨论】:

【参考方案4】:

到目前为止,我最喜欢 capar 的答案,但处理 unicode 字体可能有点麻烦,旧版浏览器通常会显示有趣的东西或小框......以及位置U+05B4 有点奇怪……对我来说,它出现在管道之外 |ִ|即使是在他们之间。

有一个方便的· (·) 但是,它以相同的方式中断剪切和粘贴。它的垂直对齐可以通过 ing 来纠正,例如:

***·com

反常,但无论如何在FF3中有效,它不能被剪切和粘贴为URL。 实际上非常好,因为它可以直观地看出为什么无法粘贴 URL。

可以将不在可疑 URL 中的点单独保留,例如,您可以这样做

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

另一种选择是在可疑点旁边插入某种零宽度实体,但是像 ‍ 这样的东西‌和 &ampzwsp;似乎在FF3中不起作用。

【讨论】:

下标中点。天才!我想对其进行测试,但如果它适用于 IE7、FF3 和 Safari,我会说这已经足够好了。我正在考虑将此与@Jon Bright 的想法混合使用,即仅替换可疑 URL(即一个点后跟一个有效的 TLD)。 我只在FF3上尝试过,如果有效请告诉我!对于那些 URLize 或 email-address-ize 各种愚蠢的事情的混乱邮件客户端来说,这可能是一个很好的技术。 .TLD 我不太确定,主要是因为要检查其中的几个,会产生一个丑陋的正则表达式。也不要忘记虚线四边形(例如:IP 地址)是有效的 URL。 是的,肯定需要多层纵深防御才能真正捕获尽可能多的滥用行为。关于 TLD 的问题是,即使有很多,也没有 那么多,在我的特殊情况下(散文文本的 1 或 2 段字段)我可能会使用丑陋的正则表达式. (无论如何,最有用的正则表达式都很丑!)【参考方案5】:

考虑合并 OWASP AntiSAMY API...

【讨论】:

【参考方案6】:

Ping 可能的 URL

如果您不介意一点服务器端计算,那么像这样的东西呢?

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

这里:

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

    pingable 实际上使用系统调用来确定主机名是否存在于网络上。你可以有一个简单的包装器来解析 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 统计 --- 发送 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:未知主机

不利的一面是,如果主机提供 404,您将无法检测到它,但这是一个非常好的第一次切入 - 验证地址是否为网站的最终方法是尝试导航到该地址。您也可以尝试获取该 URL,但这是更重量级的。

【讨论】:

对不起,我肯定确实了解 Python! :) 但无论如何,列表推导完全是......这个词是什么?无法理解。 (对于非 Python 程序员。) 对。这就是为什么我说“可能不会”:)【参考方案7】:

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

【讨论】:

【参考方案8】:

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

【讨论】:

【参考方案9】:

在多次尝试编写这段代码之后,我可以明确地说,您将无法绝对可靠地执行此操作,而且您肯定无法检测到所有允许的 URI 形式RFC。幸运的是,由于您感兴趣的 URL 非常有限,因此您可以使用上述任何技术。

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

【讨论】:

这是一个非常有趣的想法。我可能会使用它(也许在构建基本算法之后的第二阶段)。 链接到我更全面地解释了这个概念的答案:***.com/questions/8472/…【参考方案10】:

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

我认为您最好的选择是使用 TLD。有两个字母的 ccTLD 和(当前)相对较小的其他列表。这些需要以点为前缀,并以斜杠或某些单词边界为后缀。正如其他人所指出的,这不会是完美的。如果不禁止合法的“我再试一次。它不起作用”或类似的话,就无法获得“buyfunkypharmaceuticals .it”。综上所述,这是我的建议:

[^\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/]

这将得到的东西:

buyfunkypharmaceuticals.it google.com http://stackoverflo**w.com/**questions/700163/

一旦人们开始混淆他们的网址,它当然会中断,替换“。”用“点”。但是,再次假设垃圾邮件发送者是您的目标,如果他们开始这样做,他们的点击率将再下降几个数量级,趋向于零。我认为,有足够信息对 URL 进行去混淆处理的一组人,以及一组不知道足以访问垃圾邮件网站的人,有一个很小的交叉点。此解决方案应让您检测到所有可复制粘贴到地址栏的 URL,同时将附带损害降至最低。

【讨论】:

TLD 是一个很好的保护自己的咽喉要道,感谢您的回答!我正在考虑将它与 capar 的答案结合起来,并将点替换为“点状”的 unicode 字符。这样“......再次。它不起作用”会发生不明显的变化,但即使 sombody 删除了空间,URL 仍然不起作用。对于真正晦涩难懂的东西,也许我可以求助于“标记为不当”的反馈。 跟进:TLD 是垃圾邮件 URL 的致命弱点。在我的例子中(一两段不欢迎 URL 的散文文本),扫描 TLD 是检测可疑字符串的直接方法。从那里,其他答案中的一些伟大的启发式和技术可能适用。但由于这个答案对许多其他人来说是一个很好的基础,所以我会选择它作为接受的答案。【参考方案11】:

我知道这对自动链接文本没有帮助,但是如果您搜索所有句点并将其替换为看起来相同的字符,例如希伯来语点 hiriq 的 unicode 字符 (U+05B4 )?

以下段落是一个例子:

这可能会工作ִ句号看起来有点奇怪但仍然可读ִ当然好处是任何人复制和粘贴wwwִgoogleִcom都不会太远ִ:)

【讨论】:

这可能不适用于我的具体情况,但这很容易成为迄今为止最聪明、最划算的答案!【参考方案12】:

嗯,很明显,以 http:// 和 www 开头的东西是唾手可得的。试图过滤掉诸如“www.g mail.com”之类的东西会导致关于你想走多远的有趣的哲学问题。你想采取下一步并过滤掉“www dot gee mail dot com”吗? URL 的抽象描述怎么样,例如“万维网的缩写,后跟一个点,然后是字母 g,然后是单词 mail,后跟一个点,以商业 TLD 缩写结尾”。

在继续尝试设计算法之前,请务必划清要尝试过滤的内容类型。我认为应该在“gmail.com”被视为网址的级别划线,但“gmail.com”不是。否则,每次有人未能将句子中的第一个字母大写时,您都可能会得到误报。

【讨论】:

【参考方案13】:

我不确定使用正则表达式检测 URL 是否是解决此问题的正确方法。通常你会错过一些不为人知的边缘情况,如果他们有足够的动机,垃圾邮件发送者将能够利用这些情况。

如果您的目标只是从 cmets 中过滤垃圾邮件,那么您可能需要考虑 Bayesian 过滤。事实证明,它在将电子邮件标记为垃圾邮件方面非常准确,它可能也能为您做同样的事情,具体取决于您需要过滤的文本量。

【讨论】:

php 中最好的贝叶斯库是什么?

以上是关于检测文本字符串中的(淘气或漂亮)URL 或链接的主要内容,如果未能解决你的问题,请参考以下文章

将 post.get('href') 转换为文本或字符串形式,Excel 无法处理超过 255 个字符的超链接

检测光标位于 Scintilla NET 中的注释或字符串上方

如何检测 JavaScript 字符串中的 URL 并将其转换为链接?

短信客户端 URL 预览检测

使用 JavaScript 检测文本中的 URL

PHP .htaccess -> 漂亮的 url(反向)