使用 requests 库爬取豆瓣 Top250

有时候剧荒的时候,就会去豆瓣 Top250 看看有什么好看的电影,今天就用 requests 库实现一个获取取豆瓣 Top250 电影的爬虫。爬取的信息包括电影名,电影使用的封面,电影的导演主演,电影上映日期国家电影类型以及电影中的经典语录。这些都是判断是否会观看这部影片的重要因素。

根据编写爬虫的一般步骤:获取页面,解析页面,信息存储。我们编写三个函数 get_page()parse_page()store_info() 分别实现这三个模块的功能。

首先是页面获取的 get_page() 函数

 def get_page(url,headers):
    response = r.get(url=url, headers=headers) # 在请求中加入请求头 headers
    print(type(response.status_code)) 
    if response.status_code == 200: # 如果请求返回的状态码是 200
        return response.text

对这个模块进行测试,得到了网页的源代码

 <ol class="grid_view">
        <li>
            <div class="item">
                <div class="pic">
                    <em class="">1</em>
                    <a href="https://movie.douban.com/subject/1292052/">
                        <img width="100" alt="肖申克的救赎" 
 src="https://img3.doubanio.com/view/photo/s_ratio_poster/public/p480747492.jpg" class="">
                    </a>
                </div>
                <div class="info">
                    <div class="hd">
                        <a href="https://movie.douban.com/subject/1292052/" class="">
                            <span class="title">肖申克的救赎</span>
                                    <span class="title">&nbsp;/&nbsp;The Shawshank 
 Redemption</span>
                                <span class="other">&nbsp;/&nbsp;月黑高飞(港)  /  刺激1995(台)
 </span>
                        </a>

                            <span class="playable">[可播放]</span>
                    </div>
                    <div class="bd">
                        <p class="">
                            导演: 弗兰克·德拉邦特 Frank Darabont&nbsp;&nbsp;&nbsp;主演: 蒂姆·罗宾斯 
 Tim Robbins /...<br>
                             1994&nbsp;/&nbsp;美国&nbsp;/&nbsp;犯罪 剧情
                        </p>


                        <div class="star">
                                <span class="rating5-t"></span>
                                <span class="rating_num" property="v:average">9.6</span>
                                <span property="v:best" content="10.0"></span>
                                <span>1455845人评价</span>
                        </div>

                            <p class="quote">
                                <span class="inq">希望让人自由。</span>
                            </p>
                    </div>
                </div>
            </div>
        </li>
        <li>......</li>
        <li>......</li>
 </ol>

可以看到电影信息保存在 <ol>标签 有序列表中。每部电影放在 <li>标签 中。

接着使用 parse_page() 函数对获得的页面进行解析。

 def parse_page(html):
    expression = '<li.*?<img.*?alt="(.*?)"\ssrc="(.*?)".*?导演:(.*?);主演:(.*?)<br>(.*?)
 </p>.*?"v:average">(.*?)</span>.*?<span>(.*?)</span>.*?class="inq">(.*?)</span>'
    r = re.compile(expression,re.S) # 将正则表达式字符串编译成正则表达式对象,方便在以后的匹配中复用,编译
 时加入修饰符 re.S 使正则表达式中的 . 可以匹配包括换行符的所有字符,否则不能匹配到换行符。
    items = re.findall(r,html) # 在 html 字符串中找到所有的符合正则表达式的字符串
    print(len(items))
    return items

最后得到的结果为

 [('肖申克的救赎', 'https://img3.doubanio.com/view/photo/s_ratio_poster/public/p480747492.jpg', ' 弗
 兰克·德拉邦特 Frank Darabont&nbsp;&nbsp;&nbsp', ' 蒂姆·罗宾斯 Tim Robbins /...', '\n                            
 1994&nbsp;/&nbsp;美国&nbsp;/&nbsp;犯罪 剧情\n                        ', '9.6', '1455845人评价', '希
 望让人自由。'), ('霸王别姬', 
 'https://img3.doubanio.com/view/photo/s_ratio_poster/public/p1910813120.jpg', ' 陈凯歌 Kaige 
 Chen&nbsp;&nbsp;&nbsp', ' 张国荣 Leslie Cheung / 张丰毅 Fengyi Zha...', '\n                            
 1993&nbsp;/&nbsp;中国大陆 香港&nbsp;/&nbsp;剧情 爱情 同性\n                        ', '9.6', 
 '1078972人评价', '风华绝代。'),......,]

可以看到匹配的结果已经正常返回,这其中最重要的就是正则表达式的书写,关于正则表达式还会专门进行学习。返回结果是一个列表,我们只要迭代这个列表就可以得到每部电影的信息,然后将每部电影存入文件就可以了。但是发现其中有许多多余的字符串比如 &nbsp 等等,这就需要对这些字符串进行处理,得到我们想要的简洁清晰的格式。

我们在 parse_page() 中使用 yield 在函数中将电影信息以字典的形式保存电影信息,使这个函数变成一个生成式,然后我们迭代这个生成式对象,就可以得到字典形式的电影信息。在保存信息的时候,对电影信息的字符串进行处理。

改进后的 parse_page() 为:

 def parse_page(html):
    expression = '<li.*?<img.*?alt="(.*?)"\ssrc="(.*?)".*?导演:(.*?)&nbsp;.*?主演:(.*?)<br>
 (.*?)&nbsp;/&nbsp;(.*?)&nbsp;/&nbsp;(.*?)</p>.*?"v:average">(.*?)</span>.*?<span>(.*?)</span>.*?
 class="inq">(.*?)</span>'

    r = re.compile(expression,re.S)
    items = re.findall(r,html)
    print('==========================================================')
    print(len(items))
    for item in items:
        yield {
            '电影名':item[0],
            '电影封面':item[1],
            '导演':item[2].strip().split(";")[0],
            '主演':item[3].strip(),
            '年代':item[4].strip(),
            '国家':item[5].strip(),
            '类型':item[6].strip(),
            '评分':item[7],
            '评价人数':item[8],
            '经典语录':item[9]
        }

改进后的输出迭代输出结果为

 {'电影名': '肖申克的救赎', '电影封面': 
 'https://img3.doubanio.com/view/photo/s_ratio_poster/public/p480747492.jpg', '导演': '弗兰克·德拉邦
 特 Frank Darabont&nbsp', '主演': '蒂姆·罗宾斯 Tim Robbins /...', '年代': '1994', '国家': '美国', '类
 型': '犯罪 剧情', '评分': '9.6', '评价人数': '1455845人评价', '经典语录': '希望让人自由。'}
 {'电影名': '霸王别姬', '电影封面': 
 'https://img3.doubanio.com/view/photo/s_ratio_poster/public/p1910813120.jpg', '导演': '陈凯歌 
  Kaige Chen&nbsp', '主演': '张国荣 Leslie Cheung / 张丰毅 Fengyi Zha...', '年代': '1993', '国家': 
 '中国大陆 香港', '类型': '剧情 爱情 同性', '评分': '9.6', '评价人数': '1078972人评价', '经典语录': '风华
 绝代。'}
 ...

最后 store_info() 函数将爬取的信息保存起来

 def store_info(file_path,info):
    with open(file_path,'a+',encoding='utf-8') as f:
        f.write(str(info)) # 写入的要是 str 类型

这只是爬取了豆瓣 Top250 中的第 1 页信息,翻页后观察第 2 页,第 3 页的链接:https://movie.douban.com/top250?start=0&filter= https://movie.douban.com/top250?start=25&filter=https://movie.douban.com/top250?start=50&filter= 可以发现每次请求的 url 只是 start 的数字变了,而且是每次增加 25 ,因此,如果要爬10页数据的话,start 是从 0 到 225,每次增量为 25。从而我们可以在 main 函数中对 start 进行变化构造不同页面的 url。

 def main():
    urls = []
    for index in range(0, 226, 25):
        url = 'https://movie.douban.com/top250?start={index}&filter='.format(index=index)
        urls.append(url)
    return urls

这样就得到了 10 页的请求 url 的集合列表 urls。

最后将所有的程序整合一下就是:

 import requests as r
 import re

 def get_page(url,headers):
    response = r.get(url=url, headers=headers)
    if response.status_code == 200:
        return response.text

 def parse_page(html):
    expression = '<li.*?<img.*?alt="(.*?)"\ssrc="(.*?)".*?导演:(.*?)&nbsp;.*?主演:(.*?)<br>
 (.*?)&nbsp;/&nbsp;(.*?)&nbsp;/&nbsp;(.*?)</p>.*?"v:average">(.*?)</span>.*?<span>(.*?)
 </span>.*?
 class="inq">(.*?)</span>'

    r = re.compile(expression,re.S)
    items = re.findall(r,html)
    print('=====================================================================')
    print("有",len(items),"部电影")
    for item in items:
        yield {
            '电影名':item[0],
            '电影封面':item[1],
            '导演':item[2].strip().split(";")[0],
            '主演':item[3].strip(),
            '年代':item[4].strip(),
            '国家':item[5].strip(),
            '类型':item[6].strip(),
            '评分':item[7],
            '评价人数':item[8],
            '经典语录':item[9].strip()
        }

 def store_info(file_path,info):
    with open(file_path,'a+',encoding='utf-8') as f:
        f.write(str(info)) # 写入的要是 str 类型

 def main():
    urls = []
    for index in range(0, 226, 25):
        url = 'https://movie.douban.com/top250?start={index}&filter='.format(index=index)
        urls.append(url)
    return urls

 if __name__ == '__main__':
    headers = {
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like 
 Gecko) Chrome/63.0.3239.132 Safari/537.36'
    }
    path = r'C:\Users\Tang\Desktop\doubanTop250.txt'
    urls = main()
    i = 0
    for url in urls:
        print("正在爬取第 {i} 页...".format(i=i+1))
        html = get_page(url,headers)
        for item in parse_page(html):
            print(item)
            store_info(path,item)
        i += 1

最后的输出结果为:

在桌面上会创建一个文件 doubanTop250.txt ,里面保存了爬取下来的 Top250 的电影信息。

这样就获取了 豆瓣Top250 的电影信息,由于豆瓣的页面可能会更新,因此如果爬取失败的话,需要根据最新的页面重新编写解析规则。





评论

  1. #1

    dUoxKpta 2019-09-06 01:15:02
    dUoxKpta

  2. #2

    WEmXeAbt 2019-09-05 22:19:18
    WEmXeAbt

  3. #3

    zuLdJdBo 2019-09-05 19:41:58
    zuLdJdBo