Python网络爬虫案例实战:解析网页:正则表达式解析网页

Python网络爬虫案例实战:解析网页:正则表达式解析网页

正则表达式并不是Python的一部分。正则表达式是用于处理字符串的强大工具,拥有自己独特的语法以及一个独立的处理引擎,效率上可能不如str自带的方法,但功能十分强大。得益于这一点,在提供了正则表达式的语言中,正则表达式的语法都是一样的,区别只在于不同的编程语言实现支持的语法数量不同;但不用担心,不被支持的语法通常是不常用的部分。

图5-1展示了使用正则表达式进行匹配的流程。

正则表达式的大致匹配过程是:依次拿出表达式和文本中的字符比较,如果每一个字符都能匹配,则匹配成功;一旦有匹配不成功的字符则匹配失败。如果表达式中有量词或边界,那么这个过程会稍微有一些不同。

在提取网页中的数据时,可以先把源代码变成字符串,然后用正则表达式匹配想要的数据。使用正则表达式可以迅速地用极简单的方式实现字符串的复杂控制。
在这里插入图片描述

表5-1是常见的正则字符和含义。
在这里插入图片描述
下面介绍Python正则表达式的3种方法,分别是match、search 和findall。

5.2.1字符串匹配

本节利用 Python 中的re.match实现字符串匹配并找到匹配的位置。而re.match的意思是从字符串起始位置匹配一个模式,如果从起始位置匹配不了,match()就返回none。re.match的语法格式为:

re.match(string[,pos[,endpos]]| re.match(pattern,string[,flags]

match只找到一次可匹配的结果即返回。
这个方法将从string的 pos下标处开始尝试匹配 pattern;如果 pattern结束时仍可匹配,则返回一个match对象;如果匹配过程中 pattern无法匹配,或者匹配未结束就已到达endpos,则返回none。pos 和 endpos的默认值分别为0和len(string);re.match()无法指定这两个参数,参数 flags用于编译pattern时指定匹配模式。
注意:这个方法并不是完全匹配。当pattern结束时,若 string还有剩余字符,则仍然视为成功。想要完全匹配,可以在表达式末尾加上边界匹配符’$'。

[例5-1)使用两个字符串匹配并找到匹配的位置。

# encoding: UTF-8 
import re 
m=re.match('www','www.taobao.com')
print('匹配的结果:',m)
print('匹配的起始与终点:',m.span())
print('匹配的起始位置:',m.start())
print('匹配的终点位置:',m.end())       

运行程序,输出如下:

匹配的结果: <re.Match object; span=(0, 3), match='www'>
匹配的起始与终点: (0, 3)
匹配的起始位置: 0
匹配的终点位置: 3 

上面例子中的pattern只是一个字符串,也可以把 pattern改成正则表达式,从而匹配具有一定模式的字符串,例如:

# encoding: UTF-8 
import re 

line = 'Fat apples are smarter than bananas, is it right?'

# 使用 re.match 进行匹配
m = re.match(r'(\w+) (\w+) (?P<sign>.*)', line)

# 检查是否匹配成功

print('匹配的整句话:', m.group(0))
print('匹配的第一个结果:', m.group(1))
print('匹配的第二个结果:', m.group(2))
print('匹配的结果列表:', m.groups())  # 使用 groups() 获取所有捕获组

运行程序,输出如下:

匹配的整句话: Fat apples are smarter than bananas, is it right?
匹配的第一个结果: Fat
匹配的第二个结果: apples
匹配的结果列表: ('Fat', 'apples', 'are smarter than bananas, is it right?')

为什么要在match的模式前加上r呢?
r’(\w+)(\w+)(?P.*)‘前面的r的意思是raw string,代表纯粹的字符串,使用它就不会对引号中的反斜杠八’进行特殊处理。因为在正则表达式中有一些类似’d’(匹配任何数字)的模式,所以模式中的单个反斜杠’'符号都要进行转义。
假如需要匹配文本中的字符"",使用编程语言表示的正则表达式里就需要4个反斜杠
“\\”,前两个反斜杠“\”和后两个反斜杠“\”各自在编程语言中转义成一个反斜杠“\”,
所以4个反斜杠“\\”就转义成了两个反斜杠“\”,这两个反斜杠“\”最终在正则表达式中转义成一个反斜杠“\”。

5.2.2起始位置匹配字符串

re.match只能从字符串的起始位置进行匹配,而re.search 扫描整个字符串并返回。re.search()方法扫描整个字符串,并返回第一个成功的匹配,如果匹配失败,则返回 None。
与re.match()方法不同,re.match()方法要求必须从字符串的开头进行匹配,如果字符串的开头不匹配,那么整个匹配就失败了;re.search()并不要求必须从字符串的开头进行匹配,也就是说,正则表达式可以是字符串的一部分。
re.search()的语法格式为:

re.search(pattern,string,flags = 0

其中,pattern:正则中的模式字符串。string:要被查找替换的原始字符串。flags:标志位,用于控制正则表达式的匹配方式,如:是否区分大小写、多行匹配等。
[例5-2]从起始位置匹配字符串演示实例。

import re  
content = 'Hello 123456789 Word_This is just a test 666 Test'
result = re.search('(\d+).*?(\d+).*', content)   
print(result)
print(result.group())    # print(result.group(0)) 同样效果字符串
print(result.groups())
print(result.group(1))
print(result.group(2))

运行程序,输出如下:

<re.Match object; span=(6, 49), match='123456789 Word_This is just a test 666 Test'>
123456789 Word_This is just a test 666 Test
('123456789', '666')
123456789
666

适当调用以下代码,可实现数字匹配。例如:

import re  
content = 'Hello 123456789 Word_This is just a test 666 Test'
result = re.search('(\d+)', content)   
print(result)
print(result.group())    # print(result.group(0)) 同样效果字符串
print(result.groups())
print(result.group(1))

运行程序,输出如下:

<re.Match object; span=(6, 15), match='123456789'>
123456789
('123456789',)
123456789

5.2.3所有子串匹配

re.findall()在字符串中找到正则表达式所匹配的所有子串,并返回一个列表;如果没有找到匹配的,则返回空列表。返回结果是列表类型,需要遍历一下才能依次获取每组内容。re.findall()的语法格式为:

findall(patern,string,flags = 0

其中,pattern:正则中的模式字符串。string:要被查找替换的原始字符串。flags:标志位,用于控制正则表达式的匹配方式,如:是否区分大小写、多行匹配等。
[例5-3]匹配所有子串演示。

import re  
content = 'Hello 123456789 Word_This is just a test 666 Test'
results = re.findall('\d+', content)    
print(results)
for result in results:
    print(result)

运行程序,输出如下:

['123456789', '666']
123456789
666

findall与 match、search不同的是,findall能够找到所有匹配的结果,并且以列表的形式返回。

5.2.4Requests爬取猫眼电影排行

本节利用Requests库和正则表达式来爬取猫眼电影TOP100的相关内容。Requests比 urllib 使用更加方便,在此选用正则表达式来作为解析工具。
[例 5-4]利用 Requests 和正则表达式爬取猫眼电影排行信息。

#-*- coding: utf-8 -*-
import re
import os
import json
import requests
from multiprocessing import Pool
from requests.exceptions import RequestException

def get_one_page(url):
    '''
    获取网页html内容并返回
    '''
    try:
        # 获取网页html内容
        response = requests.get(url)
        # 通过状态码判断是否获取成功
        if response.status_code == 200:
            return response.text
        return None
    except RequestException:
        return None

def parse_one_page(html):
    '''
    解析HTML代码,提取有用信息并返回
    '''
    # 正则表达式进行解析
    pattern = re.compile('<dd>.*?board-index.*?>(\d+)</i>.*?data-src="(.*?)".*?name">'
        + '<a.*?>(.*?)</a>.*?"star">(.*?)</p>.*?releasetime">(.*?)</p>'
        + '.*?integer">(.*?)</i>.*?fraction">(.*?)</i>.*?</dd>', re.S)
    # 匹配所有符合条件的内容
    items = re.findall(pattern, html)

    for item in items:
        yield {
            'index': item[0],
            'image': item[1],
            'title': item[2],
            'actor': item[3].strip()[3:],
            'time': item[4].strip()[5:],
            'score': item[5] + item[6]
        }

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

def save_image_file(url, path):
    '''
    保存电影封面
    '''
    ir = requests.get(url)
    if ir.status_code == 200:
        with open(path, 'wb') as f:
            f.write(ir.content)
            f.close()

def main(offset):
    url = 'http://maoyan.com/board/4?offset=' + str(offset)
    html = get_one_page(url)
    # 封面文件夹不存在则创建
    if not os.path.exists('covers'):
        os.mkdir('covers')

    for item in parse_one_page(html):
        print(item)
        write_to_file(item)
        save_image_file(item['image'], 'covers/' + '%03d'%int(item['index']) + item['title'] + '.jpg')

if __name__ == '__main__':
    # 使用多进程提高效率
    pool = Pool()
    pool.map(main, [i*10 for i in range(10)])

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值