使用 scrapy 爬取豆瓣电影 Top250

最近在学习使用 python 做数据分析,正好今天学了下 scrapy ,来写个爬虫练练手吧。因为是第一次写爬虫,先来个简单点的,那就爬豆瓣电影 Top250 吧。

使用 scrapy 爬取豆瓣电影 Top250

最近在学习使用 python 做数据分析,正好今天学了下 scrapy ,来写个爬虫练练手吧。因为是第一次写爬虫,先来个简单点的,那就爬豆瓣电影 Top250 吧。

因为这次我是在 windows 上写的,之前由于 window 的开发环境配置实在是不省心,各种问题搞得很烦躁。最近试了下 Anconda 这个 python 的科学计算环境发行包,它集成了很多常用的科学计算 package,省去了很多配置环境变量的步骤,能做到开箱即用。而且自带了非常简单易用的虚拟环境,python 版本之间可以随意切换,互不干扰。

环境配置

首先使用 conda 新建一个环境(基于 python3):

conda create -n spider python=3

激活 spider 这个环境:

activate spider

接着安装相关依赖:

pip install scrapy beautifulsoup peewee
  • scrapy 是我们写爬虫需要用到的框架;
  • beautifulsoup 是解析 html 用到的框架;
  • peewee 是一款数据库 ORM 库,可以很方便的帮你把对象和数据库表进行映射,从而让你不用写一句 SQL 就能操作数据库,我们爬到的数据需要写到 mysql 数据库中。

然后使用 scrapy 创建爬虫项目:

scrapy startproject douban

定制爬虫

首先我们先建一个 MySQL数据库 douban,设置好用户名和密码。

接着我们需要定义一个想要爬取的数据 Item,它继承了 scrapy.Item 这个类,包含了 name(电影名称)、url(详情链接)、score(电影评分)这三个数据。为了能将这些数据写入到 MySQL 数据库,我们还使用 peewee 定义了一个 Movie 类,继承于 Model 类(该类属于 peewee),并定义了相关的属性。

import scrapy
from peewee import *

db = MySQLDatabase("douban", host='localhost', user="root", passwd="123456", charset="utf8")

class DoubanMovieItem(scrapy.Item):
    name = scrapy.Field()
    url = scrapy.Field()
    score = scrapy.Field()

class Movie(Model):
    id = PrimaryKeyField()
    name = CharField(verbose_name="电影名称", max_length=50, null=False, unique=False)
    url = CharField(verbose_name="详情链接", null=False)
    score = FloatField(verbose_name="评分", null=False, unique=False)

    class Meta:
        database = db

数据 Model 定义好了之后,我们需要在 spiders 这个目录下自定义一个 spider,这个 spider 需要继承 Spider 这个类。在该类中,我们需要设置 spider 的 name,以及 header(添加 header 能够模拟正常的请求,不加 header 很容易造成请求失败),在 start_requests 方法中定义开始的 url,并返回这个请求。

parse 这个方法中,我们使用 BeautifulSoup 来解析获取到的 response 对象,具体参数需要去豆瓣电影的 html 源码中去找。

import scrapy
from scrapy import Spider
from spider.items import DoubanMovieItem
from bs4 import BeautifulSoup

class DoubanTopMovieSpider(Spider):
    name = 'DoubanTopMovie'
    headers = {
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.110 Safari/537.36'
    }

    def start_requests(self):
        url = 'https://movie.douban.com/top250'
        yield scrapy.Request(url, headers=self.headers)

    def parse(self, response):
        item = DoubanMovieItem()

        soup = BeautifulSoup(response.text, 'lxml')
        movies = soup.findAll('div', {"class": 'item'})
        for movie in movies:
            item['name'] = movie.find('span', {'class': 'title'}).get_text()
            item['url'] = movie.find('a')['href']
            item['score'] = movie.find('span', {'class': 'rating_num'}).get_text()

            yield item

        next_url = soup.find('span', {'class': 'next'}).find('a')['href']
        if next_url:
            url = 'https://movie.douban.com/top250' + next_url
            yield scrapy.Request(url, headers=self.headers)

末尾,我们需要获取 next_url,继续进行下一页的爬取操作。

存储到 MySQL

爬到数据之后,我们需要在 pipelines 这个 module 中将数据保存到数据库,因为我们使用的是 orm,可以看到我们没有写任何 SQL 语句,就能很方便的把数据写到数据库中。

from spider.items import Movie

class SpiderPipeline(object):
    def process_item(self, item, spider):

        if Movie.table_exists() == False:
            Movie.create_table()

        mv = Movie(name=item['name'], url=item['url'], score=item['score'])
        mv.save()

        return item

最后上一张爬取到的数据截图: