Python中一些简单的正则表达式(爬虫所需(.*?))

这篇博客旨在介绍使用爬虫时一些常用的正则表达式。

在之前,我一直都是一个谈正则表达式色变的人。因为正则表达式实在是太多太多,想要记得除非是经常用,否则也很难完全掌握其中所有的内容。所以这些东西都是现用现查,然后要一个一个的搜索,将自己所需要的进行查找。所以学习正则表达式都是根据实际需求来进行学习。

正好由于爬虫的需要(因为爬虫时HTML源码的结构非常规律,我们找到规律后根据规律来提取内容,就需要用到正则表达式),所以再次对爬虫所需的一些正则表达式做了整理。(简单超详细的爬虫教程传送门


实际应用

在爬虫中,我们通常是要根据我们目标字段前后的对应关系,然后在整个HTML源码中找到与之相匹配位置进行抓取。在爬虫中,通常文本的一部分长这样(html的内容):

html = """
<div class="comment-item" data-cid="1340993183">
<div class="avatar">
<a href="https://www.douban.com/people/ydyie/" title="时间之葬">
<img class="" src="https://img1.doubanio.com/icon/u2126832-179.jpg"/>
</a>
</div>
<div class="comment">
<h3>
<span class="comment-vote">
<span class="votes">52</span>
<input type="hidden" value="1340993183"/>
<a class="j a_show_login" href="javascript:;" onclick="">有用</a>
</span>
<span class="comment-info">
<a class="" href="https://www.douban.com/people/ydyie/">时间之葬</a>
<span>看过</span>
<span class="allstar40 rating" title="推荐"></span>
<span class="comment-time " title="2018-03-14 10:59:11">
                    2018-03-14
                </span>
</span>
</h3>
<p class=""> 作为一个故事它逻辑漏洞太多,作为一个创世寓言它缺少一点完整的体系,作为一种视觉和装置艺术它略显单调,但好在它提供了一种重新看待人类与万物之间关系的可能
        </p>
</div>
</div>
"""

我们如果提取它其中的短评,也就是下述内容:

作为一个故事它逻辑漏洞太多,作为一个创世寓言它缺少一点完整的体系,作为一种视觉和装置艺术它略显单调,但好在它提供了一种重新看待人类与万物之间关系的可能

需要通过 <p class=""></p> 这两个字符串来进行查找,中间夹着的部分就是我们所需要的。其Python实现代码如下:

import re
re.findall(r'<p class=""> (.*?)<', html, re.S) # 使用 r'' 表示原始字符串不需要转义

一步一步来解析,首先看我们需要的是两个字符串中间夹着的内容,这里使用的是 '<p class=""> (.*?)<' ,所以关键是 (.*?) 这个语句,其实它就是指代符合前面提到两个字符中中间夹着的内容。

那里面的 ().*? ,分别代表什么呢?下面我们一步一步来运行,来进行探索发现。首先规定两个字符串:

str1 = 'aababaaba'

然后我们一个一个来进行尝试。


. 匹配除换行符以外的任意一个字符

输入:

>>> re.findall(r'a.b', str1)

输出:

['aab', 'aab']

单纯的一个 . 就是匹配除 \n (换行符)以外的任意一个字符。 a.b 在原字符串 'aababaaba' 中,从左到右扫,第一个字母是a,就找第三个是不是b,发现确实是的,就将aab提取出来。然后接着扫,第四个字母是a,但是第六个不是b,所以不做任何操作。直到第六个字母是a且第八个是b,就将它们提取出来,同样也是aab。后面没有满足条件的了,所以最终结果就是['aab', 'aab']

* 前面那个字符,重复0次或更多次

输入:

>>> re.findall(r'a*b', str1)

输出:

['aab', 'ab', 'aab']

在例子中,* 前面跟着a,所以a重复几次都会被提取出来。如果有aaaaaaaaaaab,也一样会被提取出来。

.* 贪婪算法,尽可能重复多次

输入:

>>> re.findall(r'a.*b', str1)

输出:

['aababaab']

.*组合在一块儿,就变成了另一个意思。在例子中,就是从第一个字母开始扫,遇到a就开始截取,然后找所有的b,最后找到以b结尾尽可能长的一段字符串。

.*? 非贪婪算法,遇到一次停一次

输入:

>>> re.findall(r'a.*?b', str1)

输出:

['aab', 'ab', 'aab']

只要是以a开始,以b结束,我们就进行截取,从左至右扫描着截取。相比前面的.,只是多了一个'ab',也就是ab之间不含任何元素,同样会被提取出来。注意,这里扫过的元素不会再被扫。

(.*?) 非贪婪算法,遇到一次停一次,只保留括号中的内容

输入:

>>> re.findall(r'a(.*?)b', str1)

输出:

['a', '', 'a']

这个相对于上一步操作只多了一个括号。它的意思也非常容易理解,就是我们只保留括号中间的内容,两边的内容全部都不要,所以相对于上一个不带括号的操作,就是将提取出来字符串左边的a与右边的b全部都删掉。


re.S

细心的童鞋发现了我们前面的代码中还用到了 re.S 参数,其表示什么呢?

表示遇到 \n 也就是换行操作时,不对操作进行中断。下面列出一个例子:

str2 = 'aababaaba \n ddsdab'

输入:

>>> re.findall(r'a.*?b', str2, re.S)

输出:

['aab', 'ab', 'aab', 'a \n ddsdab']

输入:

>>> re.findall(r'a.*?b', str2)

输出:

['aab', 'ab', 'aab', 'ab']

造成上面两种情况差异的就是\n会不会影响前面先扫到的a。不加re.S相当于每次换行都要重新扫一遍。

所以大家应该知道为什么爬取短评和评分需要使用这两个正则表达式了吗?

re.findall(r'<span class="comment-info">(.*?)<span class="comment-time "', html, re.S)
re.findall(r'<span class="allstar(.*?)0 rating"', item, re.S)

最后,正则表达式博大精深,这里只是其冰山一角,以后还是要多多进行学习。

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值