Python爬虫之爬豆瓣电影数据

0x000 前言

最近和公司的IOS谈起,以前我们是做移动端,现在已经和前端混为一谈了。 也是 , 做了一年多的Android ,一直都是在写界面展示数据,写交互 ,存储简单的数据 ,最主要的业务数据与业务逻辑都在服务器端,这让我有种离了服务端,我们只能玩玩单机和联机了 。作为曾经的web开发,深知服务端的重要性,我们要想做一款好的产品,没有服务端是万万不能的 , 而我们做移动端的更是要知晓一些服务端的知识 。

我有一个梦想,做一款属于自己的产品 ,但苦于没有足够的数据,于是就开始了我的Python爬虫之旅 。(如果有时间,我会写如何从零收集数据-->服务器端的建立 --> 客户端的编写 , 一整套教程)

0x001 Python基础

Python是一门很简洁的语言 , 因为市面上介绍Python语法的教程很多 , 大多数都很好 ,我这里就不啰嗦了 , 下面贴出我看过的网站:

廖雪峰 - Python教程
简明Python教程

0x002 网络爬虫

网络爬虫是一个自动提取网页的程序,它为搜索引擎从万维网上下载网页,是搜索引擎的重要组成。传统爬虫从一个或若干初始网页的URL开始,获得初始网页上的URL,在抓取网页的过程中,不断从当前页面上抽取新的URL放入队列,直到满足系统的一定停止条件。[来自百度]

我们通过URL让爬虫来爬取我们想要的内容 , 根据我们的规则进行深入爬取 , 比如说 , 我们想要爬取正在上映的电影数据 , 并下载电影海报,其他的内容我们忽略掉,那么我们就需要根据网页内容的规则来制订,哪些内容是我们需要的,哪些是我们不需要的。

0x003 爬取豆瓣电影数据

第一步 : 打开豆瓣电影 , 分析网页结构

我们爬取的网址是 https://movie.douban.com/cinema/nowplaying/shenzhen/ ,里面有一个的模块叫正在上映 , 我们就解析这个模块,其他的内容我们忽略掉。chrome(firefox)按f12查看网页结构。

网页结构

我们可以用元素选择工具,选中我们要分析的数据,firebug控制台就会将选中html结构展示出来。我们通过分析 , 我们想要的数据是在一个ul标签里面,每一个li标签就是一部电影的数据,我们只需要取出里面的li中的数据即可。豆瓣的前台使用了数据绑定的技术 ,这为我们获取数据,方便了不少 ,我们直接取出li中的属性就可以将电影信息获取到了 。

第二步 : 下载网页内容

我们需要将网页中的数据提取出去 , 就需要先将网页内容下载下来,写Java程序的朋友应该知道 , 如果使用Java内置网络请求,将网页数据下载下来,那将要写多少行代码,实话说,Java的网络请求真的很不友好,幸而Java社区异常强大,涌现了一批又一批比较好用的网络请求框架,如OkHttp,liteHttp等等,大大简化了我们的工作。但是,在Python中,将一个网页数据下载下来,只需要一行 。

with request.urlopen('https://movie.douban.com/cinema/nowplaying/shenzhen/') as response:

这是Python内置的urllib下的requests对象,将下载好的二进制数据封装在response对象里面,使用readlines方法就可以将其读取出来 。

from urllib import request
from urllib import parse

# 打开链接,并得到返回值
with request.urlopen('https://movie.douban.com/cinema/nowplaying/shenzhen/') as response:
      conent_list = response.readlines() # 得到一个byte类型的list
      #打印内容
      for content in conent_list:
          print(content .decode()) # 因为content是byte类型,所以需要解码成str类型

虽然内置urlib可以完成我们的需求,但是我们不会用系统内置,因为还是比较麻烦,我们将使用富有盛名的requets库,虽然名字和系统内置的差不多,但是确实截然不同的库 。系统库是在urllib模块下,而requests就在requests模块下 。

安装requests

pip install requests

example

# url 请求地址
# headers 请求头
url = 'https://movie.douban.com/cinema/nowplaying/shenzhen/'
res = requests.get(url,headers=headers)
res.text() # 得到请求的文本内容

好戏,马上开场

第三步 : 解析网页内容

解析是使用的Python内置的Html解析器,类似Java的jsoup.jar提供的api 。都是通过遍历Html dom树来进行分析,判断需要的tag ,然后进行属性解析。Python正在强大的Html解析器是,XPath解析,也是scrapy爬虫库内置的解析器 ,当然还是有beautifulsoup 。

1.引入HTMLPaser

from html.parser import HTMLParser

2.新建解析类,继承HTMLPaser

class MoviesParser(HTMLParser):

3.overload handle_starttag方法 , 解析标签

class MoviesParser(HTMLParser):
    def __init__(self):
        HTMLParser.__init__(self)
        # 电影列表list集合
        self.movies = []
        # 标签是否在li标签中的标识
        self.img_into_movie = False
        self.a_into_movie = False

    def handle_starttag(self, tag, attrs):
        # 因为属性都是key=val形式 , 所以根据属性名称取值
        def _attr(attrs,attr_name):
            for attr in attrs:
                if attr[0] == attr_name:
                    return attr[1]
            return None

        # 取出属性值 , 根据每个Item的特征取值 ,因为有些Item的属性可能会重复 , 所以要尽量找出差异性,这样才能保证数据的准确性
        if tag == 'li' and _attr(attrs,'data-title') and _attr(attrs,'data-category') == 'nowplaying':
            movie = {}
            movie['title'] = _attr(attrs,'data-title')
            movie['score'] = _attr(attrs,'data-score')
            movie['star'] = _attr(attrs,'data-star')
            movie['duration'] = _attr(attrs,'data-duration')
            movie['region'] = _attr(attrs,'data-region')
            movie['director'] = _attr(attrs,'data-director')
            movie['actors'] = _attr(attrs,'data-actors')
            self.movies.append(movie)
            self.img_into_movie = True
            self.a_into_movie = True

        #获取海报图片
        if tag == 'img' and self.img_into_movie:
            self.img_into_movie = False
            img_src = _attr(attrs,'src')
            movie = self.movies[len(self.movies) -1]
            movie['poster_url'] = img_src
            # 下载图片
            donwload_poster_url(img_src)
        
        # 解析a标签,提取电影详情页的Url
        if tag == 'a' and self.a_into_movie:
            if _attr(attrs,'data-psource') == 'title':
                self.a_into_movie = False
                movie_url = _attr(attrs,'href')
                movie = self.movies[len(self.movies) -1]
                movie['movie_url'] = movie_url

这是爬虫中最关键的部分,数据解析是保证数据正确的性的地方,解析没做好,就可能存在很多脏数据,这是我们应当避免的 。

第四步 : 下载图片

# 下载图片
def donwload_poster_url(url):
    res = requests.get(url)
    file_name = str.split(url,'/')[-1]
    file_path = 'poster_img/' + file_name
    print('download img file_path = ',file_path)
    with open(file_path,'wb') as f:
        f.write(res.content)

我们直接使用requests库,get图片地址,得到图片的二进制数据,再见二进制数据写入到文件中,这样我们的图片文件就下载好了。

完整源码

# 使用requests爬豆瓣正在上映的电影
from html.parser import HTMLParser
import requests


class MoviesParser(HTMLParser):
    def __init__(self):
        HTMLParser.__init__(self)
        self.movies = []
        self.img_into_movie = False
        self.a_into_movie = False

    def handle_starttag(self, tag, attrs):
        # 根据属性名称取值
        def _attr(attrs,attr_name):
            for attr in attrs:
                if attr[0] == attr_name:
                    return attr[1]
            return None
        # 取出属性值
        if tag == 'li' and _attr(attrs,'data-title') and _attr(attrs,'data-category') == 'nowplaying':
            movie = {}
            movie['title'] = _attr(attrs,'data-title')
            movie['score'] = _attr(attrs,'data-score')
            movie['star'] = _attr(attrs,'data-star')
            movie['duration'] = _attr(attrs,'data-duration')
            movie['region'] = _attr(attrs,'data-region')
            movie['director'] = _attr(attrs,'data-director')
            movie['actors'] = _attr(attrs,'data-actors')
            self.movies.append(movie)
            self.img_into_movie = True
            self.a_into_movie = True

        #获取海报图片
        if tag == 'img' and self.img_into_movie:
            self.img_into_movie = False
            img_src = _attr(attrs,'src')
            movie = self.movies[len(self.movies) -1]
            movie['poster_url'] = img_src
            donwload_poster_url(img_src)

        if tag == 'a' and self.a_into_movie:
            if _attr(attrs,'data-psource') == 'title':
                self.a_into_movie = False
                movie_url = _attr(attrs,'href')
                movie = self.movies[len(self.movies) -1]
                movie['movie_url'] = movie_url


# 下载图片
def donwload_poster_url(url):
    res = requests.get(url)
    file_name = str.split(url,'/')[-1]
    file_path = 'poster_img/' + file_name
    print('download img file_path = ',file_path)
    with open(file_path,'wb') as f:
        f.write(res.content)


def douban_movies(url):
    #首先构建请求头 ,模拟浏览器请求头
    headers = {'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36'}
    # # 打开链接,并得到返回值
    res = requests.get(url,headers=headers)
    # 创建Html解析器
    moviesParser = MoviesParser()
    # 解析Html数据
    moviesParser.feed(res.text)
    return moviesParser.movies

if __name__ == '__main__':
    url_str = 'https://movie.douban.com/cinema/nowplaying/shenzhen/'
    movies_res = douban_movies(url_str)

    import json
    json_str = json.dumps(movies_res,sort_keys=True,indent=4,separators=(',',': '))
    # 打印json数据
    print(json_str)

从下载数据到解析Html,只用了七十多行 ,包含了注释和空格,真是人生苦短,我用Python

0x004 结语

Python确实是一个比较简洁的语言,学起来也相对比较轻松,各种库应有尽有,可以作为获取数据的不二选择 。其实,解析Html 还可以简化,使用Xpath更加简洁,几行代码就可以搞定,在后续的文章中,我会逐一介绍 。

有时候感觉文字的表现力真是太弱了,特别是对于技术文章,因为涉及的多,很多时候写着写着就啰嗦了,然后删掉 。 俗话说:子不如表 , 表不如图,图不如视频 。最近关注了一个简书的作者,他做了一系列的视频,讲得挺好的,我思考着,要不要出个爬虫的视频教程 。

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

推荐阅读更多精彩内容