爬虫第四讲:正则表达式

正则表达式

什么是正则表达式

正则表达式是对字符串操作的一种逻辑公式,就是用事先定义好的一些特定字符、以及这些特定字符的组合,组成一个“规则字符串”,这个规则字符串用来表达对字符串的一种过滤逻辑
Python用re模块实现
在线正则表达式工具
其中有许多常用的正则表达式,非常好用。
一般的网页可以用beautifulsoup等解析库来提取,不过有的网页卸载js的变量中,只能使用正则表达式来提取。正则表达式可以用来筛选和清洗。非常有用。

python中的正则表达式讲解

  • 常见匹配模式

模式 描述
\w 匹配字母数字及下划线
\W 匹配非字母数字下划线
\s 匹配任意空白字符、等价于[\t\n\r\f]
\S 匹配任意非空字符
\d 匹配任意数字,等价于[0-9]
\D 匹配任意非数字
\A 匹配字符串的开始
\Z 匹配字符串结束,如果存在换行,只匹配到换行前的结束字符串
\z 匹配字符串结束
\G 匹配最后匹配完成的位置
\n 匹配一个换行符
\t 匹配一个制表符
^ 匹配字符串的开头
$ 匹配字符串的结尾
. 匹配任意字符,除了换行符,当re.DOTALL标记被指定时,则可以匹配包括换行符的任意字符
[...] 用来表示一组字符,单独列出:[osi]匹配‘o’,'s','j'
[^...] 匹配除括号中内容之外的字符
* 匹配0个过多个表达式
+ 匹配1个或多个表达式
匹配0个或者1个表达式
{n} 精确匹配n个前面的表达式
{n,m} 匹配n到m此有前面的正则表达式定义的片段,贪婪方式
a|b 匹配a或b
() 匹配括号内的表达式,也表示一个组
  • re.match

re.match 尝试从字符串的起始位置匹配一个模式,如果不是起始位置匹配成功的话,match()就返回none。

re.match(pattern,string,flags=0)

sample
1.常规的匹配

import re
content = 'Hello 3232 4324 World_This is a Regex Demo'
print(len(content))
result = re.match('^Hello\s\d{4}\s\d{4}\s\w{10}.*mo$',content)
print(result)
print(result.group())#匹配结果
print(result.span())#匹配结果在content中的范围

2.泛匹配--用.* 表示任意字符

import re
content = 'Hello 3232 4324 World_This is a Regex Demo'
result = re.match('^Hello.*mo$',content)
print(result)
print(result.group())
print(result.span())

3.匹配目标
一般我们把匹配目标用小括号括起来,然后指定左右端点,才能找到。

import re
content = 'Hello 3232 4324 World_This is a Regex Demo'
result = re.match('^Hello\s(\d{4})\s(\d+)\s.*mo$',content)
print(result)
print(result.group(1))#第一个小括号括起来匹配的内容
print(result.group(2))#第二个小括号括起来的内容
print(result.span())

4.贪婪匹配

import re
content = 'Hello 3232 4324 World_This is a Regex Demo'
result = re.match('^He.*(\d+).*mo$',content)
print(result)
print(result.group(1))
print(result.span())

*结果是4,因为.*表示任意字符,默认贪婪模式会匹配尽量多的任意字符,而\d+至少要匹配一个数字,所以就一直匹配到最后一个数字之前。
5.非贪婪模式

import re
content = 'Hello 3232 4324 World_This is a Regex Demo'
result = re.match('^He.*?(\d+).*mo$',content)
print(result)
print(result.group(1))
print(result.span())

.*?是非贪婪模式,匹配尽可能少字符。

6.匹配模式

import re
content = "Hello 3232 4324 World_This \nis a Regex Demo\n"
result = re.match('^He.*?(\d+).*mo$',content)
print(result)

*结果为none,原因是.不能匹配换行符,修改之后可以匹配

import re
content = "Hello 3232 4324 World_This \nis a Regex Demo\n"
result = re.match('^He.*?(\d+).*mo$',content,re.S)
print(result)

添加re.S后即可

7.转义
正则表达式中的许多特殊字符如果不做转义就无法匹配,会被认为是关键字使用,比如*.[]等等。以下就是不转义的例子

import re
content = 'price $50.09'
result = re.match('price $50.09',content)
print(result)

结果返回none,没匹配到。
转义之后才能匹配

import re
content = 'price $50.09'
result = re.match('price \$50\.09',content)
print(result)

最常见用法总结,尽量使用泛匹配模式,使用括号得到匹配目标,尽量使用非贪婪模式,使用re.S,因为HTML中有大量的换行符这样.就能匹配任意字符了。

  • re.search

    re.match从字符串的开头匹配,比如pattern无法匹配content的第一个字符,那么直接返回none。而re.search会搜索整个字符串,只要搜索到符合pattern的内容就返回第一个成功的匹配。以下是sample。
import re
content = "jlljjljh Hello 1234 World_This is a Regex Demo,Don't forget!$"
result = re.match('Hello.*?(\d+).*?',content)
print(result)#用match的话,result返回none
result = re.search('Hello.*?(\d+).*?',content)
print(result)#可以匹配
print(result.group(1))#返回1234

总结,尽量用re.search方法,尽量使用re.S,用非贪婪模式

练习

import re
html = '<div class="songList songList960 clearfix"><ol id="f1"><li><input type="checkbox" value="11417@" name="Url" class="check"> <span class="songNum topRed">01.</span> <a target="_1" href="/play/11417.htm" class="songName cBlue">大海</a></li><li><input type="checkbox" value="64541@" name="Url" class="check"> <span class="songNum topRed">02.</span> <a target="_1" href="/play/64541.htm" class="songName">天路</a></li><li><input type="checkbox" value="65937@" name="Url" class="check"> <span class="songNum topRed">03.</span> <a target="_1" href="/play/65937.htm" class="songName">再回首</a></li><li><input type="checkbox" value="59930@" name="Url" class="check"> <span class="songNum">04.</span> <a target="_1" href="/play/59930.htm" class="songName cBlue">突然的自我</a></li><li><input type="checkbox" value="1462@" name="Url" class="check"> <span class="songNum">05.</span> <a target="_1" href="/play/1462.htm" class="songName">甘心情愿</a></li></ol></div>'
result = re.search('songName.*?>(.*?)</a>.*',html,re.S)
print(result)
print(result.group(1))

匹配结果:大海

  • re.findall--搜索字符串,以列表形式返回全部能匹配的子串

import re
html = '<div class="songList songList960 clearfix"><ol id="f1"><li><input type="checkbox" value="11417@" name="Url" class="check"> <span class="songNum topRed">01.</span> <a target="_1" href="/play/11417.htm" class="songName cBlue">大海</a></li><li><input type="checkbox" value="64541@" name="Url" class="check"> <span class="songNum topRed">02.</span> <a target="_1" href="/play/64541.htm" class="songName">天路</a></li><li><input type="checkbox" value="65937@" name="Url" class="check"> <span class="songNum topRed">03.</span> <a target="_1" href="/play/65937.htm" class="songName">再回首</a></li><li><input type="checkbox" value="59930@" name="Url" class="check"> <span class="songNum">04.</span> <a target="_1" href="/play/59930.htm" class="songName cBlue">突然的自我</a></li><li><input type="checkbox" value="1462@" name="Url" class="check"> <span class="songNum">05.</span> <a target="_1" href="/play/1462.htm" class="songName">甘心情愿</a></li></ol></div>'
result = re.findall('songName.*?>(.*?)</a>.*?',html)
print(result)
  • 返回值:['大海', '天路', '再回首', '突然的自我', '甘心情愿'] *
  • re.sub---替换字符串中每一个匹配的子串后返回替换后的字符串。

import re
content = 'Hello 3232 4324 World_This is a Regex Demo'
content = re.sub('\d+','',content)#把数字替换为空
print(content)
import re
content = 'Hello 3232 4324 World_This is a Regex Demo'
content = re.sub('\d+','Replacement',content)#数字部分都替换成‘replacement’
print(content)

如果替换的结果包含想替换的内容怎么办,比如像把这2段数字前面加上No.,但数字要保留,怎么办?

import re
content = 'Hello 3232 4324 World_This is a Regex Demo'
content = re.sub('(\d+)',r'No.\1',content)#用r''中间的是原生字符串不用转义了。先把patten小括号括起来,替换内容加\n.这个n表示第几个小括号的内容替换成这样。
print(content)
  • 结果:Hello No.3232 No.4324 World_This is a Regex Demo
  • re.compile---将正则字符串编译成正则表达式对象

    将正则字符串编译成正则表达式对象,便于复用该匹配模式
import re
content = 'Hello 3232 4324 World_This is a Regex Demo'
result = re.match('^He.*?(\d+).*mo$',content,re.S)#用正则字符串匹配
myPatten = re.compile('^He.*?(\d+).*mo$',re.S)#编译正则表达式对象。
result = re.match(mypatten,content)#2种方法一样
print(result)

  • 令人期待的实战例子

爬取豆瓣读书
读取https://book.douban.com 新书速递中的书名、作者、出版社

import requests
import re
content = requests.get('https://book.douban.com/').text
#print(content)
pattern = re.compile('<li.*?cover.*?href="(.*?)".*?title="(.*?)".*?more-meta.*?author">(.*?)</span>.*?year">(.*?)</span>.*?</li>',re.S)
#print(pattern)
results = re.findall(pattern,content)
print(results)

这个例子我跑失败了,卡死...

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,390评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,821评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,632评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,170评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,033评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,098评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,511评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,204评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,479评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,572评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,341评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,213评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,576评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,893评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,171评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,486评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,676评论 2 335