正则比 xpath 快好多哦,顺便问一个很初级的正则问题。

2019-10-28 19:14:58 +08:00
 JCZ2MkKb5S8ZX9pq
# Python 3.7

body = '<img src="https://tvax2.xximg.cn/crop.0.0.1242.1242.50/?????.jpg?KID=imgbed" alt="头像" class="por" /><img src="https://h5.xximg.cn/upload/2016/05/26/319/5547.gif" alt="达人" />'

re_icon = re.compile(r'src="(.*?)" alt="(?:V|达人)"')
v_icon = re_icon.search(body)
[print(i) for i in v_icon.groups()]

关于 xpath 和正则

3356 次点击
所在节点    正则表达式
27 条回复
maemual
2019-10-28 19:50:01 +08:00
正则快,但是未来的维护性会非常非常的差
tsohgdivil
2019-10-28 20:03:42 +08:00
。。。。。。你自己都搞不清楚正则匹配到的东西是什么还说要把 xpath 全部替换成正则?

楼主的问题完美展现出了正则的缺点:经常取出一些出乎意料的结果,而且本来可能是“正确”的正则,html 都数据甚至是顺序变一变就完全不对了。

退一步来说,就算你真想用, 至少看本正则表达式的书再来用吧。。不然有可能出的问题多了去了。你这个问题的原因是贪婪模式,正则表达式默认会选取匹配内容最多的结果,所以他会从第一个 src 匹配到最后一个 alt
Rysle
2019-10-28 20:10:53 +08:00
也许正则验证工具可以帮助你理解正则表达式的正确意图,可以去试试 regex101
JCZ2MkKb5S8ZX9pq
2019-10-28 20:16:27 +08:00
@tsohgdivil
从应对变动的角度来说,xpath 也差不多吧,改结构或者属性名的话,也难免要再适配。
正则也可以把类名之类的兜进去做,或者从父级开始加判断,速度有可能也还是更快。
xpath 我测下来主要的时间开销是转 xml 结构,但毕竟整个页面可能需要的只是一小部分,整个都转这步可能也有优化的空间。

回到主题:请问贪婪模式不是加了?就应该匹配最短哪个了嘛?为什么在这边无效了呢?
eason1874
2019-10-28 20:19:13 +08:00
因为 src="(.*?)" alt="(?:V|达人)" 的 (.*?) 可以匹配任何内容。

你改成 src="([^\"]+)" alt="(?:V|达人)" 就可以匹配到你想要的了, [^\"]+ 的意思是除"以外其他字符。

你现在对正则一点都不了解,纯粹学习项目你可以改成正则,不然建议先别改,不然你修改正则浪费的时间可能比 XPath 耗时更多。
JCZ2MkKb5S8ZX9pq
2019-10-28 20:21:16 +08:00
@Rysle 试了下,跟我之前的理解差不多。

*? Quantifier — Matches between zero and unlimited times, **as few times as possible**, expanding as needed (lazy)

我的疑惑就是,这里的取到的 src 为什么不是从第二个 src 开始的?
JCZ2MkKb5S8ZX9pq
2019-10-28 20:32:49 +08:00
@eason1874
谢谢。我试了下,改成[^\S]也是对的。
不过按照我本来理解,*加了?它会自动从第二个开始匹配,匹配最短的那个。
eason1874
2019-10-28 20:35:12 +08:00
@JCZ2MkKb5S8ZX9pq #6 非贪婪是没错,如果是 src="(.*?)" 这样匹配到的就是第一个 src 属性,问题是你后面还跟了指定的 alt,所以你的正则是这么分的:

1. src=" 匹配这段字符
2. (.*?) 匹配任何内容
3. " alt="(?:V|达人)" 匹配这段字符,其中 alt 属性只能是 V 或者 达人

所以 (.*?) 得到的结果就是 src=" 和 " alt="(?:V|达人)" 这两段字符中间的内容。一般匹配属性可以用 ^\" 来匹配,因为 HTML 属性前面是双引号那么肯定也是以双引号结束,中间内容不会有双引号。

正则解析网页 DOM 有很多不确定性,比如突然 src 属性没有双引号了,或者改成单引号了,你这正则就不管用了。如果要囊括所有情况就要写得比较复杂。
imn1
2019-10-28 20:35:47 +08:00
xpath 本身并不慢,慢的是解析 DOM
如果巨量 html/xml,例如过万,建议正则;如果只有几十个,哪个熟悉用哪个

用 [^"]+ 替换 .*? 比较好

@tsohgdivil
如果遇到不规范 html,dom 解析会很惨的,正则查错就简单多了
我试过 parse 500w+ html,lxml 和 bs4 都惨不忍睹

其次,如果是提取字符串中的片段,dom 方式至少需要两步,提取 text(),再处理,正则可以一步完成
尤其要一次提取多个片段,正则写得好的话可以一个 findall 搞定,dom 要视乎 xpath 的复杂度分多次提取
JCZ2MkKb5S8ZX9pq
2019-10-28 20:38:53 +08:00
@eason1874
比如这里的正则,如果简化成 'src.*?alt',它就能取到两段。
但附上了 alt 之后的内容,我本来以为它会取第二个 src 之后的内容,但实际是得到了从第一个开始的。
请问这里怎么理解比较好呢?
JCZ2MkKb5S8ZX9pq
2019-10-28 20:41:46 +08:00
@eason1874 比如后面直接用,alt="达人",非贪婪还是没起作用,请问这该怎么理解呢?
eason1874
2019-10-28 20:46:58 +08:00
@JCZ2MkKb5S8ZX9pq #10 没看懂你说 “简化成 'src.*?alt',它就能取到两段”是什么意思。

.*? 的意思是匹配任何内容直到遇到符合后面正则的内容,过程是这样:

1. src=" 匹配这段字符,OK,找到第一个标签的 src
2. (.*?) 匹配任何内容直到遇到符合后面正则的内容就停止
3. " alt="(?:V|达人)" 匹配这段字符,OK,在第二个 img 找到,可以结束了

你改成 alt="达人" 不影响啊。
faketemp
2019-10-28 20:47:53 +08:00
你问了一个我刚刚经历研究过的问题 o(* ̄︶ ̄*)o
https://www.v2ex.com/t/611557
JCZ2MkKb5S8ZX9pq
2019-10-28 20:50:28 +08:00
@eason1874
哦,我反应过来了!

应该是这样的,它先找 src,然后找到 alt="达人"结束。
我之前的理解是最短,我以为它都找出来(第一个 src 到达人,第二个 src 到达人),然后返回一个最短的给我。

是不是这个意思 /捂脸
JCZ2MkKb5S8ZX9pq
2019-10-28 20:51:35 +08:00
@faketemp 哈哈哈哈 /握手
eason1874
2019-10-28 20:52:12 +08:00
@JCZ2MkKb5S8ZX9pq

改成 src="([^\"]+)" alt="(?:V|达人)" 之后过程就变成这样了:

1. src=" 匹配这段字符,OK,找到第一个标签的 src
2. ([^\"]+) 匹配非"内容,遇到"这段完成
3. " alt="头像" 匹配到这里,哎,alt 属性不对,丢掉
4. src=" 在第二个 img 标签又找到这段字符,开始新的匹配
5. ([^\"]+) 匹配非"内容,完成
6. " alt="(?:V|达人)" 匹配这段字符,OK,找到完全符合规则的内容,可以结束了
JCZ2MkKb5S8ZX9pq
2019-10-28 20:54:03 +08:00
@eason1874
我说找到两段,是它从 src 开始找,找到第一个 alt,它就记录一段。
然后它继续找,找到第二个 src,然后又找到第二个 alt,又记录了一段。
所以 'src.*?alt' 如果用 findall,就会看到两段。
tsohgdivil
2019-10-28 20:54:08 +08:00
你看就这么一个简单的例子就得讨论这么多层。。更说明问题了。

不是说正则不好,但是运用正则需要对正则很深的了解才不会出错,不然到处复制粘贴修修改改可能出错的地方多了去了。

所以与其在这纠结这个例子是不是有问题,我还是建议去系统性的看下正则的书籍,了解了正则的匹配过程是怎么样的这些问题都不是问题。
JCZ2MkKb5S8ZX9pq
2019-10-28 20:55:11 +08:00
@eason1874 嗯,是这么回事儿,我明白了,感谢。
eason1874
2019-10-28 20:56:32 +08:00
@JCZ2MkKb5S8ZX9pq 全局匹配用 src.*?alt 是会匹配两个,因为你这里确实有两个符合规则的。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/613769

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX