【爬虫】2.正则表达式

目录

1.正则表达式

2.Python正则表达式

3.xpath爬取豆瓣top250电影

3.1查看xpanth信息

3.2xpath获取电影其他信息

3.3 信息切片

3.3.1 replace()

3.3.2 split()

3.4信息汇总

3.5余下页数信息汇总

3.5改进

4.正则表达式爬取豆瓣top250

4.1 获取网站响应内容

4.2 从响应内容里正则化提取所需内容


 

1.正则表达式

参考:http://www.runoob.com/regexp/regexp-tutorial.html

这里面很详细介绍了正则表达式,我这里就不再赘述。

2.Python正则表达式

参考:http://www.runoob.com/python/python-reg-expressions.html

Pyhon的re 模块使 Python 语言拥有全部的正则表达式功能。

3.xpath爬取豆瓣top250电影

任务说明:

结合requests、re两者的内容爬取豆瓣电影 Top 250里的内容 要求抓取名次、影片名称、国家、导演等字段。

3.1查看xpanth信息

(1)谷歌浏览器 右键关键字选择检查

复制xpath信息:

xpath信息如下:

//*[@id="content"]/div/div[1]/ol/li[1]/div/div[2]/div[2]/p[1]

然后套路代码看看:

from lxml import etree
import requests
url='https://movie.douban.com/top250'
data=requests.get(url).text
s=etree.HTML(data)

info=s.xpath('//*[@id="content"]/div/div[1]/ol/li[1]/div/div[2]/div[2]/p[1]/text()')
print(info)

结果:

 

我们再看看第二部电影的电影信息xpath:

//*[@id="content"]/div/div[1]/ol/li[3]/div/div[2]/div[2]/p[1]

跟第一部对比:

//*[@id="content"]/div/div[1]/ol/li[1]/div/div[2]/div[2]/p[1]

我们发现il[]里面发现了变换,写个代码看看:

我们打印前10名的电影信息:

from lxml import etree
import requests
url='https://movie.douban.com/top250'
data=requests.get(url).text
s=etree.HTML(data)
for i in range(20):
    info=s.xpath('//*[@id="content"]/div/div[1]/ol/li/div/div[2]/div[2]/p[1]/text()')[i]
    print(info)

部分截图如下:

3.2xpath获取电影其他信息

(1)同样的操作我们检查一样电影名,

xpath如下:

//*[@id="content"]/div/div[1]/ol/li[1]/div/div[2]/div[1]/a/span[1]

代码:

from lxml import etree
import requests
url='https://movie.douban.com/top250'
data=requests.get(url).text
s=etree.HTML(data)
title=s.xpath('//*[@id="content"]/div/div[1]/ol/li/div/div[2]/div[1]/a/span[1]/text()')
print(title)

结果:

(2)评分

xpath:

//*[@id="content"]/div/div[1]/ol/li[15]/div/div[2]/div[2]/div/span[2]

代码:

from lxml import etree
import requests
url='https://movie.douban.com/top250'
data=requests.get(url).text
s=etree.HTML(data)
scores=s.xpath('//*[@id="content"]/div/div[1]/ol/li/div/div[2]/div[2]/div/span[2]/text()')
for movies in scores:
    print(movies)

结果如下:


 

(3)名次

xpath:

//*[@id="content"]/div/div[1]/ol/li[14]/div/div[1]/em

from lxml import etree
import requests
url='https://movie.douban.com/top250'
data=requests.get(url).text
s=etree.HTML(data)
for i in range(20):
    rank=s.xpath('//*[@id="content"]/div/div[1]/ol/li/div/div[1]/em/text()')[i]
    print(rank)

结果:

3.3 信息切片

我们在获取影片信息的时候是这样的:

我们想把导演的名字获取出来。

3.3.1 replace()

首先介绍一下python replace()的方法:

(1)replace()方法语法:

str.replace(old, new[, max])

(2)参数:

old -- 将被替换的子字符串。
new -- 新字符串,用于替换old子字符串。
max -- 可选字符串, 替换不超过 max 次

(3)返回:

返回字符串中的 old(旧字符串) 替换成 new(新字符串)后生成的新字符串,如果指定第三个参数max,则替换不超过 max 次。

(4)我们把空格去掉:

info=s.xpath('//*[@id="content"]/div/div[1]/ol/li[2]/div/div[2]/div[2]/p[1]/text()')[0]
info= info .replace(" ", "")

原结果:

去掉后:

然后我们把换行去掉:

info=s.xpath('//*[@id="content"]/div/div[1]/ol/li[2]/div/div[2]/div[2]/p[1]/text()')[0]
info= info .replace(" ", "").replace("\n", "")

结果:

3.3.2 split()

split()通过指定分隔符对字符串进行切片,如果参数 num 有指定值,则仅分隔 num+1 个子字符串

(1)语法:

str.split(str="", num=string.count(str))

(2)参数:

str -- 分隔符,默认为所有的空字符,包括空格、换行(\n)、制表符(\t)等。
num -- 分割次数。默认为 -1, 即分隔所有。

我这里用默认值分割:

info=s.xpath('//*[@id="content"]/div/div[1]/ol/li[2]/div/div[2]/div[2]/p[1]/text()')[0]
info= info .replace(" ", "").replace("\n", "").split()

结果:

这样我们就可以把导演和主演分别提取出来了。

同样的方法我把国家也导出来,代码如下:

from lxml import etree
import requests
url='https://movie.douban.com/top250'
data=requests.get(url).text
s=etree.HTML(data)

info=s.xpath('//*[@id="content"]/div/div[1]/ol/li[2]/div/div[2]/div[2]/p[1]/text()')[1]
info= info .replace(" ", "").replace("\n", "").split('/')[1]
print(info)

结果:

3.4信息汇总

我们把这些信息一起输出,首先我们输出某一部电影的所有信息

from lxml import etree
import requests
url='https://movie.douban.com/top250'
data=requests.get(url).text
s=etree.HTML(data)
#影片名
title=s.xpath('//*[@id="content"]/div/div[1]/ol/li/div/div[2]/div[1]/a/span[1]/text()')
#
info=s.xpath('//*[@id="content"]/div/div[1]/ol/li/div/div[2]/div[2]/p[1]/text()')
#导演
info1= info[0] .replace(" ", "").replace("\n", "").split()[0]  
#主演
info2= info[0] .replace(" ", "").replace("\n", "").split()[1]  
#国家
info3= info[1].replace(" ", "").replace("\n", "").split('/')[1] 
#评分
rank=s.xpath('//*[@id="content"]/div/div[1]/ol/li/div/div[1]/em/text()')
#名次
scores=s.xpath('//*[@id="content"]/div/div[1]/ol/li/div/div[2]/div[2]/div/span[2]/text()')
for i in range(25):
    info_1=s.xpath('//*[@id="content"]/div/div[1]/ol/li/div/div[2]/div[2]/p[1]/text()')[i*2]
    info_2=s.xpath('//*[@id="content"]/div/div[1]/ol/li/div/div[2]/div[2]/p[1]/text()')[i*2+1]
    #导演
    info1= info_1 .replace(" ", "").replace("\n", "").split()[0]  
    #主演
    info2= info_1 .replace(" ", "").replace("\n", "").split()[1]  
    #国家
    info3= info_2.replace(" ", "").replace("\n", "").split('/')[1]
    print("{}   {}   {}  {}  {} ".format(title[i],score[i],rank[i],info1,info2,info3))

这里我提一句,上面的info=s.xpath('//*[@id="content"]/div/div[1]/ol/li[2]/div/div[2]/div[2]/p[1]/text()')这种语句出来的Info会有两个值,第一个是导演等信息,第二个是国家等信息,所以我这段话的info分奇数和偶数处理。

结果如下:

 

3.5余下页数信息汇总

上面的我只把第一页的影片信息汇总了,接下来我们看看怎么换页。比较一下不同页的URL:

第一页:https://movie.douban.com/top250?start=0
第二页:https://movie.douban.com/top250?start=25
第三页:https://movie.douban.com/top250?start=50
第四页:https://movie.douban.com/top250?start=75
......

代码:

from lxml import etree
import requests
for page in range(10):
    url = 'https://movie.douban.com/top250?start={}&filter='.format(page*25) 
    data=requests.get(url).text
    s=etree.HTML(data)
    #影片名
    title=s.xpath('//*[@id="content"]/div/div[1]/ol/li/div/div[2]/div[1]/a/span[1]/text()')
    #
    info=s.xpath('//*[@id="content"]/div/div[1]/ol/li/div/div[2]/div[2]/p[1]/text()')
    #导演
    info1= info[0] .replace(" ", "").replace("\n", "").split()[0]  
    #主演
    info2= info[0] .replace(" ", "").replace("\n", "").split()[1]  
    #国家
    info3= info[1].replace(" ", "").replace("\n", "").split('/')[1] 
    #评分
    rank=s.xpath('//*[@id="content"]/div/div[1]/ol/li/div/div[1]/em/text()')
    #名次
    scores=s.xpath('//*[@id="content"]/div/div[1]/ol/li/div/div[2]/div[2]/div/span[2]/text()')
    for i in range(25):
        info_1=s.xpath('//*[@id="content"]/div/div[1]/ol/li/div/div[2]/div[2]/p[1]/text()')[i*2]
        info_2=s.xpath('//*[@id="content"]/div/div[1]/ol/li/div/div[2]/div[2]/p[1]/text()')[i*2+1]
        #导演
        info1= info_1 .replace(" ", "").replace("\n", "").split()[0]  
        #主演
        info2= info_1 .replace(" ", "").replace("\n", "").split()[1]  
        #国家
        info3= info_2.replace(" ", "").replace("\n", "").split('/')[1]
        print("{}   {}   {}  {}  {} {} ".format(title[i],scores[i],rank[i],info1,info2,info3))

这样运行出现了BUG:

原因是第214部影片没有演员信息,所以list Out of range

为了解决这个问题我加了一个判定语句,全部代码如下:

from lxml import etree
import requests
for page in range(10):
    url = 'https://movie.douban.com/top250?start={}&filter='.format(page*25) 
    data=requests.get(url).text
    s=etree.HTML(data)
    #影片名
    title=s.xpath('//*[@id="content"]/div/div[1]/ol/li/div/div[2]/div[1]/a/span[1]/text()')
    #
    info=s.xpath('//*[@id="content"]/div/div[1]/ol/li/div/div[2]/div[2]/p[1]/text()')
    #导演
    info1= info[0] .replace(" ", "").replace("\n", "").split()[0]  
    #主演
    info2= info[0] .replace(" ", "").replace("\n", "").split()[1]  
    #国家
    info3= info[1].replace(" ", "").replace("\n", "").split('/')[1] 
    #评分
    rank=s.xpath('//*[@id="content"]/div/div[1]/ol/li/div/div[1]/em/text()')
    #名次
    scores=s.xpath('//*[@id="content"]/div/div[1]/ol/li/div/div[2]/div[2]/div/span[2]/text()')
    for i in range(25):
        info_1=s.xpath('//*[@id="content"]/div/div[1]/ol/li/div/div[2]/div[2]/p[1]/text()')[i*2]
        info_2=s.xpath('//*[@id="content"]/div/div[1]/ol/li/div/div[2]/div[2]/p[1]/text()')[i*2+1]
        if(len(info_1 .replace(" ", "").replace("\n", "").split())==2):
            #导演
            info1= info_1 .replace(" ", "").replace("\n", "").split()[0]  
            #主演
            info2= info_1 .replace(" ", "").replace("\n", "").split()[1]  
            #国家
            info3= info_2.replace(" ", "").replace("\n", "").split('/')[1]
            print("{}   {}   {}  {}  {} {} ".format(title[i],scores[i],rank[i],info1,info2,info3))
        else:
             #导演
            info1= info_1 .replace(" ", "").replace("\n", "").split()[0]  

            #国家
            info3= info_2.replace(" ", "").replace("\n", "").split('/')[1]
            print("{}   {}   {}   {}  {}".format(title[i],scores[i],rank[i],info1,info3))

现在程序完美运行~部分结果截图如下:

看到排名我还有点疑惑,为什么排第215的分数有9.2,216的分数有8.8,我还以为是我这里出了问题,去网站看了一下也是这样,难道不是按照分数排的吗...

 

3.5改进

这里我们默认电影名字以及评分都是完整的、正确的信息,这种默认一般情况下是没问题的。但其实是有缺陷的。如果我们少爬了或者多爬了信息,就会发生匹配的错误,那么该怎么避免这种错误呢? 

仔细思考下,发现我们若是以电影名字为单位,分别获取对应的信息,那么匹配肯定完全正确。 电影名字的标签肯定在这部电影的框架内,于是我们从电影名字的标签往上找,发现覆盖整部电影的标签,把xPath信息复制下来

//*[@id="content"]/div/div[1]/ol/li[1

全部代码如下:

from lxml import etree
import requests
for page in range(10):
    url = 'https://movie.douban.com/top250?start={}&filter='.format(page*25) 
    data=requests.get(url).text
    s=etree.HTML(data)
    file = s.xpath('//*[@id="content"]/div/div[1]/ol/li')
    for div in file:
    #影片名
        title=div.xpath('./div/div[2]/div[1]/a/span[1]/text()')
        #
        info=div.xpath('./div/div[2]/div[2]/p[1]/text()')
        #评分
        rank=div.xpath('./div/div[1]/em/text()')
        #名次
        scores=div.xpath('./div/div[2]/div[2]/div/span[2]/text()')

        if(info[0] .replace(" ", "").replace("\n", "").split()==2):
            #导演
            info1= info[0].replace(" ", "").replace("\n", "").split()[0]  
            #主演
            info2= info[0] .replace(" ", "").replace("\n", "").split()[1]  
            #国家
            info3= info[1].replace(" ", "").replace("\n", "").split('/')[1]
            print("{}   {}   {}  {}  {} {} ".format(title[0],scores[0],rank[0],info1,info2,info3))
        else:
             #导演
            info1= info_1 .replace(" ", "").replace("\n", "").split()[0]  

            #国家
            info3= info_2.replace(" ", "").replace("\n", "").split('/')[1]
            print("{}   {}   {}   {}  {}".format(title[i],scores[i],rank[i],info1,info3))

完美运行~

 

4.正则表达式爬取豆瓣top250

上面我阴差阳错学习了xpath的方法,实际上我本来打算用正则表达式完成以上任务,现在补起来。

4.1 获取网站响应内容

import re, requests
url = "https://movie.douban.com/top250?"
movie_NO = movie_name = movie_count = movie_director = []  # 初始化数据,保证名称循环都是空的
req = requests.get(url)
# print(req.content.decode("utf-8"))  # 打印接口请求到的信息
html_reponse = req.content.decode("utf-8")
print(html_reponse)  

部分截图如下:

 

4.2 从响应内容里正则化提取所需内容

(1) re.findall()

首先我们来看看这个函数的用法:

findall(pattern, string, flags=0)

返回string中所有与pattern相匹配的全部字串,返回形式为数组

(2)提取名次

我们看名次在这里:

所以本页用这个语句查询:

movie_NO = re.findall('<em class="">(.*?)</em>', html_reponse)  # 名次

结果如下:

(3)影片名

如果我们这样写:

movie_title=re.findall('<span class="title">(.*?)</span>', html_reponse)

那结果如图:

原因是

每个影片有几个题目,但是我们只要第一个就行。

movie_title = re.findall(
        '[a-zA-Z]+://[^\s]*/" class="">\n                            <span class="title">(.*?)</span>',
        html_reponse)  # 影片名称# 
print(movie_title)

(3)导演

movie_director = re.findall('导演: (.*?)[&nbsp;|<br>]', html_reponse)

(4)国家

movie_count = re.findall('&nbsp;/&nbsp;(.*?)&nbsp;/&nbsp;', html_reponse)  # 国家

(5)全部代码:

import re, requests

for i in range(10):  # 一共有10页数据
    url = "https://movie.douban.com/top250?start=" + str(i * 25) + "&filter="

    movie_NO = movie_name = movie_count = movie_director = []  # 初始化数据,保证名称循环都是空的
    req = requests.get(url)
    # print(req.content.decode("utf-8"))  # 打印接口请求到的信息
    html_reponse = req.content.decode("utf-8")
    
    movie_NO = re.findall('<em class="">(.*?)</em>', html_reponse)  # 名次
    # print(movie_NO)
    # print(len(movie_NO))

    movie_name = re.findall(
        '[a-zA-z]+://[^\s]*/" class="">\n                            <span class="title">(.*?)</span>',
        html_reponse)  # 影片名称
    # print(movie_name)
    # print(len(movie_name))

    movie_count = re.findall('&nbsp;/&nbsp;(.*?)&nbsp;/&nbsp;', html_reponse)  # 国家
    # print(movie_count)
    # print(len(movie_count))

    movie_director = re.findall('导演: (.*?)[&nbsp;|<br>]', html_reponse)  # 导演  (这里用竖杆把两种可能的都匹配到了,在此坑躺了两个小时!!)
    # print(movie_director)
    # print(len(movie_director))

    for j in range(25):
        #  电影的信息拼接起来
        movie_info = "排名:" + movie_NO[j] + "  电影名:" + movie_name[j] + "  国家:" + movie_count[j] + "  导演:" + \
                     movie_director[j]
        print(movie_info)

结果:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值