对网站源码使用正则式的疑问? - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐学习书目
Learn Python the Hard Way
Python Sites
PyPI - Python Package Index
http://diveintopython.org/toc/index.html
Pocoo
值得关注的项目
PyPy
Celery
Jinja2
Read the Docs
gevent
pyenv
virtualenv
Stackless Python
Beautiful Soup
结巴中文分词
Green Unicorn
Sentry
Shovel
Pyflakes
pytest
Python 编程
pep8 Checker
Styles
PEP 8
Google Python Style Guide
Code Style from The Hitchhiker's Guide
explist
V2EX    Python

对网站源码使用正则式的疑问?

  •  
  •   explist 2016-05-02 00:12:46 +08:00 4609 次点击
    这是一个创建于 3459 天前的主题,其中的信息可能已经有所发展或是发生改变。
    网站: http://sou.kuwo.cn/ws/NSearch?type=all&catalog=yueku2016&key=%E6%B1%AA%E5%B3%B0
    要求:爬取其上的歌曲 ID ,歌名,歌手名
    我写了个太难看了,如何写得更优雅点,或其它更好的方法
    pat = re.compile(r'<p class="m_name">\s+<a href=".+?(\d+)/"\s*title="(.+?)".+?\s+.+?\s+.+?\s+.+?\s+.+?\s+?<p class="s_name".+?title="(.+?)"><')
    res = pat.findall(html.read().decode())
    另外:如何插入图片啊这里,代码也很乱

    源码示例:
    <li class="clearfix">
    <p class="number"><input type="checkbox" checked="checked" name="musicNum" value="122560" mid="122560" />01</p>

    <p class="m_name">
    <a href="http://www.kuwo.cn/yinyue/122560/" title="怒放的生命" target="_blank">
    <script>document.write("怒放的生命".replace(/(汪峰)/gi,'<em class="redFont">$1</em>'))</script>
    </a>
    </p>
    <p class="a_name"><a href="http://www.kuwo.cn/album/7985/" title="怒放的生命" target="_blank"><script>document.write("怒放的生命".replace(/(汪峰)/gi,'<em class="redFont">$1</em>'))</script></a></p>
    <p class="s_name"><a href="http://www.kuwo.cn/mingxing/%E6%B1%AA%E5%B3%B0/" target="_blank" title="汪峰"><script>document.write("汪峰".replace(/(汪峰)/gi,'<em class="redFont">$1</em>'))</script></a></p>
    <p class="listen"><a href="http://player.kuwo.cn/MUSIC/MUSIC_122560" title="怒放的生命试听" target="_blank"></a></p>
    <p class="video"><a href="http://www.kuwo.cn/mv/122560/" title="怒放的生命 MV" target="_blank"></a></p>
    <p class="share"><a href="Javascript:void(0);" Onclick="showShareMusic(this,'怒放的生命','','122560')" title="分享"></a></p>
    <p class="down"><a href="Javascript:void(0);" title="怒放的生命下载" Onclick="showDownMusic2014('MUSIC_122560');"></a></p>
    </li>
    40 条回复    2016-05-04 14:58:38 +08:00
    fy
        1
    fy  
       2016-05-02 00:41:19 +08:00
    比较复杂的 html 就使用 lxml
    而且说实话这种东西没什么复杂不杂的,挂了就修,忘记了就重写,一个正则能顶个十天半个月的,你管他好不好看
    just1
        2
    just1  
       2016-05-02 00:53:31 +08:00 via Android
    value="(\d+)"[\s\S]*(:?"(:?s|a)_name"[\s\S]*?title="(.*)?"){2}
    难看,但是短了点
    qqmishi
        3
    qqmishi  
       2016-05-02 00:54:03 +08:00 via Android
    这种可以试试用 bueatifulsoup 解析
    我的正则写的比你的更难看,但又不是不能用对吧,,,自用就懒得管优雅不优雅了
    icybee
        4
    icybee  
       2016-05-02 00:55:10 +08:00
    bs,pyquery?
    ayaseangle
        5
    ayaseangle  
       2016-05-02 01:36:47 +08:00
    用 xml 解析方便。。。。
    skydiver
        6
    skydiver  
       2016-05-02 01:38:24 +08:00 via iPad
    大忌就是用正则匹配 html
    binux
        7
    binux  
       2016-05-02 06:52:13 +08:00
    html 不是正则语言,不符合正则文法
    不要用正则解析 html !
    不要用正则解析 html !
    不要用正则解析 html !
    aprikyblue
        8
    aprikyblue  
       2016-05-02 07:13:43 +08:00 via Android
    嗯。。楼上说的很清楚了。
    会有各种坑
    当然,如果是那种自己用几次就扔的,那当没说
    TangMonk
        9
    TangMonk  
       2016-05-02 11:09:53 +08:00 via Android
    用 xpath 嘛
    donghouhe
        10
    donghouhe  
       2016-05-02 11:17:58 +08:00
    @binux 大牛,膜拜中.....终于见到活的了
    chairuosen
        11
    chairuosen  
       2016-05-02 11:32:56 +08:00
    pyquery
    explist
        12
    explist  
    OP
       2016-05-02 11:40:02 +08:00
    哪种方式速度快一点?我的正则实在是太慢了! OMGD
    explist
        13
    explist  
    OP
       2016-05-02 11:45:11 +08:00
    好多都要生成树,而正则表达式写的好的话,会快一些?
    jackal
        14
    jackal  
       2016-05-02 11:47:31 +08:00   1
    抛开立场之分
    (立场之分是指有人要让正则表达式做不该它做的事情,比如解析任意复杂的 html 等)

    题主的问题是:希望从数个固定格式的网页数据中抓取固定模式的数据段。 我的意思是,前提条件是 kuwo 的歌曲网页数据都很固定,而且网页中关于歌曲 id ,歌曲名称,歌手名称,都是固定的模式数据( class=“ number ”, class=“ name ” , class=“ s_name ”)

    这样,我觉得一个简单的实现,是可以用正则表达式来解析 /抓取数据的。

    题主的正则表达式已经可以了,只是 3 点还有问题:
    1 )应该遵从 class=“ number ”, class=“ name ” , class=“ s_name ”的格式来抓取对应的数据,而不是从其他地方去获取(以后就算页面改版,很可能 class 的名字和对应数据是不会改变的)

    2 )让.*能够匹配换行,这样你的正则表达式中+?\s+.+?\s+.+?\s+.+?\s+.+?\s+?就不要再出现了。

    3 )为提高效率,尽量使用普通字符串( plain text )匹配,少用.*+?等符号
    jackal
        16
    jackal  
       2016-05-02 11:52:49 +08:00
    @explist
    快还是慢,数据说话。(你可以优化正则表达式,或者再用其他的第三方库 parser 来写一个,然后做一个时间比较, 希望你贴上一个比较数据)

    正则表达式并不慢,只要用对地方。
    xiamx
        17
    xiamx  
       2016-05-02 11:54:59 +08:00
    Html 是 Context Free Grammar ,不是 Regular Language 所以不能被 Regular expression 识别
    explist
        18
    explist  
    OP
       2016-05-02 11:55:07 +08:00
    @jackal 说得太好了,胜读十年书啊
    能不能写个示范出来?
    explist
        19
    explist  
    OP
       2016-05-02 11:59:48 +08:00
    @jackal 贴 个比较数据对新手来说要求有点高啊,以上所说的第三方库都没学习过,还要从头来,只知道一个自带的:
    html.parser.HTMLParser
    N4HS3zwwKs7wira0
        20
    N4HS3zwwKs7wira0  
       2016-05-02 12:00:26 +08:00
    @jackal Regex 不加奇怪的语法是 O(n). Suffix array 可以 O(m), m 是 query string length , n 是 text length 。但现在 Regex 语法太杂,如果不能用 DFA model ,复杂度很容易 blow up.
    explist
        21
    explist  
    OP
       2016-05-02 12:02:25 +08:00
    @jackal 就是不知道怎么让 . 匹配换行,[.\n] 这样试了不行
    explist
        22
    explist  
    OP
       2016-05-02 12:08:52 +08:00
    这下好了点: r'<p class="m_name">\s+<a href=".+?(\d+)/"\s*title="(.+?)"[\s\S]+?<p class="s_name".+?title="(.+?)"><'
    N4HS3zwwKs7wira0
        23
    N4HS3zwwKs7wira0  
       2016-05-02 12:09:15 +08:00
    @explist Add (?s) before your regexp.
    jackal
        24
    jackal  
       2016-05-02 12:14:07 +08:00   1
    我看到非常多的网友在回复中说, 不能用正则表达式来做这件事情。

    我已经在我的回复中表达了这一点:“抛开立场之见”, 如果这个任务相对简单,不做其他扩展, 则完全可以用正则表达式来完成。

    也请大家在做结论的时候, 切勿简单的思考或者表明立场。 实际上, 不少的网友心里并没有认真思考过, 什么叫合适, 什么叫不合适, 而是简单尊从 StackOverflow 或者某一些结论。

    任何正确的结论都有前提条件; 当前提条件不满足了, 正确的就可能变成错误的结论, 希望大家深思。
    jackal
        25
    jackal  
       2016-05-02 12:32:07 +08:00
    @explist 匹配换行符, 使用[\s\S]

    不建议从<p class="m_name">里面去抓数据。

    衡量性能, 请安装一个 RegexBuddy (这个是收费的)或者有好几个网站提供网页(免费,请自己找一下)来做类似的事情。
    cevincheung
        26
    cevincheung  
       2016-05-02 12:36:08 +08:00
    xpath 啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊!!!!
    jarlyyn
        27
    jarlyyn  
       2016-05-02 15:18:05 +08:00 via Android
    @jackal

    既然这个观点是你提出来的,那烦请给出范例。

    作为一个方面做 mud 机器人正则写到吐,现在写 web 各种 class 混用的人,好奇怎么处理多 class 的 html.

    我就不提万一 html 里有注释这种蛋疼的情况了。
    fy
        28
    fy  
       2016-05-02 15:59:48 +08:00   1
    @jarlyyn 我觉得没有必要假设网站的作者整天改 html ,以我的经验基本上一改就是全改, xpath 也好 css 选择器也好全都救不回来,老老实实重写。

    正则当然是非常简单粗暴了,胜在方便熟练无依赖(lxml 需要二进制或者编译安装)。我觉得根据场景灵活选择吧,没有必要上纲上线。

    @explist 匹配的时候,最后一个参数后面加上 re.DOTALL ,点就能匹配换行了
    aliipay
        29
    aliipay  
       2016-05-02 16:22:47 +08:00
    @fy lxml 用 xpath 吗?感觉写起来也很难看
    binux
        30
    binux  
       2016-05-02 16:32:8 +08:00 via Android
    @fy 但是他们会没事给加的特效,例如加红加粗加下划线
    explist
        31
    explist  
    OP
       2016-05-02 16:38:24 +08:00
    @just1 失败了
    jarlyyn
        32
    jarlyyn  
       2016-05-02 18:03:26 +08:00 via Android
    @fy

    这和假设有什么关系, html 就是这样写的啊,随时加个 new 或者 hot 的类
    just1
        33
    just1  
       2016-05-02 18:58:02 +08:00 via Android
    reg='value="(\d+)"[\s\S]*?"a_name"[\s\S]*?title="(.*?)"[\s\S]*?"s_name"[\s\S]*?title="(.*?)"'
    eoo
        34
    eoo  
       2016-05-02 20:51:15 +08:00 via Android
    对于一个写 PHP 的我来说 我比较倾向于正则表达式 ,如果用类似 Simple HTML DOM 来解析 HTML 或者引入几十 K 甚至上百 几百 K 的类来解析 HTML 估计内存会挂掉。
    bigwahaha
        35
    bigwahaha  
       2016-05-02 20:58:42 +08:00
    xpath 去解析 html 才是正道,楼主快回来
    explist
        36
    explist  
    OP
       2016-05-02 21:30:41 +08:00
    这样速度快了不少:
    re.compile(r'"m_name">.+?/(\d+)/"\s*title="(.+?)".+?s_name".+?title="(.+?)"',re.S)
    fy
        37
    fy  
       2016-05-02 22:02:08 +08:00
    @aliipay 还好吧,其实也不怎么丑, css 选择器也可以用
    xiamx
        38
    xiamx  
       2016-05-02 22:39:47 +08:00
    @jackal "抛开立场之分 (立场之分是指有人要让正则表达式做不该它做的事情,比如解析任意复杂的 html 等) "

    正则表达式_不能_解析 html 。 正则能解析的 HTML 只是当前 HTML 文档的一个子集。
    52cik
        39
    52cik  
       2016-05-03 14:54:50 +08:00
    js 的正则是这样的,不知道其他语言能不能跑。
    musicNum\D+(\d+)[^(]+\("([^"]+)[^(]+...([^)]+)
    $1 是 id ,$2 是歌曲,$3 是歌手
    retanoj
        40
    retanoj  
       2016-05-04 14:58:38 +08:00
    我在源码里直接搜索 li class="clearfix 就定位到 n 条歌曲记录
    不可以先对源码 parse 一下,然后用 xpath 或者各种选择器去选么?
    例如 document.querySelector("li[class='clearfix'] > p[class='m_name'] > a").getAttribute('title')
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     3064 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 31ms UTC 12:33 PVG 20:33 LAX 05:33 JFK 08:33
    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