scrapy 爬虫初步尝试

前言

可以写爬虫的语言有很多,相关的工具库也有很多,当然,类似于「八爪鱼」傻瓜式的工具也有很多。之前学习 nodejs 的时候用其爬过一个新浪博客,也挺方便,由于 node 最近一段时间用的比较少,大多数东西都忘了。近期由于经常接触数据处理的工作多一些,经常使用 Python,因此,在有爬虫的需求时,第一反应时找一个和Python相关的,查找资料发现,有很多的库像BeautifuSoup都很方便,但由于本人太懒,不想去折腾,就想着找一个完整的框架,能够为我们提供好基础服务,我们只需要实现自己的业务逻辑就可以,而 scrapy 就这样进入了我的视野。

关于 scrapy

Scrapy是一个为了爬取网站数据,提取结构性数据而编写的应用框架。 可以应用在包括数据挖掘,信息处理或存储历史数据等一系列的程序中。

scrapy 基础

scrapy 安装

  • 首先需要 Python 环境支持(Python2.x 或 Python3.x 都可,scrapy 1.x 已经支持Python3),如何安装 Python?, 下载对应系统的安装包手动安装即可,或者通过Conda 等工具包也可以。安装完后检查是否存在环境变量中:在command line(命令行)或者terminal(终端)执行 python -V or python3 -V,结果如下图所示。

  • 安装 scrapy: scrapy 支持 Windows、Linux 及 Mac OS 系统,强烈建议在非 windows平台下操作,当然 windows 也可以,只是坑比较多😭,条件不允许或者喜欢折腾的同学可以大胆尝试(大多数问题在 google 上都可以找到答案)。现在可以开始安装了,在终端中输入 pip install scrapy 或者 pip3 install scrapy, 使用 Conda 的同学需要 conda install -c conda-forge scrapy=1.3.3 , 之后可以祈祷🙏了,可能会有部分依赖包安装会因为网络问题安装失败,查看错误日志找对应的 .whl 编译文件下载安装,幸运的话就可以安装成功了,运行命令scrapy version, 成功结果如下图所示。

常用命令

  • 创建项目:scrapy startproject project_name
  • 创建 spider : scrapy genspider spider_name
  • 运行命令:scrapy crawl SpiderName(此处的SpiderName 为 spider 文件中指定) ,或者执行 scrapy runspider spider_file_name

目录结构

这是 scrapy 项目的目录结构图,主要部分就是图中红色箭头和红色圈中标示处。 

  • spiders 目录:就是放爬虫文件(定义去哪儿爬?怎么爬?怕什么)的地方,如图所示book.pymovie.py 就是用来爬去豆瓣 TOP250 图书和电影的主爬虫文件。
  • items.py: 定义需要爬取字段的文件,例如:想要爬取电影,具体的爬取内容如标题、海报、演员、导演、上映日期等都可以此处定义。
  • pipelines.py: 英文直译过来的意思就是「管道」,在此处也是类似的意思,它主要负责将爬取的内容存储到数据库、文件等,当然也可以在此对内容预处理。
  • settings.py: 主要用于配置爬虫运行时的请求头信息,需要开启的中间件,以及 cookie等。

初步实践

本次实践以豆瓣电影 TOP 250 为例, 爬取电影的图片链接、电影豆瓣链接、电影名称、电影的导演及演员信息、电影的豆瓣评分和电影简短介绍。

创建项目

  • 通过scrapy startproject douban 创建项目,目录结构图如前图所示。
  • 通过 scrapy genspider movie(注:创建爬虫文件时可以指定模板,这里默认就可),此时我们可以看到在 spiders 目录下多出了一个 movie.py 文件,这就是爬虫代码编写的地方。
  • 文件初始内容如下,我们需要将 start_urls 修改为 https://movie.douban.com/top250
1
2
3
4
5
6
7
8
9
10
11
12
# -*- coding: utf-8 -*-
import scrapy
from douban.items import MovieItem


class MovieSpider(scrapy.Spider):
name = "movie"
allowed_domains = ["movie.douban.com"]
start_urls = ['https://movie.douban.com/top250']

def parse(self, response):
pass

页面分析

好了,基础工作做完了,现在可以开始分析页面(html + css)了。 打开浏览器(推荐 chrome,自带开发者工具很好用),输入网址就可以看到页面了,我们发现这个页面有 25部电影,在页面底部是翻页的导航条如下图所示。

 具体到每部电影,会发现用于装饰电影的渲染框架是确定的,只是所包含内容不一样,这样我们就很容易地找到了规则。每一部电影都放置在 <li></li> 包裹的 <div class="item"></div> 中,再具体到详细信息下面再说。

URL 规则解析

我们的目标是爬取 TOP250 电影,但第一页上只有25部,显然不能达到我们的要求,如何爬取所有的内容呢?这就需要通过爬虫自动解析跳转 URL,一页一页的读取了。

通过观察我们发现除了第一页外,其它页面的链接都是这样的:

  • 第二页:https://movie.douban.com/top250?start=25&filter=
  • 第三页: https://movie.douban.com/top250?start=50&filter=

这样我们很容易通过如下代码构造出 url 了(当然,也可以通过解析页面上的 html 代码,此处觉得麻烦直接构造了)。

1
2
for index in range(1, 10):
link = self.start_urls[0] + '?start=' + str(index * 25) + '&filter='

逻辑实现

现在到了具体实现的时候,爬虫运行的时候会默认调用 super 类的 start_request 方法,此方法会默认请求 start_url 里面的地址并默认调用 parse 方法解析。此时只是让爬虫页面跳转运转起来了,我们还对具体内容需要指定具体的解析方法,这里命名为 parse_next 。callback 就是为 Request 方法获取的页面指定具体解析方法。

1
2
3
4
5
6
7
def parse(self, response):
# 请求第一页
yield scrapy.Request(response.url, callback=self.parse_next)
# 请求其他页
for index in range(1, 10):
link = self.start_urls[0] + '?start=' + str(index * 25
yield scrapy.Request(link, callback=self.parse_next)

Request 方法返回的结果其实就是具体的 html 页面,现在就可以从中抽取我们需要的信息了,通过观察页面源码,我们发现包裹电影信息的<div class="item"></div> 都是被同样的类修饰—-「item」,这样我们就可以整个页面上所有的包裹电影信息的div了,如下图所示。

 以此类推,就可以找到电影的相关信息了。

  • 图片链接:

  • 豆瓣链接:

  • 电影名称:

  • 详细信息:

  • 豆瓣评分:

  • 简介:

 此时,我们需要的信息都找到了,这里我们使用 xpath 来解析,详细见代码。(此处代码需要重构)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
def parse_next(self, response):
for item in response.xpath('//div[@class="item"]'):
movie = MovieItem()
movie['img'] = item.xpath('div[@class="pic"]/a/img/@src').extract()[0]
movie['link'] = item.xpath('div[@class="pic"]/a/@href').extract()[0]
name = item.xpath('div[@class="info"]/div[@class="hd"]/a/span[1]/text()').extract()
if len(name) == 0:
movie['name'] = ''
else:
movie['name'] = name[0]
info = item.xpath('div[@class="info"]/div[@class="bd"]/p[1]/text()').extract()
if len(info) == 0:
movie['info'] = ''
else:
movie['info'] = info[0].replace("\n", '').strip()
ratings = item.xpath('div[@class="info"]/div[@class="bd"]/div/span[2]/text()').extract()
if len(ratings) == 0:
movie['ratings'] = ''
else:
movie['ratings'] = ratings[0]
quote = item.xpath('div[@class="info"]/div[@class="bd"]/p[2]/span/text()').extract()
if len(quote) == 0:
movie['quote'] = ''
else:
movie['quote'] = quote[0]
yield movie

xpath:https://scrapy-chs.readthedocs.io/zh_CN/1.0/topics/selectors.html#topics-selectors-relative-xpaths

到这里,我们其实就可以运行爬虫, scrapy crawl movie -o douabn_movie_top250.csv, 这样就可以直接把爬取结果保存到CSV 文件里(scrapy 默认提供 csv 和 json 保存)。

结果保存

上面已经介绍了 csv 和 json 形式的存储,这里特指存储到数据库。本次我们用到的数据库为 MySQL 数据库,所用到的 Python 库为 peewee, peewee 是一个 ORM 的库,简单好用,具体参见文档http://docs.peewee-orm.com/en/latest/

此处在douban 目录下新建一个 models.py 文件,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# -*- coding: utf-8 -*-
from peewee import *

db = MySQLDatabase('douban', host='localhost', port=3306, user='用户名', password='***')

# define base model
class BaseModel(Model):
class Meta:
database = db

class Movie(BaseModel):
id = PrimaryKeyField()
img = CharField()
name = CharField()
link = CharField()
info = TextField()
ratings = CharField()
quote = CharField()


db.connect()
db.create_tables([Book, Movie], safe=True)

定义好模型之后,就可以在 pipelines 中指定了,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# -*- coding: utf-8 -*-
from douban.models import Book
from douban.models import Movie
# Define your item pipelines here
#
# Don't forget to add your pipeline to the ITEM_PIPELINES setting
# See: http://doc.scrapy.org/en/latest/topics/item-pipeline.html

class DoubanPipeline(object):

def process_item(self, item, spider):
return item

class MoviePipeline(object):

def process_item(self, item, spider):
Movie.create(name=item['name'], img=item['img'], link=item['link'],
info=item['info'], ratings=item['ratings'], quote=item['quote'])
return item

记得写完之后在 settings.py 文件中启用 pipelines, 代码如下:

1
2
3
"ITEM_PIPELINES": {
'douban.pipelines.MoviePipeline': 300,
}

现在就可以开始让爬虫起来了,scrapy crawl movie, 等到爬虫运行完之后,查看数据库,结果如下:

到此,本次初步尝试结束!

源码地址

  • https://coding.net/u/huprince/p/douban/git

参考

  • https://scrapy-chs.readthedocs.io/zh_CN/1.0/index.html
  • https://scrapy.org/doc/
  • http://docs.peewee-orm.com/en/latest/
hudengjin wechat
huprince's 微信公众号
激情打赏,放肆挥霍