python爬虫数据提起二之正则表达式

1 正则表达式简介

正则表达式(处理字符串强大的工具,有特定的语法结构) 功能:实现字符串的检索,替换,匹配验证

2 常用匹配方法

  1. match() 从字符串起始位置匹配正则表达式,如果匹配,就返回匹配成功结果如果不匹配,就返回None。 参数1 正则表达式 参数2 要匹配的字符串
import re

content = 'Hello 123 4567 World_This is a Regex Demo'

print(len(content))

result = re.match('^Hello\s\d\d\d\s\d{4}\s\w{10}',content)
print(result)
print(result.group())#输出匹配的内容
print(result.span())#输出匹配的范围

41
<re.Match object; span=(0, 25), match='Hello 123 4567 World_This'>
Hello 123 4567 World_This
(0, 25)

匹配目标 从字符串中提取一部分内容 可以使用()

import re

content = 'Hello 123 4567 World_This is a Regex Demo'

result = re.match('^Hello\s(\d+)\s(\d+)\sWorld',content)
print(result)
print(result.group())
print(result.group(1))
print(result.group(2))
print(result.span())

<re.Match object; span=(0, 20), match='Hello 123 4567 World'>
Hello 123 4567 World
123
4567
(0, 20)

通用匹配 . 匹配任意字符 除换行符
* 匹配前面的字符无限次

import re

content = 'Hello 123 4567 World_This is a Regex Demo'

result = re.match('^Hello.*Demo$',content)
print(result)
print(result.group())

<re.Match object; span=(0, 41), match='Hello 123 4567 World_This is a Regex Demo'>
Hello 123 4567 World_This is a Regex Demo

贪婪与非贪婪

# 贪婪模式,.*会匹配尽可能多的字符,直到剩下一个数字7和(\d+)匹配
import re

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

<re.Match object; span=(0, 40), match='Hello 1234567 World_This is a Regex Demo'>
7

贪婪匹配下,. 会尽可能多的匹配字符 使用.? 拒绝贪婪模式 尽可能少匹配字符

import re

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

<re.Match object; span=(0, 40), match='Hello 1234567 World_This is a Regex Demo'>
1234567

3 修饰符

import re

content = '''Hello 1234567 World_This
is a Regex Demo'''
result = re.match('^He.*?(\d+).*?Demo$',content) # 匹配不了换行符,在每一行内进行匹配

print(result.group(1))

AttributeError: 'NoneType' object has no attribute 'group'

. 匹配换行符之外的任意字符 添加修饰符 re.S (使.匹配包括换行符在内的所有字符)

import re

content = '''Hello 1234567 World_This
is a Regex Demo'''
result = re.match('^He.*?(\d+).*?Demo$',content,re.S) # 添加re.S后将整个字符串视为一个整体

print(result.group(1))

1234567

  • re.I 使匹配对大小写不敏感
  • re.L 做本地化识别(locale-aware)匹配
  • re.M 多行匹配 ,影响^和$
  • re.S 使.匹配包括换行符在内的所有字符
  • re.U 根据Unicode字符集解析字符 影响 \w \W \b \B

网页匹配 常用 re.S re.L

4 转义匹配

.匹配除换行符以外的任意字符 如果目标字符串里面包含.

import re

content = '(百度)www.baidu.com'
result = re.match('\(百度\)www\.baidu\.com',content)
print(result)

<re.Match object; span=(0, 17), match='(百度)www.baidu.com'>

遇到正则匹配模式的特殊字符 在前面加反斜杠转义

4.1 search()

   search()匹配整个字符串 返回第一个成功匹配的结果 匹配失败 返回None match() 从字符串起始位置匹配正则表达式

import re

content = 'Auto Hello 1234567 World_This is a Regex Demo'
result = re.match('He.*?(\d+).*Demo',content)
print(result)#返回None

import re

content = 'Auto Hello 1234567 World_This is a Regex Demo'
result = re.search('He.*?(\d+).*Demo',content)
print(result)

None
<re.Match object; span=(5, 45), match='Hello 1234567 World_This is a Regex Demo'>

4.1.1 实例:

利用search方法 提取 html文档中 齐秦 往事随风

  • 查看文件 html.txt
import re

html = '''<div id="songs-list">
<h2 class="title">经典老歌</h2>
<p class="introduction">
经典老歌列表
</p>
<ul id="list" class="list-group">
<li data-view="2">一路上有你</li>
<li data-view="7">
<a href="/2.mp3" singer="任贤齐">沧海一声笑</a>
</li>
<li data-view="4" class="active">
<a href="/3.mp3" singer="齐秦">往事随风</a>
</li>
<li data-view="6"><a href="/4.mp3" singer="beyond">光辉岁月</a></li>
<li data-view="5"><a href="/5.mp3" singer="陈慧琳">记事本</a></li>
<li data-view="5">
<a href="/6.mp3" singer="邓丽君">但愿人长久</a>
</li>
</ul>
</div>'''
result = re.search('li.*?active.*?singer="(.*?)">(.*?)</a>',html,re.S)
print(result)
print(result.group(1))
print(result.group(2))

<re.Match object; span=(15, 286), match='list">\n<h2 class="title">经典老歌</h2>\n<p class="in>
齐秦
往事随风

4.2 findall()

   findall()搜索整个字符串 返回匹配规则的所有内容 实例:
   利用 findall方法 提取所有a节点的超链接 歌手 歌名

result = re.findall('li.*?href="(.*?)".*?singer="(.*?)">(.*?)</a>',html,re.S)
print(result)

[('/2.mp3', '任贤齐', '沧海一声笑'), ('/3.mp3', '齐秦', '往事随风'), ('/4.mp3', 'beyond', '光辉岁月'), ('/5.mp3', '陈慧琳', '记事本'), ('/6.mp3', '邓丽君', '但愿人长久')]

遍历 依次获取每组内容

for results in result:
  print(results)

('/2.mp3', '任贤齐', '沧海一声笑')
('/3.mp3', '齐秦', '往事随风')
('/4.mp3', 'beyond', '光辉岁月')
('/5.mp3', '陈慧琳', '记事本')
('/6.mp3', '邓丽君', '但愿人长久')

对应索引依次取出

for results in result:
  print(results[0],results[1],results[2])

/2.mp3 任贤齐 沧海一声笑
/3.mp3 齐秦 往事随风
/4.mp3 beyond 光辉岁月
/5.mp3 陈慧琳 记事本
/6.mp3 邓丽君 但愿人长久

4.3 sub()

参数1 规则 参数2 规则 参数3 字符串

strs = '34iaU8hw9kcj2k3O0jc7oqqw8W'

去掉所有数字

import re

strs = '34iaU8hw9kcj2k3O0jc7oqqw8W'

strs = re.sub('\d+','',strs)

print(strs)

iaUhwkcjkOjcoqqwW

获取html文本 所有li节点的歌名

import re

html = '''<div id="songs-list">
<h2 class="title">经典老歌</h2>
<p class="introduction">
经典老歌列表
</p>
<ul id="list" class="list-group">
<li data-view="2">一路上有你</li>
<li data-view="7">
<a href="/2.mp3" singer="任贤齐">沧海一声笑</a>
</li>
<li data-view="4" class="active">
<a href="/3.mp3" singer="齐秦">往事随风</a>
</li>
<li data-view="6"><a href="/4.mp3" singer="beyond">光辉岁月</a></li>
<li data-view="5"><a href="/5.mp3" singer="陈慧琳">记事本</a></li>
<li data-view="5">
<a href="/6.mp3" singer="邓丽君">但愿人长久</a>
</li>
</ul>
</div>'''

results = re.findall('<li.*?>\s*?(<a.*?>)?(\w+)(</a>)?\s*?</li>',html,re.S)
for result in results:
    print(result[1])

一路上有你
沧海一声笑
往事随风
光辉岁月
记事本
但愿人长久

利用sub方法 去掉a节点 再用findall方法提取

import re

html = '''<div id="songs-list">
<h2 class="title">经典老歌</h2>
<p class="introduction">
经典老歌列表
</p>
<ul id="list" class="list-group">
<li data-view="2">一路上有你</li>
<li data-view="7">
<a href="/2.mp3" singer="任贤齐">沧海一声笑</a>
</li>
<li data-view="4" class="active">
<a href="/3.mp3" singer="齐秦">往事随风</a>
</li>
<li data-view="6"><a href="/4.mp3" singer="beyond">光辉岁月</a></li>
<li data-view="5"><a href="/5.mp3" singer="陈慧琳">记事本</a></li>
<li data-view="5">
<a href="/6.mp3" singer="邓丽君">但愿人长久</a>
</li>
</ul>
</div>'''

html = re.sub('<a.*?>|</a>','',html) # 用参数2来替换参数1
# print(html)
results = re.findall('<li.*?>(.*?)</li>',html,re.S)
# print(results)
for result in results:
    print(result.strip())#去掉字符串两边的空格或者换行符

一路上有你
沧海一声笑
往事随风
光辉岁月
记事本
但愿人长久

4.4 compile()

compile() 将正则字符串编译成正则表达式对象 以便复用 也可以传入修饰符 例如re.S 相当于做了一层封装

import re

str1 = '2016-12-25 12:00'
str2 = '2017-12-17 11:55'
str3 = '2018-12-23 15:00'
pattern = re.compile('\d{2}:\d{2}')
result1 = re.sub(pattern,'',str1)
result2 = re.sub(pattern,'',str2)
result3 = re.sub(pattern,'',str3)
print(result1,result2,result3)

2016-12-25 2017-12-17 2018-12-23

5 正则匹配规则

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

6 实战

   利用requests库和正则表达式 抓取猫眼电影TOP100

  1. 目标 抓取电影名称 时间 评分 图片等
    地址: https://maoyan.com/board/4?offset=0 结果以文件形式保存
  2. 分析
    • offset 代表偏移量 如果为n 电影序号为n+1~n+10 每页显示10个
    • 获取100 分开请求10次 offset 分别为0 10 20...90 利用正则提取相关信息
  3. 抓取页面
import requests
#爬取第一页 页面信息
def get_one_page(url):
    header = {
    "User-Agent":"Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/534.16 (KHTML, like Gecko) Chrome/10.0.648.133 Safari/534.16",
    }
    response = requests.get(url,headers=header)
    if response.status_code == 200:#判断是否请求成功
        return response.text

    return None

def main():
    url = 'http://maoyan.com/board/4'
    html = get_one_page(url)#调用请求函数
    print(html)
main()
  1. 分析页面
电影信息对应节点为<dd> 
提取排名 class 为 board-index i节点内 正则 <dd>.*?board-index.*?>(.*?)</i>
电影图片 查看为第二个img链接 <dd>.*?board-index.*?>(.*?)</i>.*?data-src="(.*?)"
电影名字 p节点 class 为name <dd>.*?board-index.*?>(.*?)</i>.*?data-src="(.*?)".*?name.*?a.*?>(.*?)</a>
主演    <dd>.*?board-index.*?>(.*?)</i>.*?data-src="(.*?)".*?name.*?a.*?>(.*?)</a>.*?star.*?>(.*?)</p>
发布时间 <dd>.*?board-index.*?>(.*?)</i>.*?data-src="(.*?)".*?name.*?a.*?>(.*?)</a>.*?star.*?>(.*?)</p>.*?releasetime.*?>(.*?)</p>
评分 <dd>.*?board-index.*?>(.*?)</i>.*?data-src="(.*?)".*?name.*?a.*?>(.*?)</a>.*?star.*?>(.*?)</p>.*?releasetime.*?>(.*?)</p>.*?integer.*?>(.*?)</i>.*?fraction.*?>(.*?)</i>.*?</dd>
  1. 定义分析页面的方法 parse_one_page()
import requests
import re
def get_one_page(url):
    header = {
    "User-Agent":"Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/534.16 (KHTML, like Gecko) Chrome/10.0.648.133 Safari/534.16",
    }
    response = requests.get(url,headers=header)
    if response.status_code == 200:
        return response.text
    return None

def parse_one_page(html):
    pattern = re.compile(
    '<dd>.*?board-index.*?>(.*?)</i>.*?data-src="(.*?)".*?name.*?a.*?>(.*?)</a>.*?star.*?>(.*?)</p>.*?releasetime.*?>(.*?)</p>.*?integer.*?>(.*?)</i>.*?fraction.*?>(.*?)</i>.*?</dd>',re.S
    )
    items = re.findall(pattern,html)
    print(items)
# 定义一个main函数 调用get_one_page 发送请求 打印结果
def main():
    url = 'http://maoyan.com/board/4'
    html = get_one_page(url)
    # print(html)
    parse_one_page(html)
main()
  1. 将匹配结果遍历 生成字典,单页面电影提取
import requests
import re
import json
# 请求页面
def get_one_page(url):
    header = {
    "User-Agent":"Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/534.16 (KHTML, like Gecko) Chrome/10.0.648.133 Safari/534.16",
    }
    response = requests.get(url,headers=header)
    if response.status_code == 200:
        return response.text
    return None

#解析页面
def parse_one_page(html):# html为网页源码
    pattern = re.compile(
    '<dd>.*?board-index.*?>(.*?)</i>.*?data-src="(.*?)".*?name.*?a.*?>(.*?)</a>.*?star.*?>(.*?)</p>.*?releasetime.*?>(.*?)</p>.*?integer.*?>(.*?)</i>.*?fraction.*?>(.*?)</i>.*?</dd>',re.S
    )#定义规则
    items = re.findall(pattern,html)#查找整个页面
    # print(items)
    #遍历结果生成字典
    for item in items:
        yield {
        'index':item[0],
        'image': item[1],
        'title': item[2].strip(),
        'actor': item[3].strip()[3:] if len(item[3]) > 3 else'',
        'time': item[4].strip()[5:] if len(item[4]) > 5 else '',
        'score': item[5].strip()+item[6].strip()
        }
        #返回一个生成器 yield

#写入文件
def write_to_file(content):
    with open('result.txt','a',encoding='utf-8') as f:
        print(type(json.dumps(content)))
        f.write(json.dumps(content,ensure_ascii=False)+'\n')


# 定义一个main函数 调用get_one_page 发送请求 打印结果
def main():
    url = 'http://maoyan.com/board/4'
    html = get_one_page(url)
    # print(html)
    for item in parse_one_page(html):#遍历生成器
        write_to_file(item)
main()

分页爬取 定义一个main函数 调用get_one_page 发送请求 打印结果

def main(offset):
    url = 'http://maoyan.com/board/4?offset=' + str(offset)
    html = get_one_page(url)
    # print(html)
    for item in parse_one_page(html):#遍历生成器
        write_to_file(item)

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

推荐阅读更多精彩内容