当前位置: 首页 > 编程日记 > 正文

一种精确从文本中提取URL的思路及实现

在今年三四月份,我接受了一个需求:从文本中提取URL。这样的需求,可能算是非常小众的需求了。大概只有QQ、飞信、阿里旺旺等之类的即时通讯软件存在这样的需求。在研究这个之前,我测试了这些软件这块功能,发现它们这块的功能还是非常弱的。这类软件往往也是恶意URL传播的媒介,如果不能准确识别出URL,相应的URL安全检测也无从谈起。而且网上也有很多使用正则表达式的方法,可是我看了下,方法简单但是不够精确,对于要求不高的情况可以胜任,但是如果“坏人”想绕过这种提取也是很方便的。(转载请指明出处)下面也是我在公司内部做的一次分享的内容:

URL介绍

全称:Uniform Resource Locators。

最常见“最”标准的URL

例子:http://www.g.cn/

衍生出浏览器可以接受的URL(在地址栏输入的URL首先会被浏览器截获,浏览器可更具其对URL的理解进行相关容错)

协议后对斜杠无要求:

http:www.g.cn
http:\www.g.cn
http:\\/\www.g.cn

目前主流IM对最常见“最”标准的URL的识别没有问题,但是对衍生出来的URL都是无法正确识别的。

比较常见但是“不标准”(无协议头)URL

无协议头,无二级域名

例子:g.cn

无协议头,有二级域名www

例子:www.g.cn

无协议头,有二级域名,但是不是www

例子:mp3.g.cn

目前国内主流IM对URL的判别上,在没有协议头(http://等)时,寻找有没有“www.”,如果存在“www.”,则认为其后是URL。

比较少见的URL

格式省略或者特殊的UR

  • 顶级域名后包含“点”
    • 例子:www.g.cn.(同www.g.cn)

部分省略

  • 例子:www.g.cn.?wd=3(同www.g.cn./?wd=3、www.g.cn/?wd=3)

包含用户名和密码的URL

  • 密码不为空
    • 例子:username:password@www.g.cn
  • 密码为空
    • 例子:username:@www.g.cn

目前国内主流IM对这类URL判断是不准确的,如上例只能识别为www.g.cn

比较特殊的URL

完全没有分隔符的

例子:g.cnclick this(可以识别为g.cn,但是国内IM都不会去这么识别)

比较难以归类的

例子:mailto:@g.cn(以mailto协议标准,这个URL不符合RFC规定,因为mailto:后面@之前应该有“用户名”;以http或者ftp协议标准,这个URL是合法的,因为这个URL中用户名位mailto,密码为空。囧啊!惊恐

看一下国内一些IM的表现:

URL标准定义

定义于RFC1738,详细请见http://tools.ietf.org/html/rfc1738

具有相似的格式(ftp,http,https,wais,nntp……)

<scheme> ://<user>:<password>@<host>:<port>/<url-path>

“<user>:<password>@”, “:<password>”,“:<port>”,和“/<url-path>” 是可选的。

“<user>:<password>@”可以是“<user>@”(不需要密码),也可以是“<user>:@”(密码为空)。

形式多样的(mailto,news)

形式太多样,定义宽松

一些其他特殊协议(afs……)

要么不用了,要么这份RFC没给出定义,要么很少用。

格式相似的协议的URL Scheme的BNF范式

HTTP(用来指定互联网资源)

http://<host>:<port>/<path>?<searchpart>

gopher (用来指定互联网资源,已经很少用了)

gopher://<host>:<port>/<gopher-path>

nntp(网络新闻传输协议)

nntp://<host>:<port>/<newsgroup-name>/<article-number>

telnet(Internet远程登陆服务的标准协议和主要方式)

telnet://<user>:<password>@<host>:<port>/

wais(广域信息查询系统)

wais://<host>:<port>/<database> 
wais://<host>:<port>/<database>?<search> 
wais://<host>:<port>/<database>/<wtype>/<wpath>

格式相似的协议的URL Scheme的BNF范式

file(描述文件资源)

file://<host>/<path>

prospero(Be used to designate resources that are accessed via the Prospero Directory Servic)

prospero://<host>:<port>/<hsoname>;<field>=<value>

形式多样的协议的URL Scheme的BNF范式

news

news:<newsgroup-name>.
news:<message-id>

例子:news:msnews.microsoft.com

mailto

mailto:<rfc822-addr-spec>

例子:mailto:1@g.cn

一些其他特殊协议

afsAndrew File System global file names.
midMessage identifiers for electronic mail.
cidContent identifiers for MIME body parts.
nfsNetwork File System (NFS) file names.
tn3270Interactive 3270 emulation sessions.
mailserverAccess to data available from mail servers.
z39.50Access to ANSI Z39.50 services.

URL的RFC文档对提取URL的帮助

  • 提供了所有的协议头,帮助准确找到URL起始位置
  • 提供了http、ftp等协议名
  • 定义了各种URL的范式,为准确得提取URL有很大的帮助
    • 如ali-inc.com中的ali-inc部分要求“-”是可选的,且在存在“-”时,要求其左右存在数字或者字母。
    • 如user name和password部分(username:password@g.cn)如果出现“:”、 “@”或“/”时要加密,这将帮助寻找到URL的起始位置(@user:pass@g.cn提取的URL是user:pass@g.cn)。

基于以上问题,可以有种折中方案:将URL范式和现在已知的toplabel结合,构成一个新的范式。以下是RFC文档中BNF范式结合实际问题被修改成的正则表达式:

((((ftp:|https:|http:)([\Q/\\E])*)|())(((%[0-9a-fA-F][0-9a-fA-F])|([a-zA-Z0-9])|([\Q$-_.+!*'(),;?&=\E]))+(:((%[0-9a-fA-F][0-9a-fA-F])|([a-zA-Z0-9])|([\Q$-_.+!*'(),;?&=\E]))*)?@)?(((((([a-zA-Z0-9]){1}([a-zA-Z0-9\-])*([a-zA-Z0-9]{1}))|([a-zA-Z0-9]))\.)+(biz|com|edu|gov|info|int|mil|name|net|org|pro|aero|cat|coop|jobs|museum|travel|arpa|root|mobi|post|tel|asia|geo|kid|mail|sco|web|xxx|nato|example|invalid|test|bitnet|csnet|onion|uucp|ac|ad|ae|af|ag|ai|al|am|an|ao|aq|ar|as|at|au|aw|ax|az|ba|bb|bd|be|bf|bg|bh|bi|bj|bm|bn|bo|br|bs|bt|bv|bw|by|bz|ca|cc|cd|cf|cg|ch|ci|ck|cl|cm|cn|co|cr|cu|cv|cx|cy|cz|de|dj|dk|dm|do|dz|ec|ee|eg|eh|er|es|et|eu|fi|fj|fk|fm|fo|fr|ga|gb|gd|ge|gf|gg|gh|gi|gl|gm|gn|gp|gq|gr|gs|gt|gu|gw|gy|hk|hm|hn|hr|ht|hu|id|ie|il|im|in|io|iq|ir|is|it|je|jm|jo|jp|ke|kg|kh|ki|km|kn|kp|kr|kw|ky|kz|la|lb|lc|li|lk|lr|ls|lt|lu|lv|ly|ma|mc|md|me|mg|mh|mk|ml|mm|mn|mo|mp|mq|mr|ms|mt|mu|mv|mw|mx|my|mz|na|nc|ne|nf|ng|ni|nl|no|np|nr|nu|nz|om|pa|pe|pf|pg|ph|pk|pl|pm|pn|pr|ps|pt|pw|py|qa|re|ro|rs|ru|rw|sa|sb|sc|sd|se|sg|sh|si|sj|sk|sl|sm|sn|so|sr|st|su|sv|sy|sz|tc|td|tf|tg|th|tj|tk|tl|tm|tn|to|tp|tr|tt|tv|tw|tz|ua|ug|uk|um|us|uy|uz|va|vc|ve|vg|vi|vn|vu|wf|ws|ye|yt|yu|za|zm|zw))|([0-9]{1,3}(\.[0-9]{1,3}){3}))(\:([0-9]+))?(([\Q/\\E])+((((%[0-9a-fA-F][0-9a-fA-F])|([a-zA-Z0-9])|([\Q$-_.+\!*'(),;:@&=\E]))*)(([\Q/\\E])*((%[0-9a-fA-F]{2})|([a-zA-Z0-9])|([\Q$-_.+\!*'(),;:@&=\E]))*)*)(\?((%[0-9a-fA-F]{2})|([a-zA-Z0-9])|([\Q$-_.+!*'(),;:@&=<>#"{}[] ^`~|\/\E]))*)*)*)

看着是不是特别复杂?是的!这将导致效率非常低下。(这是很久前一个做实验的版本,不能保证其准确性)利用这个正则表达式中我们可以发现很多域名,这些域名都是我从某款安全辅助软件的二进制文件中扒下来了大笑。可能有人会认为这个正则效率的瓶颈在匹配这些域名上,其实不是,我做个实验,主要的瓶颈在domainlabel(就是.com等之前的那部分)上,所以优化比较困难。而且这个正则还没考虑一些特殊问题,比如将“。”识别成"."。

域名

biz|com|edu|gov|info|int|mil|name|net|org|pro|aero|cat|coop|jobs|museum|travel|arpa|root|mobi|post|tel|asia|geo|kid|mail|sco|web|xxx|nato|example|invalid|test|bitnet|csnet|onion|uucp|ac|ad|ae|af|ag|ai|al|am|an|ao|aq|ar|as|at|au|aw|ax|az|ba|bb|bd|be|bf|bg|bh|bi|bj|bm|bn|bo|br|bs|bt|bv|bw|by|bz|ca|cc|cd|cf|cg|ch|ci|ck|cl|cm|cn|co|cr|cu|cv|cx|cy|cz|de|dj|dk|dm|do|dz|ec|ee|eg|eh|er|es|et|eu|fi|fj|fk|fm|fo|fr|ga|gb|gd|ge|gf|gg|gh|gi|gl|gm|gn|gp|gq|gr|gs|gt|gu|gw|gy|hk|hm|hn|hr|ht|hu|id|ie|il|im|in|io|iq|ir|is|it|je|jm|jo|jp|ke|kg|kh|ki|km|kn|kp|kr|kw|ky|kz|la|lb|lc|li|lk|lr|ls|lt|lu|lv|ly|ma|mc|md|me|mg|mh|mk|ml|mm|mn|mo|mp|mq|mr|ms|mt|mu|mv|mw|mx|my|mz|na|nc|ne|nf|ng|ni|nl|no|np|nr|nu|nz|om|pa|pe|pf|pg|ph|pk|pl|pm|pn|pr|ps|pt|pw|py|qa|re|ro|rs|ru|rw|sa|sb|sc|sd|se|sg|sh|si|sj|sk|sl|sm|sn|so|sr|st|su|sv|sy|sz|tc|td|tf|tg|th|tj|tk|tl|tm|tn|to|tp|tr|tt|tv|tw|tz|ua|ug|uk|um|us|uy|uz|va|vc|ve|vg|vi|vn|vu|wf|ws|ye|yt|yu|za|zm|zw

这个域名还是比较全的,我没有全部测试,只是大致看了下,毕竟是别人总结的,不知道别人是否在里面放了“标记”信息。我曾经担心过xxx这个域名,还搜了下,发现很大大笑!。还有请仔细看,这些域名中没有数字,这为我之后的设计提出了一种思路。

国内IM对URL提取的处理

解读:

目前对URL的提取思路基本上是先考虑是否存在协议部分(http,ftp等),如果存在协议部分,则认为此协议之后URL可以接受的部分都是URL。这种方式存在很大的缺陷,如http://1也会被识别为一个URL。而http:g.cn则不会识别为一个完整的URL。
对于不存在协议部分的情况,寻找www.,如果存在www.则认为此串为URL,如:www.1就会被识别为URL,而mp3.g.cn则不会识别为URL。以mp3.g.cn和www.g.cn为例,.cn为顶级域名,g.cn为一级域名,而mp3.g.cn和www.g.cn都是二级域名。由于一开始时,人们习惯将二级域名www.g.cn指向了一级域名g.cn,久而久之,人们就认为www.开头的URL为一级域名。我想可能这个是造成目前这种判断URL的逻辑的原因。

优缺点

  • 优点:
    • 逻辑简单
    • 效率高
  • 缺点:
    • 判断不准确

产生以上优缺点的原因:只是寻找http,https,ftp,file,mailto,www这几个关键词。因为关键词少,所以逻辑简单也高效。有利有弊,因为关键词少,也一定程度上影响了URL判断的不准确性。

再次对URL进行分析和思考

常见的URL分类:

  • IP形式: 192.168.1.1,10.20.11.1
  • Domain形式:g.cn、www.g.cn,mp3.g.cn

观察可以见得:IP形式的URL结构最为简单:4个小于255的数字被.分割;domain形式比较复杂,但是它们有共性:都具有顶级域名.cn。

提取URL的大致思路:

通过以上的规律,可以发现,使用顶级域名来识别URL比使用协议或者www二级域名的方式要准确,同时辅助以IP鉴别,以求达到最大覆盖。

对前人做了总结和分析后,以下是我设计的提取逻辑

  • 提取URL的基本逻辑

  • 案例:
原始文字提取结果
这个是g.cng.cng.co
g.com/index.htm?g.com/index.htm?s=g.cn
s=g.cn1.2.3.4561.2.3.45
g.cn和g.comg.cn
g.com
1.2.3.4.51.2.3.4
2.3.4.5

以上是设计的相关逻辑

  • 以下是我写的一个demo的提取结果

  • 效率

最差URL形式

最优URL形式

URL形式

g.com.12.com.12.com.……

g.com/1111111111111……

遍历次数

约 (n+1)*n/2(O(n^2))

约n(O(1))

URL长度

最差耗时(ms/10,000次查找)

最优耗时(ms/100,000次查找)

200

1529

400

410

3921

578

810

11703

953

1620

39463

1719

2450

82980

2453

3270

143151

3219

4300

266341

4141

优化

目前的代码还是存在很多可以优化的地方:

因为我采用的递归调用,所以在最差情况下,执行效率大概是26ms一条,所以可以将递归改成循环来解决。

我使用的是C++类写的,如果改成C并_fastcall调用约定也会快些。

目前这个逻辑大致思路是从头到尾走一遍(不包括回溯),提取出以domain形式和IP形式的URL。在此之前,我设计成以domain形式从头到尾检测一次,和以IP形式从头到尾检测一次,然后综合两个结果的方法,这样的设计会比我目前这样的设计快一个数量级(已测)。

(转载请指明出处)

相关文章:

解读 | 2019年10篇计算机视觉精选论文(上)

作者 | 神经小兮来源 | HyperAI超神经&#xff08;ID:HyperAI&#xff09;2019 年转眼已经接近尾声&#xff0c;我们看到&#xff0c;这一年计算机视觉&#xff08;CV&#xff09;领域又诞生了大量出色的论文&#xff0c;提出了许多新颖的架构和方法&#xff0c;进一步提高了视…

不错的工具:Reflector for .NET

下载地址: http://www.aisto.com/roeder/dotnet/ 注意&#xff1a;下载时要输一些注册信息&#xff0c;输入用户名时&#xff0c;中间要加一个空格。

Possible MySQL server UUID duplication for server

&#xfeff;&#xfeff;在mysql enterprise monitor监控过程中出现这样的event事件&#xff0c;Topic: Possible MySQL server UUID duplication for server 事件&#xff0c;从该提示的描述来看貌似是存在重复的uuid&#xff0c;而实际上主从关系并不存在重复的uuid。主从关…

使用VC实现一个“智能”自增减线程池

工作中接手了一款产品的改造。因为该产品可能使用很多线程&#xff0c;所以产品中使用了线程池。&#xff08;转载请指明来自BreakSoftware的CSDN博客&#xff09; 线程池的一个优点是降低线程创建和销毁的频率&#xff1b;缺点是可能在比较闲的时候还存在一定数量的空闲线程。…

国内外财务软件科目结构的比较

科目结构是整个会计核算的基础。国内外财务软件都是任意定义科目的分段及科目编码长度&#xff0c;一般都能支持六段到九段。但科目结构在不同的国家有不同的规范&#xff0c;因而在不同的财务软件中也就有不同的控制。在科目分类上&#xff0c;国内外有明显的区别。国外财务软…

朋友圈装死,微博蹦迪,Python教你如何掌握女神情绪变化 | CSDN博文精选

作者 | A字头来源 | 数据札记倌很多人都是在朋友圈装死&#xff0c;微博上蹦迪。微信朋友圈已经不是一个可以随意发表心情的地方了&#xff0c;微博才是&#xff01;所以你不要傻傻盯着女神的朋友圈发呆啦&#xff01;本文教你如何用Python自动通知女神微博情绪变化&#xff0c…

java异常笔记

Throwable是所有Java程序中错误处理的父类&#xff0c;有两种资类&#xff1a;Error和Exception。Error&#xff1a;表示由JVM所侦测到的无法预期的错误&#xff0c;由于这是属于JVM层次的严重错误&#xff0c;导致JVM无法继续执行&#xff0c;因此&#xff0c;这是不可捕捉到的…

2019最新进展 | Transformer在深度推荐系统中的应用

作者 | Alex-zhai来源 | 深度传送门&#xff08;ID:deep_deliver&#xff09;【导读】最近基于Transformer的一些NLP模型很火&#xff08;比如BERT&#xff0c;GPT-2等&#xff09;&#xff0c;因此将Transformer模型引入到推荐算法中是近期的一个潮流。Transformer比起传统的L…

自己架设windows升级服务器

大部分对计算机比较熟悉的朋友都知道&#xff0c;通常安装好Windows 操作系统后要做的第一件事就是上Windows Update网站去给Windows 安装补丁程序&#xff0c;否则各种漏洞对系统就是一个很大的威胁。不过遗憾的是很多人还没有这样的意识&#xff0c;疏忽了给系统打补丁。这也…

内嵌IE网页窗口中消除IE默认脚本设置影响的方法

随着人们对客户端软件界面要求的不断提高,软件开发商面临着一个问题:如何快速廉价开发出各种丰富效果的UI界面。设计出一套丰富控件的界面库是不容易的,且产品经理丰富的想法和UED对效果的追求,往往会使程序员疲于编写这些“效果控件”。目前市面上使用的很多界面库是基于X…

win7 64位操作系统中 Oracle 11g 安装教程(图解)

1.下载Oracle 11g R2 for Windows版本&#xff0c;下载地址如下  官方网站&#xff1a;  http://download.oracle.com/otn/nt/oracle11g/112010/win32_11gR2_database_1of2.zip http://download.oracle.com/otn/nt/oracle11g/112010/win32_11gR2_database_2of2.zip 2.解压两…

使用APIHOOK实现进程隐藏

今天翻出一些今年前写的代码。其中一个是09年&#xff0c;我帮一个读研的同学写的一个“无公害恶意”程序。大致要求就是要实现自启动和自我隐藏。我使用的都是些简单的技术&#xff0c;只是实现自我隐藏稍微让我花费了点时间写算法。其实这个算法也很简单&#xff0c;就是大学…

程序员创业前要做哪些准备?

作者 | hsm_computer出品 | CSDN博客在互联网时代&#xff0c;不少干IT的人白手起家&#xff0c;在短短的几年里通过努力干出了一番事业&#xff0c;有房有车有公司&#xff0c;在人前也很光鲜。这就吸引了更多的程序员想要通过自主创业来实现财务自由。殊不知&#xff0c;创业…

Flex编码过程

Flex编码过程当我们开发一个Flex程序&#xff0c;我们重复其他类型网络程序的过程&#xff0c;例如HTML,JSP,ASP和CFML。创建一个有用的Flex程序是很容易的&#xff1a;打开我们最喜欢的文本编辑器&#xff0c;例如Flex Builder&#xff0c;输入XML标签&#xff0c;编译成为SWF…

BufferedWriter

package JBJADV003;import java.io.*;public class BufferedWriterTest { public static void main(String[] args) { try { //创建一个FileWriter 对象 FileWriter fwnew FileWriter("c:\\myDoc\\hello.txt"); //创建一个BufferedWriter 对象 BufferedWriter bwnew…

使用VC内嵌Python实现的一个代码检测工具

最近组内准备整顿代码&#xff0c;领导让我写个简单的python脚本分析代码中注释的行数和无效注释。因为这个需求不是很急&#xff0c;所以我想把简单的事情做复杂点。于是就写了一个用VC内嵌Python&#xff0c;并通过模拟按键和发消息去控制其他软件的工具。&#xff08;转载请…

Python如何实现24个微信大群万人同步转发直播?

作者 | 猪哥66来源 | CSDN博客今天我们来学习微信机器人多群转发做同步图文直播&#xff01;一、背景介绍猪哥一年前在建Python学习群的时候就说过&#xff0c;要邀请企业大佬来学习群做直播。其实文章早就写好了&#xff0c;但是一直没有找到好的转发软件&#xff0c;所以耽搁…

ITSM实施三招[案例]

当前国外成熟的ITSM解决方案的实施成本相对比较高&#xff0c;使一些对成本较敏感的的IT部门&#xff0c;成为ITSM实施的一个真空区。对于国内起步阶段的ITSM&#xff08;IT服务管理&#xff09;实施来说&#xff0c;南航的ITSM实施之路是一个借鉴。 南航it环境 在各大航空公司…

lr手工添加关联函数的步骤:

点击“确定”后&#xff1a; 如何修改已经创建好的关联规则&#xff1a;

新闻内容实现分页

/**//// <summary> /// 新闻内容分页 /// </summary> /// <param name"content">新闻内容</param> /// <param name"extension">扩展名(aspx,html..)</param> /// <returns></returns>pub…

使用自己的数据集训练MobileNet、ResNet实现图像分类(TensorFlow)| CSDN博文精选

作者 | pan_jinquan来源 | CSDN博文精选之前写了一篇博客《使用自己的数据集训练GoogLenet InceptionNet V1 V2 V3模型&#xff08;TensorFlow&#xff09;》https://panjinquan.blog.csdn.net/article/details/81560537&#xff0c;本博客就是此博客的框架基础上&#xff0c;完…

VC下提前注入进程的一些方法1——远线程不带参数

前些天一直在研究Ring3层的提前注入问题。所谓提前注入&#xff0c;就是在程序代码逻辑还没执行前就注入&#xff0c;这样做一般用于Hook API。&#xff08;转载请指明出处&#xff09;自己写了个demo&#xff0c;在此记下。 我的demo使用了两种注入方式&#xff1a;1 远线程&a…

【转】用示例说明索引数据块中出现热块的场景,并给出解决方案

文章转自&#xff1a;http://www.luocs.com/archives/582.html

VC下提前注入进程的一些方法2——远线程带参数

在前一节中介绍了通过远线程不带参数的方式提前注入进程&#xff0c;现在介绍种远线程携带参数的方法。&#xff08;转载请指明出处&#xff09; 1.2 执行注入的进程需要传信息给被注入进程 因为同样采用的是远线程注入&#xff0c;所以大致的思路是一样的&#xff0c;只是在细…

芬兰开放“线上AI速成班”课程,全球网民均可免费观看

出品 | AI科技大本营&#xff08;ID:rgznai100&#xff09;去年&#xff0c;芬兰推出了一个免费的“人工智能线上速成班”项目&#xff0c;目的是向该国民众教授与新技术有关的知识。现在&#xff0c;作为送给全世界的圣诞节礼物&#xff0c;这个项目已面向全球网民开放访问&am…

deepin开通ssh

1、在终端打入下面命令进行安装sudo apt-get install openssh-server2、启用sshservice ssh start 反馈&#xff1a;start: Rejected send message, 1 matched rules; type"method_call", sender":1.56" (uid1000 pid2272 comm"start ssh ") int…

实现等待窗体的几种方式

实现等待窗体的几种方式&#xff1a;下面说明了五种可以实现等待窗体的方式&#xff0c;其中三种给出了代码。准备资料安全访问控件成员为了保证在创建控件的线程上调用控件成员&#xff0c;用下面的方式封装控件的属性、方法、其他自定义成员的访问。如: winWordControl.LoadD…

GitHub宝藏项目标星1.6w+,编程新手有福了

作者 | Rocky0429来源 | Python空间&#xff08;ID: Devtogether&#xff09;特别惭愧的是&#xff0c;虽然我很早就知道 GitHub&#xff0c;但是学会逛 GitHub 的时间特别晚。当时一方面是因为菜&#xff0c;看着这种全是英文的东西难受&#xff0c;不知道该怎么去玩&#xff…

VC下提前注入进程的一些方法3——修改程序入口点

前两节中介绍了通过远线程进行注入的方法。现在换一种方法——修改进程入口点。&#xff08;转载请指明出处&#xff09; 在PE文件中&#xff0c;其中有个字段标识程序入口点位置。我们通过这个字段&#xff0c;到达程序入口点。PE文件的结构我这儿不讨论&#xff08;我会在之后…