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 · 4185 次点击
    这是一个创建于 2910 天前的主题,其中的信息可能已经有所发展或是发生改变。
    网站: 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 )匹配,少用.*+?等符号
    lianxiangru
        15
    lianxiangru  
       2016-05-02 11:51:38 +08:00
    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
    lianxiangru
        20
    lianxiangru  
       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="(.+?)"><'
    lianxiangru
        23
    lianxiangru  
       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:28 +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   ·   我们的愿景   ·   实用小工具   ·   2781 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 29ms · UTC 04:05 · PVG 12:05 · LAX 21:05 · JFK 00:05
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.