实例讲解 Scrapy 爬虫 (一)

󰃭 2017-04-05

scrapy 简介

scrapy 是使用 python 语言开发的网络爬虫框架。 具有下面几个特点:

  • 设计简单,用户只需要简单定义数据提取规则,让 scrapy 完成抓取的工作,即可获取数据。
  • 提供丰富的插件扩展机制
  • 开源,100% 用 python 编写(基于 twisted 框架)
  • 官方文档比较详细

本系列包含的主题

  • scrapy 介绍,基本知识
  • 基本网页抓取(spider item pipeline)开发
  • downloader middlewares 开发
  • extensions(扩展)开发
  • 调试 scrapy
  • scrapy 开发中的问题合集(跟随项目更新)

基础知识

所有的资料方法都来自:

  • 官方文档
  • scrapy 源代码
  • python 3.4.2

scrapy 主要组件

  • scrapy engine, 控制整个爬虫的运行,请求调度,spider调用,下载调用,信号事件触发
  • spider(蜘蛛), 用来解析页面的类,解析后创建新的请求,或者创建数据结果集合
  • scheduler(调度器), 负责管理请求(来自 spider),存入队列,执行时返回给 scrapy引擎
  • Downloader(下载器),抓取页面并返回结果给 spider
  • Item pipeline(item管道), 处理网页中抽取的数据结果,进行清洗,校验,存储等操作
  • Downloader middlewares(下载器中间件),下载器与 spider 之间的勾子,可以对请求和响应的数据进行操作
  • extensions(扩展),在 scrapy 启动时初始化,提供增强的辅助功能

上面是主要用到的一些组件,还有 spider middlerwares 等没包含在内

scrapy 运行机制

  • 引擎调用蜘蛛获取第一个要抓取的 url, 存入调度器
  • 引擎从调度器获取请求 url(上面放入的 url),
  • 引擎传递请求 -> 下载中间件 -> 下载器
  • 下载器下载页面,响应结果 -> 下载中间件 -> 引擎
  • 引擎把响应结果交给蜘蛛处理
  • 蜘蛛解析响应,创建结果 Item 和新的请求
  • 结果 item 交给 item 管道处理
  • 新的请求存入调度器,重复上面的操作

环境安装

$ virtualenv -p python3 /project/scrapyenv
$ source /project/scrapyenv/bin/active

$ pip install scrapy

scrapy 基本命令

$ scrapy --help

Scrapy 1.3.3 - project: doubanbook

Usage:
  scrapy <command> [options] [args]

Available commands:
  bench         Run quick benchmark test
  check         Check spider contracts
  commands
  crawl         Run a spider
  edit          Edit spider
  fetch         Fetch a URL using the Scrapy downloader
  genspider     Generate new spider using pre-defined templates
  list          List available spiders
  parse         Parse URL (using its spider) and print the results
  runspider     Run a self-contained spider (without creating a project)
  settings      Get settings values
  shell         Interactive scraping console
  startproject  Create new project
  version       Print Scrapy version
  view          Open URL in browser, as seen by Scrapy

Use "scrapy <command> -h" to see more info about a command

开始第一个抓取程序,抓取豆瓣读书的一些电子书籍信息

创建项目

  • 创建项目,在项目目录执行命令 scrapy startproject doubanbook 生成下面的目录和文件
  • scrapy.cfg: 项目配置文件
  • douban/: 项目的 python 源代码 module
  • douban/items.py: 定义 item 类,用来存储爬取的数据.
  • douban/pipelines.py: pipelines文件,定义清洗,处理数据的类
  • douban/settings.py: 项目的配置文件
  • douban/spiders/: 放置你开发的蜘蛛(可以定义多个蜘蛛)

定义 items

item 就是存储爬取到数据的类,它的使用方式与字典相似。 定义的 item 继承自 scrapy.item.Item, 使用scrapy.item.Field 定义保存抓取数据的字段 修改 items.py 文件,定义抓取的三个字段

import scrapy
from scrapy.item import Item, Field

class DoubanbookItem(Item):
    title = Field()
    author = Field()
    publisher = Field()
    category = Field()
    price = Field()

设置 user agent

# 在 setting.py 文件
USER_AGENT = 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36'

创建 spider

$ cd doubanbook
$ scrapy genspider -t basic DoubanBook douban.com

在 spiders 目录下创建 spider 文件 doubanbook_spider.py, spider 的工作是提供抓取的 url 列表,解析返回的响应页面,从中提取数据 items, spider 需要继承自 scrapy.spider.Spider

scrapy.Spider 包含下面的方法和属性:

  • name: 指定 spider 的名字,必须是唯一的,不同的 spider 使用不同的名字(这个名字定义后在 scrapy命令行可以查看,并在运行时选择执行 spider)
  • start_urls: 这个属性定义了开始抓取的 url 列表,这个列表中的页面被下载后,从下载的页面中提取新的 url 请求
  • parse() 提供回调方法,解析抓取到的响应页面,提取数据并创建 items,或者生成更多的 request(url) 供下一步抓取
#!/usr/bin/env python
# coding: utf-8

import bs4

import scrapy
from scrapy.selector import Selector

class DoubanbookSpider(scrapy.Spider):
    name = "DoubanBook"
    allowed_domains = ["douban.com"]
    start_urls = [
        "http://book.douban.com", # 第一页面
    ]
    def parse(self, response):
        """通过 xpath 获取热门电子书的链接"""
        sel = Selector(response)
        sites = sel.xpath('//div[@class="section ebook-area"]//ul[@class="list-col list-col5"]/li//div[@class="title"]')
        for site in sites:
            title = site.xpath('a/@title').extract()
            link = site.xpath('a/@href').extract()
            title, link = title[0], link[0]
            print(title, link)
            yield Request(url=link, callback=self.parse2)

    def parse2(self, response):
        """
        解析电子书详细信息页面,使用 dom 解析获取数据
        """
        soup = bs4.BeautifulSoup(response.body)
        bookInfo = soup.findAll('div', attrs={'class':'article-profile-bd'})
        if bookInfo:
            bookInfo = bookInfo[0]
            item = DoubanBookItem()
            item['title'] = "".join(bookInfo.findAll('h1', attrs={'class':'article-title'})[0].strings)
            item['author'] = "".join(bookInfo.findAll('p', attrs={'class':'author'})[0].strings)
            item['category'] = "".join(bookInfo.findAll('p', attrs={'class':'category'})[0].strings)

            bookPrice = soup.findAll('span', attrs={'class':'current-price-count'})
            if bookPrice:
                item['price'] = list(bookInfo[0].strings)[0]

            return item

运行爬虫

  • 运行 scrapy list 查看当前有的蜘蛛
  • 运行 scrapy crawl -t json -o result.json DoubanBook 输出爬取的结果到 json 格式的结果文件

数据处理和存储

上面执行爬虫使用的是 scrapy 自身的数据导出方式(通过命令行参数 -t 指定), 如果需要自定义导出方式,例如存储数据到 redis 队列可以如下操作

修改 pipelines.py 文件,添加如下代码

import redis
import json

class DoubanbookPipeline(object):
    def process_item(self, item, spider):
        if float(item['price']) > 2:
            # to redis
            rd = redis.Redis(host='localhost', port=6379, db=0)
            rd.lpush('result', json.dumps(dict(item)))
            # to file
            line = json.dumps(dict(item)) + "\n"
            return item
        else:
            raise DropItem("Price err")

在配置文件中开启pipline插件,修改 settings.py 文件

ITEM_PIPELINES = {
                'douban.pipelines.DoubanPipeline': 300,
                }

检查抓回来的数据

$ redis-cli
$ lrange result 1 10000

技术点

页面的分析主要采用 xpath/css选择器/dom解析/正则表达式 等方式处理(上面演示了2种方法) 解析函数被调用的方式是基于异步回调,不要在解析函数内调用长时间阻塞的函数,要尽快把控制权返回给引擎

相关文件链接

相关代码放在社区的 git 服务器上 https://git.u3v3.com/qinghe/python_learn, doubanbook 目录