爬虫实战——豆瓣电影250篇

Python有着丰富的网络库,因此在做数据收集方面有着无可比拟的优势。以上的话是商业吹一波,当不得真(现在很多库都有多语言版本)。

一个爬虫基本上都可以划分为三个部分:数据获取,数据处理,数据持久化。我们会以爬取豆瓣电影250为例子,每一个模块对应一个函数,一步一步来获得我们想要的数据。

数据获取

Python为我们提供了几个常用的模拟get、post请求的库,例如urllib、requests等等。来看个简单的例子

from urllib import request
#构造一个Get请求
with request.urlopen('https://movie.douban.com/top250') as f:
#读取Response中的数据部分
    data = f.read()
#请求状态
    print('Status:', f.status, f.reason)
#获取Response Headers
    for k, v in f.getheaders():
        print('%s: %s' % (k, v))
    print('Data:', data.decode('utf-8'))'

这里获取了豆瓣电影250的数据,包括所有的response headers和data。如果你不熟悉html协议,可以运行一下上面的程序,看一看我们得到的数据是什么样子。这就是最简单的爬虫,我们构造了一个请求,得到了请求对应的资源。怎么样,是不是很轻松。

这里隐藏了一个问题:我们现在的程序都是爬取的是单页面数据,不能够获得完整的数据。我们将在后序解决这个问题。

数据处理

上面的程序还有个问题,data其实是一个html格式的数据,含有我们不需要的标签信息,需要对数据做一些处理,让数据能够聚焦在有价值的信息上。
数据处理有两种方式,一种是正则匹配,有兴趣的同学可以搜索相关信息;还有一种就是我想要介绍的BeautifulSoup文本解析工具,它是一个用于解析Html和Xml的Python库。

def parse_html(html):
    soup = BeautifulSoup(html,"html.parser")
    movie_list_soup = soup.find('ol', {'class': 'grid_view'})
    movie_name_list = []
    for movie_li in movie_list_soup.find_all('li'):
        detail = movie_li.find('div', {'class': 'hd'})
        movie_name = detail.find('span', {'class': 'title'}).getText()
        movie_name_list.append(movie_name)
    return movie_name_list

对比已经获得的html格式的数据,可以很清晰的看到BeautifulSoup如何找到我们想要的数据。

<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>

...

BeautifulSoup首先定位到class属性为grid_view的ol标签,该标签下面每一个li标签内都是一个电影的数据。找到li标签中的数据后,定位class为hd的div标签,最后找到class为title的span标签,并提取其中的内容。

数据持久化

数据处理完后,就可以对数据做持久化操作,本例中我们把数据保存在txt文件中。

def save_data(data):
    with open('topMoive.txt', 'w', encoding='utf8') as movie_list:
        movie_list.write('\n'.join(data))

整合程序

def main():
    url = 'https://movie.douban.com/top250'
    data = download_page(url)
    print(data.decode('utf-8'))
    movie_list = parse_html(data)
    save_data(movie_list)
    
if __name__ == '__main__':
    main()

这里就不多说了,依次调用数据获取,数据解析,数据持久化三个函数就可以。

多页面爬取

这是一个比较复杂的问题,多页面的爬取在算法层面其实相当于图的遍历。如果你没有遗忘的话,图的遍历总会涉及到快速查找某节点是否已经被访问过,这在爬虫系统里面就涉及到url去重的问题。此外,如果url数量实在是太多,为了提升性能,还会涉及到分布式爬虫系统,其实就是准备一个队列,由多个机器去读取里面的url而已。

幸运的是,在"爬取豆瓣电影Top250"这个命题下面,我们无需面对上面的问题。下面开始改造我们的程序吧。

        <span class="next">
            <link rel="next" href="?start=25&amp;filter="/>
            <a href="?start=25&amp;filter=" >后页&gt;</a>
        </span>

上面一段程序,是从html中摘取的片段,我们发现下一页其实已经通过链接给我们了,只要重复获得该链接我们就可以不断获得新的电影信息。于是我们在解析数据环节获取下一页的链接,并返回该链接。

def parse_html(html):
    soup = BeautifulSoup(html,"html.parser")
    movie_list_soup = soup.find('ol', {'class': 'grid_view'})
    movie_name_list = []
    for movie_li in movie_list_soup.find_all('li'):
        detail = movie_li.find('div', {'class': 'hd'})
        movie_name = detail.find('span', {'class': 'title'}).getText()
        movie_name_list.append(movie_name)
    next_page = soup.find('span', {'class': 'next'}).find('a')
    if next_page:
        return movie_name_list, URL_CONST + next_page['href']
    return movie_name_list, None

在main中,我们要启动一个循环,直到返回的下一页链接为空。

URL_CONST = 'https://movie.douban.com/top250'
def main():
    url = URL_CONST
    movie_list = []
    while url:
        data = download_page(url)
        movie_list_tmp, url = parse_html(data)
        print('\n'.join(movie_list_tmp))
        movie_list.extend(movie_list_tmp)
    save_data(movie_list)

至此我们实现了简单的多页面爬虫,完整的程序参见文末。后面一章,我们会讲一下如何绕过登录去获取数据,会涉及到selenium、requests等python库。

from bs4 import BeautifulSoup
from urllib import request


def download_page(url):
    with request.urlopen(url) as f:
        data = f.read()
    return data

URL_CONST = 'https://movie.douban.com/top250'


def main():
    url = URL_CONST
    movie_list = []
    while url:
        data = download_page(url)
        movie_list_tmp, url = parse_html(data)
        print('\n'.join(movie_list_tmp))
        movie_list.extend(movie_list_tmp)
    save_data(movie_list)

def parse_html(html):
    soup = BeautifulSoup(html,"html.parser")
    movie_list_soup = soup.find('ol', {'class': 'grid_view'})
    movie_name_list = []
    for movie_li in movie_list_soup.find_all('li'):
        detail = movie_li.find('div', {'class': 'hd'})
        movie_name = detail.find('span', {'class': 'title'}).getText()
        movie_name_list.append(movie_name)
    next_page = soup.find('span', {'class': 'next'}).find('a')
    if next_page:
        return movie_name_list, URL_CONST + next_page['href']
    return movie_name_list, None

def save_data(data):
    with open('topMoive.txt', 'w', encoding='utf-8') as movie_list:
        movie_list.write('\n'.join(data))

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

推荐阅读更多精彩内容