Python 爬取豆瓣电影TOP250并做数据分析

* 爬取豆瓣电影TOP页面的电影数据 * 根据电影的分类进行数据统计 * 实现可以通过分类标签查找标签下的所有电影

主要内容

  • 爬取豆瓣电影TOP页面的电影数据
  • 根据电影的分类进行数据统计
  • 实现可以通过分类标签查找标签下的所有电影

前提准备


本爬虫中使用了requests库来获取页面信息,然后使用lxml的etree进行文档结构解析并抓取有用信息。获取的数据存储在MySQL中,这里使用了我的mysql类库Esql,数据库的配置信息放在了config.py中。最后使用了numpy以及matplotlib做数据分析。

脚本头部:

#!/usr/bin/env python    
#encoding: utf-8  

import sys    
reload(sys)
sys.setdefaultencoding('utf8')

import requests
from lxml import etree
from Esql.Builder import Builder as MySQL
import numpy as np
import matplotlib.pyplot as plt
from config import configs

脚本主运行代码:

if __name__ == '__main__':
    db = MySQL(configs['db'])

    # 数据存储
    for i in range(10):
        movies = get_page(i+1)
        save_data(db, movies)
    pylot_show(db)

配置文件 config.py

configs = {
    'db': {
	    'host': 'myhostname',
	    'user': 'myuser',
	    'passwd': 'mypassword',
	    'db': 'mydbname'
	},
}

### 爬取数据
def get_page(i):
    url = 'https://movie.douban.com/top250?start='+str(25*(i-1))
    html = requests.get(url).content.decode('utf-8')
    selector = etree.HTML(html)

    movies = []
    #category 
    info = selector.xpath('//div[@class="info"]/div[@class="bd"]/p[1]/text()')
    #movice's name
    name = selector.xpath('//div[@class="info"]/div[@class="hd"]/a/span[@class="title"][1]/text()')
    #movice's href
    href = selector.xpath('//div[@class="info"]/div[@class="hd"]/a/@href')

    for i,j in enumerate(info[1::2]):
        movie = {}
        raw = str(j).strip()
        movie['cate'] = raw.split('/')[-1][2:].split(' ')
        movie['name'] = name[i]
        movie['href'] = href[i]
        movies.append(movie)
    return movies

返回的数据结构为字典数组,其中单个字段元素的包含 cate,name,href 三个键值对,分别代表电影分类,电影名称,电影链接,由于单个电影可包含多个分类,因此 cate 的值为list类型。

存储数据

def save_data(db, data):
    for movie in data:
        db.table('movie').insert({'name': movie['name'], 'href': movie['href']})
        movie_id = db.getInsertId()
        for cate in movie['cate']:
            cate_id = db.table('movie_category').where('name', cate).pluck('id')
            if not cate_id:
                db.table('movie_category').insert({'name': cate})
                cate_id = db.getInsertId()
                
            db.table('movie_category_relation').insert({'movie_id': movie_id, 'cate_id': cate_id})

这里出现了三张数据表 movie,movie_category,movie_category_relation。它们的结构如下:

CREATE TABLE `douban`.`movie` (
`id` INT(11) UNSIGNED NOT NULL AUTO_INCREMENT ,
`name` VARCHAR(30) NOT NULL ,
`href` VARCHAR(255) NOT NULL ,
PRIMARY KEY (`id`))
ENGINE = InnoDB;

CREATE TABLE `douban`.`movie_category` ( 
`id` INT(11) UNSIGNED NOT NULL AUTO_INCREMENT , 
`name` VARCHAR(30) NOT NULL , 
PRIMARY KEY (`id`)) 
ENGINE = InnoDB;

CREATE TABLE `douban`.`movie_category_relation` ( 
`movie_id` INT(11) NOT NULL COMMENT "movie id", 
`cate_id` INT(11) NOT NULL COMMENT "category id", 
PRIMARY KEY ()) 
ENGINE = InnoDB;

### 数据分析
def pylot_show(db):
    cates = db.table('movie_category').get()
    count = []   # 每个分类的数量
    category = []  # 分类

    for cate in cates:
        count.append(db.table('movie_category_relation').where('cate_id', cate['id']).count())
        category.append(cate['name'])

    y_pos = np.arange(len(category))  # 定义y轴坐标数
    plt.barh(y_pos, count, align='center', alpha=0.4)  # alpha图表的填充不透明度(0~1)之间
    plt.yticks(y_pos, category)  # 在y轴上做分类名的标记

    for count, y_pos in zip(count, y_pos):
        # 分类个数在图中显示的位置,就是那些数字在柱状图尾部显示的数字
        plt.text(count, y_pos, count,  horizontalalignment='center', verticalalignment='center', weight='bold')  
    plt.ylim(+28.0, -1.0) # 可视化范围,相当于规定y轴范围
    plt.title(u'豆瓣电影TOP250')   # 图表的标题
    plt.ylabel(u'电影分类')     # 图表y轴的标记
    plt.subplots_adjust(bottom = 0.15) 
    plt.xlabel(u'分类出现次数')  # 图表x轴的标记
    plt.savefig('douban.png')   # 保存图片

最终生成的统计图片:

更多爬虫技巧请戳传送门