正则比 xpath 快好多哦,顺便问一个很初级的正则问题。 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
JCZ2MkKb5S8ZX9pq
V2EX    正则表达式

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

  •  
  •   JCZ2MkKb5S8ZX9pq 2019-10-28 19:14:58 +08:00 3730 次点击
    这是一个创建于 2179 天前的主题,其的信息可能已经有所发展或是发生改变。
    # 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()] 
    • 请问为什么这么取的时候,返回的是从第一个 src 开始的呢?
    • 忽然短路,求点醒。

    关于 xpath 和正则

    • etree.HTML 这个步骤很费时。
    • etree.HTML,re.compile 都不计时,也还是 re 快很多。
    • 想把有些能转正则的,用正则处理了。
    第 1 条附言    2019-10-28 20:45:18 +08:00

    补一点数据,分别10万次。

    • 每次都做etree.HTML,8.409秒.
    • 开头做一次etree.HTML,之后只用xpath查询,4.592秒。
    • 正则,0.996秒。
    27 条回复    2019-10-28 21:10:37 +08:00
    maemual
        1
    maemual  
       2019-10-28 19:50:01 +08:00   1
    正则快,但是未来的维护性会非常非常的差
    tsohgdivil
        2
    tsohgdivil  
       2019-10-28 20:03:42 +08:00 via iPhone
    。。。。。。你自己都搞不清楚正则匹配到的东西是什么还说要把 xpath 全部替换成正则?

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

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

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

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

    你现在对正则一点都不了解,纯粹学习项目你可以改成正则,不然建议先别改,不然你修改正则浪费的时间可能比 XPath 耗时更多。
    JCZ2MkKb5S8ZX9pq
        6
    JCZ2MkKb5S8ZX9pq  
    OP
       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
        7
    JCZ2MkKb5S8ZX9pq  
    OP
       2019-10-28 20:32:49 +08:00
    @eason1874
    谢谢。我试了下,改成[^\S]也是对的。
    不过按照我本来理解,*加了?它会自动从第二个开始匹配,匹配最短的那个。
    eason1874
        8
    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
        9
    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
        10
    JCZ2MkKb5S8ZX9pq  
    OP
       2019-10-28 20:38:53 +08:00
    @eason1874
    比如这里的正则,如果简化成 'src.*?alt',它就能取到两段。
    但附上了 alt 之后的内容,我本来以为它会取第二个 src 之后的内容,但实际是得到了从第一个开始的。
    请问这里怎么理解比较好呢?
    JCZ2MkKb5S8ZX9pq
        11
    JCZ2MkKb5S8ZX9pq  
    OP
       2019-10-28 20:41:46 +08:00
    @eason1874 比如后面直接用,alt="达人",非贪婪还是没起作用,请问这该怎么理解呢?
    eason1874
        12
    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
        13
    faketemp  
       2019-10-28 20:47:53 +08:00
    你问了一个我刚刚经历研究过的问题 o(* ̄ ̄*)o
    t/611557
    JCZ2MkKb5S8ZX9pq
        14
    JCZ2MkKb5S8ZX9pq  
    OP
       2019-10-28 20:50:28 +08:00
    @eason1874
    哦,我反应过来了!

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

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

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

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

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

    所以与其在这纠结这个例子是不是有问题,我还是建议去系统性的看下正则的书籍,了解了正则的匹配过程是怎么样的这些问题都不是问题。
    JCZ2MkKb5S8ZX9pq
        19
    JCZ2MkKb5S8ZX9pq  
    OP
       2019-10-28 20:55:11 +08:00
    @eason1874 嗯,是这么回事儿,我明白了,感谢。
    eason1874
        20
    eason1874  
       2019-10-28 20:56:32 +08:00
    @JCZ2MkKb5S8ZX9pq 全局匹配用 src.*?alt 是会匹配两个,因为你这里确实有两个符合规则的。
    tsohgdivil
        21
    tsohgdivil  
       2019-10-28 20:58:00 +08:00 via iPhone
    而且情况简单还好,如果情况比较复杂,就算你再怎么精通正则,也不太可能写出一个完全“正确”的正则出来,最多写一个在特定情况下能够取出来正确值的正则,但是总是会出现 edge case
    JCZ2MkKb5S8ZX9pq
        22
    JCZ2MkKb5S8ZX9pq  
    OP
       2019-10-28 21:02:38 +08:00
    @tsohgdivil

    我是菜鸟,讨论能帮助我更好地理解。
    层数多是我水平的问题,未必是正则不如 xpath 好用。

    你的两层回复都没有任何实际的帮助。
    书我看过了,没看懂,所以才又碰到问题来请教。
    通过 eason 的热心回复,帮助我在很短时间就明白了问题所在。这就是提问的价值。

    你提出正则会有很多意外,这个观点本身是没问题的。
    但后续部分,如果你同样打这么多字,能提供一些有效的帮助,那我会更加欢迎。
    tsohgdivil
        23
    tsohgdivil  
       2019-10-28 21:04:14 +08:00 via iPhone
    @JCZ2MkKb5S8ZX9pq #22
    好的,对不起,下次我尽量简短一点。我想说的就是“看书就完了”,不然这个坑踩了还有无数坑等着。
    JCZ2MkKb5S8ZX9pq
        24
    JCZ2MkKb5S8ZX9pq  
    OP
       2019-10-28 21:05:33 +08:00
    @tsohgdivil 书我看过了,没看懂,所以才又碰到问题来请教。
    tsohgdivil
        25
    tsohgdivil  
       2019-10-28 21:06:25 +08:00 via iPhone
    推荐《精通正则表达式》
    tsohgdivil
        26
    tsohgdivil  
       2019-10-28 21:08:25 +08:00 via iPhone
    @JCZ2MkKb5S8ZX9pq #24
    你可以看下这本书,像这种问题在里面有很多讨论,里面详细讲解了正则的匹配过程。看完基本上可以自己写一个正则引擎了。
    ClericPy
        27
    ClericPy  
       2019-10-28 21:10:37 +08:00
    哈哈, 你拿一个 C 写的东西和一个 py 写的东西怎么比...
    很多问题等你多用几年就自然理解了, 比性能是不能跨语言比的, 要比也得 lxml 和 re 比

    不过算法不同, 就算用 lxml, Xpath 解析也比 re 慢的, 光假装构建个 DOM 就费老劲了, 别忘了 Element 还有一大堆绑定方法, 用途不一样, 就别和性能较劲了
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     5162 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 29ms UTC 03:49 PVG 11:49 LAX 20:49 JFK 23:49
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86