从零到正则高手
用一个正则提取并解析类 URL 字符串
目录
- 🚀 引言
- 🔍 从文本中提取 URL
- 🛳️ 这个 120+ 字节的正则
- 🧩 逐步拆解
- 🛠️ 解析示例
- ☑️ 后续步骤
- 📝 总结
- 📚 深入学习
太长不看版: 直接跳到 120+ 字节正则部分。
🚀 引言
从原始文本中提取 URL 有时就像在玩一场乏味的“打地鼠”游戏。标点符号、括号嵌套以及各种模棱两可的格式都会挫败你的努力。无论你是在构建网络爬虫、数据分析器还是聊天应用,准确提取 URL 都是刚需。
在这篇文章中,我们将通过一种灵活的两步走方案来正面解决这个问题。我们的目标是:先捕获所有“疑似”URL 的字符串,然后在后续流程中处理验证逻辑。
💡 注意: 此模式并非用于 验证 URL!它对标点符号和拼写错误具有刻意的容错性(Permissive)。
🔍 目标:从文本中提取 URL
在处理原始文本提取时,两步走方案非常有效:
- 捕获所有疑似 URL:撒大网,抓取所有可能是 URL 的字符串。这就是我们这个“120+ 字节正则”大显身手的地方。
- 验证:一旦捕获了这些候选对象,再通过二次检查(例如 DNS 解析、与已知域名比对)来剔除无效条目。
挑战可视化
“提取(Extract)”和“解析(Parse)”这两个词经常被混用,但它们代表了不同的过程。提取 URL 是指从大段文本中识别并捕获潜在的 URL;而解析则是将这些 URL 拆解为各个组成部分。
当我提到解析或“URL 组成部分”时,我指的是以下组件:
🛳️ 这个 120+ 字节的正则
下面是一个简洁的正则表达式,旨在一次性完成 URL 的提取和解析。它支持各种协议、域名、路径以及可选的查询参数/片段部分。
别担心——我们会一步步拆解它!
const urlRegex = /([-.a-z0-9]+:\/{1,3})([^-\/\.[\](|)\s?][^`\/\s\]?]+)([-_a-z0-9!@$%^&*()=+;/~\.]*)[?]?([^#\s`?]*)[#]?([^#\s'"`\.,!]*)/gi;// 兼容性:ES5+
// 同样的模式,为了可读性按行拆分:([-.a-z0-9]+:\/{1,3})([^-\/\.[\](|)\s?][^`\/\s\]?]+)([-_a-z0-9!@$%^&*()=+;/~\.]*)[?]?([^#\s`?]*)[#]?([^#\s'"`\.,!]*)在下方评论区分享你遇到过(或编写过)的最狂野的正则!🚀
🧩 逐步拆解
让我们将这个正则分解为各个组件,以了解其工作原理:
1. 协议 (Group 1): ([-.a-z0-9]+:/{1,3})
- 目的: 匹配 URL 的协议部分(例如
http://、ftp://、custom-scheme://)。 说明:
[-.a-z0-9]+:匹配一个或多个小写字母、数字、连字符或点(协议方案中的常见字符)。:/{1,3}:匹配一个冒号,后跟一到三个斜杠(:/、://或:///)。
2. 域名 (Group 2): ([^-/.[](|)s?][^`/s]?]+)
- 目的: 捕获 URL 的域名或主机部分。
说明:
[^-/.[](|)\s?]:匹配除指定特殊字符和空白符以外的任何字符。[^`/\s]?]+:匹配一个或多个字符,但不包括反引号、斜杠、空白符或闭方括号。
3. 路径 (Group 3): ([-_a-z0-9!@$%^&*()=+;/~\.]*)
- 目的: 匹配 URL 的路径组件。
说明:
[-_a-z0-9!@$%^&()=+;/~.]:匹配零个或多个路径中常见的 URL 安全字符。
4. 查询参数 (Group 4): [?]?([^#\s`?]*)
- 目的: 可选匹配以
?字符开头的查询字符串。 说明:
[?]?:可选匹配一个?。(方括号并非严格必须,但它们比极简的??更清晰。它还为下一个(类似的)匹配组[#]?提供了视觉上的平行感。)([^#\s`?]*):匹配零个或多个非井号、空白符、反引号或问号的字符。
5. 片段 (Group 5): [#]?([^#\s’”`.,!]*)
- 目的: 可选匹配以
#开头的片段标识符。 说明:
[#]?:可选匹配一个#。([^#\s’”`.,!]*):匹配零个或多个非禁止标点或空白符的字符。
🛠️ 解析示例
下面是如何利用这段复杂的正则表达式,配合一点 JavaScript 代码来完成工作的示例:
const text = `Check this out: https://example.com/path?query=123#sectionAnd also (ftp://files.server.org/index).Plus a weird one: custom-scheme://host/param;weird^stuff`;
const urlRegex = /([-.a-z0-9]+:\/{1,3})([^-\/\.[\](|)\s?][^`\/\s\]?]+)([-_a-z0-9!@$%^&*()=+;/~\.]*)[?]?([^#\s`?]*)[#]?([^#\s'"`\.,!]*)/gi;
const matches = [ ...text.matchAll(urlRegex),].map((match) => match[0]);console.log("Extracted URLs:", matches);
const parts = [ ...text.matchAll(urlRegex),].map((match) => match.slice(1));console.log("Extracted Parts:", parts);[ "https://example.com/path?query=123#section", "ftp://files.server.org/index", "custom-scheme://host/param;weird^stuff"][ [ "https://", // Protocol "example.com", // Domain "/path", // Path "query=123", // Query "section" // Fragment ], [ "ftp://", // Protocol "files.server.org", // Domain "/index", // Path "", // Query "" // Fragment ], [ "custom-scheme://", // Protocol "host", // Domain "/param;weird^stuff", // Path "", // Query "" // Fragment ]]☑️ 后续步骤
根据你的具体用例,你可能需要微调此正则,或者增加更多的验证和后处理步骤。
不同项目,不同需求
不同项目对 URL 的要求和安全考量各不相同:
- 网络爬虫:验证 URL 以确保其可访问且可信。
- 数据处理:从用户生成的内容中提取 URL,同时确保安全性。
- 数据分析:过滤掉重复或无关的链接,用于研究或营销目的。
- 面向用户的应用:在聊天应用或论坛中自动为 URL 添加超链接。
后处理与验证
在收集到潜在的 URL 后,应进行额外的检查:
- DNS 查询:验证域名是否可以解析。
- 安全检查:使用相关服务检查是否存在恶意软件或钓鱼网站。
- 自定义规则:应用项目特定的过滤器(例如:允许的顶级域名、URL 最大长度)。
📝 总结
提取半结构化的字符串数据,或许是掌握正则表达式过程中最有成就感的部分。
以下是关键要点回顾:
- 使用可视化工具编写、测试并理解你的 正则模式。
- 将挑战拆解为多个部分并分别解决。从某种意义上说,捕获组为我们的正则提供了形象的“路标”。
- 在进行数据摄取时,使用“宽松”的匹配表达式,避免死磕严格的规范一致性。
- 在初步提取后执行验证步骤至关重要——务必考虑你项目的安全性和特定需求。
通过遵循这些步骤,你可以有效地提取任何半结构化字符串数据,为进一步的处理和验证打下基础。
📚 延伸学习
- 记得去 RegEx101.com 上的实时演示 动手试试!
- StackOverflow 上的原始问题,以及 我在这里的回答链接。
- MDN 正则表达式文档
- 高级正则技术:探索前瞻(lookaheads)、后顾(lookbehinds)和其他高级模式,以实现更精确的匹配。
- RFC 3986 - URI 通用语法
