scrapy爬虫使用简明教程

目录

一、基本框架介绍

二、安装与基本命令

三、scrapy框架基本使用及完整案例

四、各组件的一些用法说明

一、基本框架介绍

Scrapy框架介绍:

  • Scrapy是: 由Python语言开发的一个快速、高层次的屏幕抓取和web抓取框架,用于抓取web站点并从页面中提取结构化的数据。

  • Scrapy吸引人的地方在于它是一个框架,任何人都可以根据需求方便的修改。它也提供了多种类型爬虫的基类,如BaseSpider、sitemap爬虫等,最新版本又提供了web2.0爬虫的支持。

  • Scrap,是碎片的意思,这个Python的爬虫框架叫Scrapy。

Scrapy框架的运行原理

  • 引擎(Scrapy Engine)

    • 用来处理整个系统的数据流处理, 触发事务(框架核心)
  • Item 项目,它定义了爬取结果的数据结构,爬取的数据会赋值成改Item对象

  • 调度器(Scheduler)

    • 用来接受引擎发过来的请求, 压入队列中, 并在引擎再次请求的时候返回.
    • 可以想像成一个URL(抓取网页的网址或者说是链接)的优先队列, 由它来决定下一个要抓取的网址是什么, 同时去除重复的网址
  • 下载器(Downloader)

    • 用于下载网页内容, 并将网页内容返回给蜘蛛(Scrapy下载器是建立在twisted这个高效的异步模型上的)
  • 爬虫(Spiders)

    • 爬虫是主要干活的, 用于从特定的网页中提取自己需要的信息, 即所谓的实体(Item)。用户也可以从中提取出链接,让Scrapy继续抓取下一个页面
  • 项目管道(Pipeline)

    • 负责处理爬虫从网页中抽取的实体,主要的功能是持久化实体、验证实体的有效性、清除不需要的信息。当页面被爬虫解析后,将被发送到项目管道,并经过几个特定的次序处理数据。
  • 下载器中间件(Downloader Middlewares)

    • 位于Scrapy引擎和下载器之间的框架,主要是处理Scrapy引擎与下载器之间的请求及响应。
  • 爬虫中间件(Spider Middlewares)

    • 介于Scrapy引擎和爬虫之间的框架,主要工作是处理蜘蛛的响应输入和请求输出。
  • 调度中间件(Scheduler Middewares)

    • 介于Scrapy引擎和调度之间的中间件,从Scrapy引擎发送到调度的请求和响应。

数据处理流程

  • 引擎打开一个域名,时蜘蛛处理这个域名,并让蜘蛛获取第一个爬取的URL。

  • 引擎从蜘蛛那获取第一个需要爬取的URL,然后作为请求在调度中进行调度。
  • 引擎从调度那获取接下来进行爬取的页面。
  • 调度将下一个爬取的URL返回给引擎,引擎将它们通过下载中间件发送到下载器。
  • 当网页被下载器下载完成以后,响应内容通过下载中间件被发送到引擎。
  • 引擎收到下载器的响应并将它通过蜘蛛中间件发送到蜘蛛进行处理。
  • 蜘蛛处理响应并返回爬取到的项目,然后给引擎发送新的请求。
  • 引擎将抓取到的项目项目管道,并向调度发送请求。
  • 系统重复第二部后面的操作,直到调度中没有请求,然后断开引擎与域之间的联系。

二、安装与基本命令

# 安装scrapy
$ pip install scrapy
$ pip list
# 获取页面内容
$ scrapy
$ scrapy fetch http://www.csdn.net
# 简单交互
$ scrapy shell http://www.csdn.net
: response.selector.re("<title>(.*?)</title>")

Scrapy 命令 分为两种:全局命令项目命令

  • 全局命令:在哪里都能使用。

  • 项目命令:必须在爬虫项目里面才能使用。

全局命令

C:\Users\zhouxw>scrapy -h
Scrapy 1.2.1 - no active project

使用格式:
  scrapy <command> [options] [args]

可用的命令:
  bench         测试本地硬件性能(工作原理:):scrapy bench
  commands
  fetch         取URL使用Scrapy下载
  genspider     产生新的蜘蛛使用预先定义的模板
  runspider     运用单独一个爬虫文件:scrapy runspider abc.py
  settings      获取设置值
  shell         进入交互终端,用于爬虫的调试(如果你不调试,那么就不常用):scrapy shell http://www.baidu.com --nolog(--nolog 不显示日志信息)
  startproject  创建一个爬虫项目,如:scrapy startproject demo(demo 创建的爬虫项目的名字)
  version       查看版本:(scrapy version)
  view          下载一个网页的源代码,并在默认的文本编辑器中打开这个源代码:scrapy view http://www.aobossir.com/

  [ more ]      从项目目录运行时可获得更多命令

使用 "scrapy <command> -h" 要查看有关命令的更多信息

项目命令

       项目命令里有几个选项全局命令里没有

D:\BaiduYunDownload\first>scrapy -h
Scrapy 1.2.1 - project: first

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

Available commands:
  bench         Run quick benchmark test
  check         Check spider contracts
  commands
  crawl         运行一个爬虫文件。:scrapy crawl f1 或者 scrapy crawl f1 --nolog
  edit          使用编辑器打开爬虫文件 (Windows上似乎有问题,Linux上没有问题):scrapy edit f1
  fetch         Fetch a URL using the Scrapy downloader
  genspider     Generate new spider using pre-defined templates
  list          列出当前爬虫项目下所有的爬虫文件: scrapy list
  parse         Parse URL (using its spider) and print the results
  runspider     Run a self-contained spider (without creating a project)
  settings      获取设置值
  shell         进入交互终端,用于爬虫的调试(如果你不调试,那么就不常用)
  startproject  创建一个爬虫项目,如:scrapy startproject demo(demo 创建的爬虫项目的名字)
  version       查看版本:(scrapy version)
  view          下载一个网页的源代码,并在默认的文本编辑器中打开这个源代码

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

三、scrapy框架基本使用及案例

框架使用方法

使用scrapy startproject命令来创建一个项目

进入项目目录,创建爬虫spider类文

进入items.py创建自己的Item容器类

进入自定义的spider类,解析Response信息,并封装到Item

使用Item Pipeline项目管道对解析出来的Item数据进行清理、验证、去重、存储。

执行爬取命令来进行爬取信息。

案例实战

  • 任务:爬取csdn学院中的课程信息(编程语言的)

  • 网址:https://edu.csdn.net/courses/o280/p1 (第一页)

  • https://edu.csdn.net/courses/o280/p2 (第二页)

① 创建项目

  • 在命令行编写下面命令,创建项目educsdn
scrapy startproject  educsdn
  • 项目目录结构:
educsdn
├── educsdn
│   ├── __init__.py
│   ├── __pycache__
│   ├── items.py        # Items的定义,定义抓取的数据结构
│   ├── middlewares.py  # 定义Spider和DownLoader的Middlewares中间件实现。 
│   ├── pipelines.py    # 它定义Item Pipeline的实现,即定义数据管道
│   ├── settings.py     # 它定义项目的全局配置
│   └── spiders         # 其中包含一个个Spider的实现,每个Spider都有一个文件
│       ├── __init__.py
│       └── __pycache__
└── scrapy.cfg    #Scrapy部署时的配置文件,定义了配置文件路径、部署相关信息等内容

② 进入educsdn项目目录,创建爬虫spider类文件(courses课程)

  • 执行genspider命令,第一个参数是Spider的名称,第二个参数是网站域名。
scrapy genspider courses edu.csdn.net
├── educsdn
│   ├── __init__.py
│   ├── __pycache__
│   │   ├── __init__.cpython-36.pyc
│   │   └── settings.cpython-36.pyc
│   ├── items.py
│   ├── middlewares.py
│   ├── pipelines.py
│   ├── settings.py
│   └── spiders
│       ├── __init__.py
│       ├── __pycache__
│       │   └── __init__.cpython-36.pyc
│       └── courses.py  #在spiders目录下有了一个爬虫类文件courses.py
└── scrapy.cfg

# courses.py的文件代码如下: (这是此时的初始代码)
# -*- coding: utf-8 -*-
import scrapy

class CoursesSpider(scrapy.Spider):
    name = 'courses'
    allowed_domains = ['edu.csdn.net']
    start_urls = ['http://edu.csdn.net/']

    def parse(self, response):
        pass
  • Spider是自己定义的类,Scrapy用它来从网页中抓取内容,并解析抓取结果。

  • 此类继承Scrapy提供的Spider类scrapy.Spider,类中有三个属性:name、allowed_domains、start_urls和方法parse。

  • name:是每个项目唯一名字,用于区分不同Spider。

  • allowed_domains: 它是允许爬取的域名,如果初始或后续的请求链接不是这个域名,则请求链接会被过滤掉
  • start_urls: 它包含了Spider在启动时爬取的URL列表,初始请求是由它来定义的。
  • parse方法: 调用start_urls链接请求下载执行后则调用parse方法,并将结果传入此方法。

③ 创建Item

  • Item是保存爬取数据的容器,它的使用方法和字典类型,但相比字典多了些保护机制。

  • 创建Item需要继承scrapy.Item类,并且定义类型为scrapy.Field的字段:(课程标题、课程地址、图片、授课老师,视频时长、价格)

  • 具体代码如下:(修改类名为CoursesItem)

import scrapy

class CoursesItem(scrapy.Item):
    # define the fields for your item here like:
    # name = scrapy.Field()
    title = scrapy.Field()
    url = scrapy.Field()
    pic = scrapy.Field()
    teacher = scrapy.Field()
    time = scrapy.Field()
    price = scrapy.Field()
    #pass

④ 解析Response

  • 在courses.py文件中,parse()方法的参数response是start_urls里面的链接爬取后的结果。

  • 提取的方式可以是CSS选择器、XPath选择器或者是re正则表达式。

# -*- coding: utf-8 -*-
import scrapy
from educsdn.items import CoursesItem

class CoursesSpider(scrapy.Spider):
    name = 'courses'
    allowed_domains = ['edu.csdn.net']
    start_urls = ['https://edu.csdn.net/courses/o280/p1']
    p=1
    def parse(self, response):
        #解析并输出课程标题
        #print(response.selector.css("div.course_item span.title::text").extract())
        #获取所有课程
        dlist = response.selector.css("div.course_item")
        #遍历课程,并解析信息后封装到item容器中
        for dd in dlist:
            item = CoursesItem()
            item['title'] = dd.css("span.title::text").extract_first()
            item['url'] = dd.css("a::attr(href)").extract_first()
            item['pic'] = dd.css("img::attr(src)").extract_first()
            item['teacher'] = dd.re_first("<p>讲师:(.*?)</p>")
            item['time'] = dd.re_first("<em>([0-9]+)</em>课时")
            item['price'] = dd.re_first("¥([0-9\.]+)")
            #print(item)
            #print("="*70)
            yield item

        #获取前10页的课程信息 
        self.p += 1
        if self.p <= 10:
            next_url = 'https://edu.csdn.net/courses/o280/p'+str(self.p)
            url = response.urljoin(next_url) #构建绝对url地址(这里可省略)
            yield scrapy.Request(url=url,callback=self.parse)

⑤、创建数据库和表:

  • 在mysql中创建数据库csdndb和数据表courses
CREATE TABLE `courses` (                          
   `id` int(10) unsigned NOT NULL AUTO_INCREMENT,  
   `title` varchar(255) DEFAULT NULL,              
   `url` varchar(255) DEFAULT NULL,                
   `pic` varchar(255) DEFAULT NULL,                
   `teacher` varchar(32) DEFAULT NULL,             
   `time` varchar(16) DEFAULT NULL,                
   `price` varchar(16) DEFAULT NULL,               
   PRIMARY KEY (`id`)                              
 ) ENGINE=InnoDB DEFAULT CHARSET=utf8

⑥、使用Item Pipeline

  • Item Pipeline为项目管道,当Item生产后,他会自动被送到Item Pipeline进行处理:
  • 我们常用Item Pipeline来做如下操作:
    • 清理HTML数据
    • 验证抓取数据,检查抓取字段
    • 查重并丢弃重复内容
    • 将爬取结果保存到数据库里
import pymysql
from scrapy.exceptions import DropItem

class EducsdnPipeline(object):
    def process_item(self, item, spider):
        # 过滤价格为空的数据
        if item['price'] == None:
            raise DropItem("Drop item found: %s" % item)
        else:
            return item


class MysqlPipeline(object):
    # 课程信息导入mysql数据处理类
    def __init__(self,host,database,user,password,port):
        self.host = host
        self.database = database
        self.user = user
        self.password = password
        self.port = port
        self.db=None
        self.cursor=None

    @classmethod
    def from_crawler(cls,crawler):
        return cls(
            host = crawler.settings.get("MYSQL_HOST"),
            database = crawler.settings.get("MYSQL_DATABASE"),
            user = crawler.settings.get("MYSQL_USER"),
            password = crawler.settings.get("MYSQL_PASS"),
            port = crawler.settings.get("MYSQL_PORT")
        )

    def open_spider(self,spider):
        self.db = pymysql.connect(self.host,self.user,self.password,self.database,charset='utf8',port=self.port)
        self.cursor = self.db.cursor()

    def process_item(self, item, spider):
        sql = "insert into courses(title,url,pic,teacher,time,price) values('%s','%s','%s','%s','%s','%s')"%(item['title'],item['url'],item['pic'],item['teacher'],str(item['time']),str(item['price']))
        #print(item)
        self.cursor.execute(sql)
        self.db.commit()
        return item

    def close_spider(self,spider):
        self.db.close()

⑦ 修改配置文件

  • 打开配置文件:settings.py 开启并配置ITEM_PIPELINES信息,配置数据库连接信息
ITEM_PIPELINES = {
    'educsdn.pipelines.EducsdnPipeline': 300,
    'educsdn.pipelines.MysqlPipeline': 301,
}
MYSQL_HOST = 'localhost'
MYSQL_DATABASE = 'csdndb'
MYSQL_USER = 'root'
MYSQL_PASS = ''
MYSQL_PORT = 3306

⑧、运行爬取:

  • 执行如下命令来启用数据爬取
scrapy crawl courses

四、各组件的一些用法说明

4.1  运行结果保存

  • 将结果保存到文件中: 格式:json、csv、xml、pickle、marshal等

scrapy crawl fang -o fangs.json -t json
scrapy crawl fang -o fangs.csv  -t csv
scrapy crawl fang -o fangs.xml  -t xml
scrapy crawl fang -o fangs.pickle 
scrapy crawl fang -o fangs.marshal

4.2 Selector选择器

  • 对用爬取信息的解析,有正则re、Xpath、Beautiful Soup和PyQuery。
  • 而Scrapy还给我们提供自己的数据解析方法,即Selector(选择器)。
  • Selector(选择器)是基于lxml来构建的,支持XPath、CSS选择器以及正则表达式,功能全面,解析速度和准确度非常高。

直接使用:

  • Selector(选择器)是一个可以独立使用模块。 直接导入模块,就可以实例化使用,如下所示:
from scrapy import Selector
content="<html><head><title>My html</title><body><h3>Hello Word!</h3></body></head></html>"
selector = Selector(text=content)
print(selector.xpath('/html/head/title/text()').extract_first())
print(selector.css('h3::text').extract_first())

Scrapy shell

  • 我们借助于Scrapy shell来模拟请求的过程,然后把一些可操作的变量传递给我们,如request、response等。
zhangtaodeMacBook-Pro:scrapydemo zhangtao$ scrapy shell http://www.baidu.com
2018-05-08 14:46:29 [scrapy.utils.log] INFO: Scrapy 1.5.0 started (bot: scrapybot)
2018-05-08 14:46:29 [scrapy.utils.log] INFO: Versions: lxml 4.2.1.0, libxml2 2.9.8, cssselect 1.0.3, parsel 1.4.0, w3lib 1.19.0, Twisted 18.4.0, Python 3.6.4 (default, Jan  6 2018, 11:49:38) - [GCC 4.2.1 Compatible Apple LLVM 8.0.0 (clang-800.0.42.1)], pyOpenSSL 17.5.0 (OpenSSL 1.1.0h  27 Mar 2018), cryptography 2.2.2, Platform Darwin-15.6.0-x86_64-i386-64bit
2018-05-08 14:46:29 [scrapy.crawler] INFO: Overridden settings: {'DUPEFILTER_CLASS': 'scrapy.dupefilters.BaseDupeFilter', 'LOGSTATS_INTERVAL': 0}
2018-05-08 14:46:29 [scrapy.middleware] INFO: Enabled extensions:
['scrapy.extensions.corestats.CoreStats',
 'scrapy.extensions.telnet.TelnetConsole',
 'scrapy.extensions.memusage.MemoryUsage']
2018-05-08 14:46:29 [scrapy.middleware] INFO: Enabled downloader middlewares:
['scrapy.downloadermiddlewares.httpauth.HttpAuthMiddleware',
 'scrapy.downloadermiddlewares.downloadtimeout.DownloadTimeoutMiddleware',
 'scrapy.downloadermiddlewares.defaultheaders.DefaultHeadersMiddleware',
 'scrapy.downloadermiddlewares.useragent.UserAgentMiddleware',
 'scrapy.downloadermiddlewares.retry.RetryMiddleware',
 'scrapy.downloadermiddlewares.redirect.MetaRefreshMiddleware',
 'scrapy.downloadermiddlewares.httpcompression.HttpCompressionMiddleware',
 'scrapy.downloadermiddlewares.redirect.RedirectMiddleware',
 'scrapy.downloadermiddlewares.cookies.CookiesMiddleware',
 'scrapy.downloadermiddlewares.httpproxy.HttpProxyMiddleware',
 'scrapy.downloadermiddlewares.stats.DownloaderStats']
2018-05-08 14:46:29 [scrapy.middleware] INFO: Enabled spider middlewares:
['scrapy.spidermiddlewares.httperror.HttpErrorMiddleware',
 'scrapy.spidermiddlewares.offsite.OffsiteMiddleware',
 'scrapy.spidermiddlewares.referer.RefererMiddleware',
 'scrapy.spidermiddlewares.urllength.UrlLengthMiddleware',
 'scrapy.spidermiddlewares.depth.DepthMiddleware']
2018-05-08 14:46:29 [scrapy.middleware] INFO: Enabled item pipelines:
[]
2018-05-08 14:46:29 [scrapy.extensions.telnet] DEBUG: Telnet console listening on 127.0.0.1:6023
2018-05-08 14:46:29 [scrapy.core.engine] INFO: Spider opened
2018-05-08 14:46:29 [scrapy.core.engine] DEBUG: Crawled (200) <GET http://www.baidu.com> (referer: None)
[s] Available Scrapy objects:
[s]   scrapy     scrapy module (contains scrapy.Request, scrapy.Selector, etc)
[s]   crawler    <scrapy.crawler.Crawler object at 0x108ea8ac8>
[s]   item       {}
[s]   request    <GET http://www.baidu.com>
[s]   response   <200 http://www.baidu.com>
[s]   settings   <scrapy.settings.Settings object at 0x109cbb8d0>
[s]   spider     <DefaultSpider 'default' at 0x109f56e10>
[s] Useful shortcuts:
[s]   fetch(url[, redirect=True]) Fetch URL and update local objects (by default, redirects are followed)
[s]   fetch(req)                  Fetch a scrapy.Request and update local objects 
[s]   shelp()           Shell help (print this help)
[s]   view(response)    View response in a browser
>>> response.url
'http://www.baidu.com'
>>> response.status
200
>>> response.xpath('/html/head/title/text()').extract_first()
'百度一下,你就知道'
>>> response.xpath('//a/text()').extract_first()
'新闻'
>>> response.xpath('//a/text()').extract()
['新闻', 'hao123', '地图', '视频', '贴吧', '登录', '更多产品', '关于百度', 'About Baidu', '使用百度前必读', '意见反馈']
>>> response.xpath('//a/@href').extract()
['http://news.baidu.com', 'http://www.hao123.com', 'http://map.baidu.com', 'http://v.baidu.com', 'http://tieba.baidu.com', 'http://www.baidu.com/bdorz/login.gif?login&tpl=mn&u=http%3A%2F%2Fwww.baidu.com%2f%3fbdorz_come%3d1', '//www.baidu.com/more/', 'http://home.baidu.com', 'http://ir.baidu.com', 'http://www.baidu.com/duty/', 'http://jianyi.baidu.com/']

Xpath选择器:

  • response.selector属性返回内容相当于response的body构造了一个Selector对象。
  • Selector对象可以调用xpath()方法实现信息的解析提取。
    • 在xpath()后使用extract()可以返回所有的元素结果。
    • 若xpath()有问题,那么extract()会返回一个空列表。
    • 在xpath()后使用extract_first()可以返回第一个元素结果。
  • 使用scrapy shell 爬取"淘宝网"->"商品分类"->"特色市场"的信息。
$ scrapy shell https://www.taobao.com/tbhome/page/special-markets
... ...

>>> response.url
'https://www.taobao.com/tbhome/page/special-markets'

>>> response.status
200

>>> response.selector.xpath("//dt/text()")
[<Selector xpath='//dt/text()' data='时尚爆料王'>, <Selector xpath='//dt/text()' data='品质生活家'>, 
 <Selector xpath='//dt/text()' data='特色玩味控'>, <Selector xpath='//dt/text()' data='实惠专业户'>]

>>> response.selector.xpath("//dt/text()").extract()
['时尚爆料王', '品质生活家', '特色玩味控', '实惠专业户']

>>> dllist = response.selector.xpath("//dl[@class='market-list']")
>>> for v in dllist:
...     print(v.xpath("./dt/text()").extract_first())
... 
时尚爆料王
品质生活家
特色玩味控
实惠专业户

>>> for v in dllist:
...     print(v.xpath("./dt/text()").extract_first())
...     print("="*50)
...     alist = v.xpath(".//a")
...     for a in alist:
...             print(a.xpath("./@href").extract_first(),end=":")
...             print(a.xpath("./span/img/@alt").extract_first())
... 
时尚爆料王
==================================================
https://if.taobao.com/:潮流从这里开始
https://guang.taobao.com/:外貌协会の逛街指南
https://mei.taobao.com/:妆 出你的腔调
https://g.taobao.com/:探索全球美好生活
//star.taobao.com/:全球明星在这里
https://mm.taobao.com/:美女红人集中地
https://www.taobao.com/markets/designer/stylish:全球创意设计师平台
品质生活家
==================================================
https://chi.taobao.com/chi/:食尚全球 地道中国
//q.taobao.com:懂得好生活
https://www.jiyoujia.com/:过我想要的生活
https://www.taobao.com/markets/sph/sph/sy:尖货奢品品味选择
https://www.taobao.com/markets/qbb/index:享受育儿生活新方式
//car.taobao.com/:买车省钱,用车省心
//sport.taobao.com/:爱上运动每一天
//zj.taobao.com:匠心所在  物有所值
//wt.taobao.com/:畅享优质通信生活
https://www.alitrip.com/:比梦想走更远
特色玩味控
==================================================
https://china.taobao.com:地道才够味!
https://www.taobao.com/markets/3c/tbdc:为你开启潮流新生活
https://acg.taobao.com/:ACGN  好玩好看
https://izhongchou.taobao.com/index.htm:认真对待每一个梦想。
//enjoy.taobao.com/:园艺宠物爱好者集中营
https://sf.taobao.com/:法院处置资产,0佣金捡漏
https://zc-paimai.taobao.com/:超值资产,投资首选
https://paimai.taobao.com/:想淘宝上拍卖
//xue.taobao.com/:给你未来的学习体验
//2.taobao.com:让你的闲置游起来
https://ny.taobao.com/:价格实惠品类齐全
实惠专业户
==================================================
//tejia.taobao.com/:优质好货 特价专区
https://qing.taobao.com/:品牌尾货365天最低价
https://ju.taobao.com/jusp/other/mingpin/tp.htm:奢侈品团购第一站
https://ju.taobao.com/jusp/other/juliangfan/tp.htm?spm=608.5847457.102202.5.jO4uZI:重新定义家庭生活方式
https://qiang.taobao.com/:抢到就是赚到!
https://ju.taobao.com/jusp/nv/fcdppc/tp.htm:大牌正品 底价特惠
https://ju.taobao.com/jusp/shh/life/tp.htm?:惠聚身边精选好货
https://ju.taobao.com/jusp/sp/global/tp.htm?spm=0.0.0.0.biIDGB:10点上新 全球底价
https://try.taobao.com/index.htm:总有新奇等你发现

CSS选择器:

  • 同xpath()一样。
  • 使用scrapy shell 爬取"淘宝网"->"商品分类"->"主题市场"的信息。
>>> response.url
'https://www.taobao.com/tbhome/page/market-list'
>>> response.status
200
>>> response.css("a.category-name-level1::text").extract()
['女装男装', '鞋类箱包', '母婴用品', '护肤彩妆', '汇吃美食', '珠宝配饰', '家装建材', '家居家纺', '百货市场', '汽车·用品', '手机数码', '家电办公', '更多服务', '生活服务', '运动户外', '花鸟文娱', '农资采购']

#获取淘宝页面中所有分类信息
>>> dlist = response.css("div.home-category-list")
>>> for dd in dlist:
      print(dd.css("a.category-name-level1::text").extract_first())
      print("="*50)
      alist = dd.css("li.category-list-item")
      for v in alist:
              print(v.xpath("./a/text()").extract_first())
              print("-"*50)
              talist = v.css("div.category-items a")
              for a in talist:
                      print(a.css("::text").extract_first(),end=" ")
              print()

>>>女装男装
==================================================
潮流女装
--------------------------------------------------
羽绒服 毛呢大衣 毛衣 冬季外套 新品 裤子 连衣裙 腔调 

时尚男装
--------------------------------------------------
秋冬新品 淘特莱斯 淘先生 拾货 秋冬外套 时尚套装 潮牌 爸爸装 

性感内衣
--------------------------------------------------
春新品 性感诱惑 甜美清新 简约优雅 奢华高贵 运动风 塑身 基础内衣 

...

注:css中获取属性:a.css("::attr(href)").extract_first()

正则匹配:

>>> response.xpath("//head").re("<title>(.*?)</title>")
['淘宝首页行业市场']
>>> response.selector.re("<a .*?>(.*?)</a>")

4.3 Spider的使用

  • 在Scrapy中,要抓取网站的链接配置、抓取逻辑、解析逻辑里其实都是在Spider中配置的。

  • Spider要做的事就是有两件:定义抓取网站的动作分析爬取下来的网页

Spider运行流程:

  • 整个抓取循环过程如下所述:

    • 以初始的URL初始化Request,并设置回调函数。请求成功时Response生成并作为参数传给该回调函数。
    • 在回调函数内分析返回的网页内容。返回结果两种形式,一种为字典或Item数据对象;另一种是解析到下一个链接。
    • 如果返回的是字典或Item对象,我们可以将结果存入文件,也可以使用Pipeline处理并保存。
    • 如果返回Request,Response会被传递给Request中定义的回调函数参数,即再次使用选择器来分析生成数据Item。

Spider类分析

  • Spider类源代码:打开文件Python36/Lib/site-packages/scrapy/spiders/__init__.py
import logging
import warnings

from scrapy import signals
from scrapy.http import Request
from scrapy.utils.trackref import object_ref
from scrapy.utils.url import url_is_from_spider
from scrapy.utils.deprecate import create_deprecated_class
from scrapy.exceptions import ScrapyDeprecationWarning
from scrapy.utils.deprecate import method_is_overridden

#所有爬虫的基类,自定义的爬虫必须从继承此类
class Spider(object_ref):

    #定义spider名字的字符串(string)。spider的名字定义了Scrapy如何定位(并初始化)spider,所以其必须是唯一的。
    #name是spider最重要的属性,而且是必须的。
    #一般做法是以该网站(domain)(加或不加 后缀 )来命名spider。 例如,如果spider爬取 douban.com ,该spider通常会被命名为 douban
    name = None
    custom_settings = None

    #初始化,提取爬虫名字,start_ruls
    def __init__(self, name=None, **kwargs):
        if name is not None:
            self.name = name
        # 如果爬虫没有名字,中断后续操作则报错
        elif not getattr(self, 'name', None):
            raise ValueError("%s must have a name" % type(self).__name__)

        # python 对象或类型通过内置成员__dict__来存储成员信息
        self.__dict__.update(kwargs)

        #URL列表。当没有指定的URL时,spider将从该列表中开始进行爬取。因此,第一个被获取到的页面的URL将是该列表之一。 后续的URL将会从获取到的数据中提取。
        if not hasattr(self, 'start_urls'):
            self.start_urls = []

    @property
    def logger(self):
        logger = logging.getLogger(self.name)
        return logging.LoggerAdapter(logger, {'spider': self})

    # 打印Scrapy执行后的log信息
    def log(self, message, level=log.DEBUG, **kw):
        log.msg(message, spider=self, level=level, **kw)

    @classmethod
    def from_crawler(cls, crawler, *args, **kwargs):
        spider = cls(*args, **kwargs)
        spider._set_crawler(crawler)
        return spider

    #判断对象object的属性是否存在,不存在做断言处理
    def set_crawler(self, crawler):
        assert not hasattr(self, '_crawler'), "Spider already bounded to %s" % crawler
        self._set_crawler(crawler)

    def _set_crawler(self, crawler):
        self.crawler = crawler
        self.settings = crawler.settings
        crawler.signals.connect(self.close, signals.spider_closed)

    #@property
    #def crawler(self):
    #    assert hasattr(self, '_crawler'), "Spider not bounded to any crawler"
    #    return self._crawler

    #@property
    #def settings(self):
    #    return self.crawler.settings

    #该方法将读取start_urls内的地址,并为每一个地址生成一个Request对象,交给Scrapy下载并返回Response
    #该方法仅调用一次
    def start_requests(self):
        for url in self.start_urls:
            yield self.make_requests_from_url(url)

    #start_requests()中调用,实际生成Request的函数。
    #Request对象默认的回调函数为parse(),提交的方式为get
    def make_requests_from_url(self, url):
        return Request(url, dont_filter=True)

    #默认的Request对象回调函数,处理返回的response。
    #生成Item或者Request对象。用户必须实现这个类
    def parse(self, response):
        raise NotImplementedError

    @classmethod
    def handles_request(cls, request):
        return url_is_from_spider(request.url, cls)

    @staticmethod
    def close(spider, reason):
        closed = getattr(spider, 'closed', None)
        if callable(closed):
            return closed(reason)

    def __str__(self):
        return "<%s %r at 0x%0x>" % (type(self).__name__, self.name, id(self))

    __repr__ = __str__
  • Spider类继承自scrapy.spiders.Spider.
  • Spider类这个提供了start_requests()方法的默认实现,读取并请求start_urls属性,并调用parse()方法解析结果。
  • Spider类的属性和方法:
    • name:爬虫名称,必须唯一的,可以生成多个相同的Spider实例,数量没有限制。
    • allowed_domains: 允许爬取的域名,是可选配置,不在此范围的链接不会被跟进爬取。
    • start_urls: 它是起始URL列表,当我们没有实现start_requests()方法时,默认会从这个列表开始抓取。
    • custom_settings: 它是一个字典,专属于Spider的配置,此设置会覆盖项目全局的设置,必须定义成类变量。
    • crawler:它是由from_crawler()方法设置的,Crawler对象包含了很多项目组件,可以获取settings等配置信息。
    • settings: 利用它我们可以直接获取项目的全局设置变量。
    • start_requests(): 使用start_urls里面的URL来构造Request,而且Request是GET请求方法。
    • parse(): 当Response没有指定回调函数时,该方法会默认被调用。
    • closed(): 当Spider关闭时,该方法会调用

实战案例

  • 任务:使用scrapy爬取关键字为python信息的百度文库搜索信息(每页10条信息)
  • url地址分析: https://wenku.baidu.com/search?word=python&pn=0 第一页
  • url地址分析: https://wenku.baidu.com/search?word=python&pn=10 第二页

  • 具体实现:

  • ① 使用scrapy命令创建爬虫项目dbwenku
    $ scrapy startproject bdwenku
    
  • ② 创建spider爬虫文件wenku:
$ cd bdwenku
$ scrapy genspider wenku wenku.baidu.com
  • ③ 编辑wenku.py爬虫文件,代码如下:
# -*- coding: utf-8 -*-
import scrapy

class WenkuSpider(scrapy.Spider):
    name = 'wenku'
    allowed_domains = ['wenku.baidu.com']
    start_urls = ['https://wenku.baidu.com/search?word=python&pn=0']

    def parse(self, response):
        print("Hello Scrapy")
        print(response)
  • ④ 运行测试:
 $ scrapy crawl wenku
  • 爬取数据出现错误: DEBUG: Forbidden by robots.txt: 请求被拒绝了
  • 也就是百度文库的robots.txt设置了禁止外部爬取信息。
  • 解决办法:在settings.py配置文件中,将ROBOTSTXT_OBEY的值设为False,忽略robot协议,继续爬取
...
2018-05-11 11:00:54 [scrapy.core.engine] INFO: Spider opened
2018-05-11 11:00:54 [scrapy.extensions.logstats] INFO: Crawled 0 pages (at 0 pages/min), scraped 0 items (at 0 items/min)
2018-05-11 11:00:54 [scrapy.extensions.telnet] DEBUG: Telnet console listening on 127.0.0.1:6027
2018-05-11 11:00:56 [scrapy.core.engine] DEBUG: Crawled (200) <GET https://wenku.baidu.com/robots.txt> (referer: None)
2018-05-11 11:00:56 [scrapy.downloadermiddlewares.robotstxt] DEBUG: Forbidden by robots.txt: <GET https://wenku.baidu.com/search?word=python&pn=0>
2018-05-11 11:00:56 [scrapy.core.engine] INFO: Closing spider (finished)
2018-05-11 11:00:56 [scrapy.statscollectors] INFO: Dumping Scrapy stats:
{'downloader/exception_count': 1,
 'downloader/exception_type_count/scrapy.exceptions.IgnoreRequest': 1,
...
  • ⑤ Spider爬虫文件:wenku.py 的几种写法:
# 单请求的信息爬取
# -*- coding: utf-8 -*-
import scrapy

class WenkuSpider(scrapy.Spider):
    name = 'wenku'
    allowed_domains = ['wenku.baidu.com']
    start_urls = ['https://wenku.baidu.com/search?word=python&pn=0']

    def parse(self, response):
        dllist = response.selector.xpath("//dl")
        #print(len(dllist))
        for dd in dllist:
            print(dd.xpath("./dt/p/a/@title").extract_first())
# 多个请求的信息爬取
# -*- coding: utf-8 -*-
import scrapy

class WenkuSpider(scrapy.Spider):
    name = 'wenku'
    allowed_domains = ['wenku.baidu.com']
    start_urls = ['https://wenku.baidu.com/search?word=python&pn=0','https://wenku.baidu.com/search?word=python&pn=10','https://wenku.baidu.com/search?word=python&pn=20']

    def parse(self, response):
        dllist = response.selector.xpath("//dl")
        #print(len(dllist))
        for dd in dllist:
            print(dd.xpath("./dt/p/a/@title").extract_first())

        print("="*70) #输出一条每个请求后分割线
# 实现一个爬取循环,获取10页信息
# -*- coding: utf-8 -*-
import scrapy

class WenkuSpider(scrapy.Spider):
    name = 'wenku'
    allowed_domains = ['wenku.baidu.com']
    start_urls = ['https://wenku.baidu.com/search?word=python&pn=0']
    p=0
    def parse(self, response):
        dllist = response.selector.xpath("//dl")
        #print(len(dllist))
        for dd in dllist:
            print(dd.xpath("./dt/p/a/@title").extract_first())

        print("="*70)

        self.p += 1
        if self.p < 10:
            next_url = 'https://wenku.baidu.com/search?word=python&pn='+str(self.p*10)
            url = response.urljoin(next_url) #构建绝对url地址(这里可省略)
            yield scrapy.Request(url=url,callback=self.parse)

4.4  Downloader Middleware的使用

  • 在Downloader Middleware的功能十分强大:可以修改User-Agent、处理重定向、设置代理、失败重试、设置Cookies等。
  • Downloader Middleware在整个架构中起作用的位置是以下两个。
    • 在Scheduler调度出队列的Request发送给Doanloader下载之前,也就是我们可以在Request执行下载前对其进行修改。
    • 在下载后生成的Response发送给Spider之前,也就是我们可以生成Resposne被Spider解析之前对其进行修改。

使用说明:

  • 在Scrapy中已经提供了许多Downloader Middleware,如:负责失败重试、自动重定向等中间件:
  • 它们都被定义到DOWNLOADER_MIDDLEWARES_BASE变量中。
  • 字典格式,其中数字为优先级,越小的优先调用。
# 在python3.6/site-packages/scrapy/settings/default_settings.py默认配置中

DOWNLOADER_MIDDLEWARES_BASE = {
    # Engine side
    'scrapy.downloadermiddlewares.robotstxt.RobotsTxtMiddleware': 100,
    'scrapy.downloadermiddlewares.httpauth.HttpAuthMiddleware': 300,
    'scrapy.downloadermiddlewares.downloadtimeout.DownloadTimeoutMiddleware': 350,
    'scrapy.downloadermiddlewares.defaultheaders.DefaultHeadersMiddleware': 400,
    'scrapy.downloadermiddlewares.useragent.UserAgentMiddleware': 500,
    'scrapy.downloadermiddlewares.retry.RetryMiddleware': 550,
    'scrapy.downloadermiddlewares.ajaxcrawl.AjaxCrawlMiddleware': 560,
    'scrapy.downloadermiddlewares.redirect.MetaRefreshMiddleware': 580,
    'scrapy.downloadermiddlewares.httpcompression.HttpCompressionMiddleware': 590,
    'scrapy.downloadermiddlewares.redirect.RedirectMiddleware': 600,
    'scrapy.downloadermiddlewares.cookies.CookiesMiddleware': 700,
    'scrapy.downloadermiddlewares.httpproxy.HttpProxyMiddleware': 750,
    'scrapy.downloadermiddlewares.stats.DownloaderStats': 850,
    'scrapy.downloadermiddlewares.httpcache.HttpCacheMiddleware': 900,
    # Downloader side
}

自定义Downloader Middleware中间件

  • 我们可以通过项目的DOWNLOADER_MIDDLEWARES变量设置来添加自己定义的Downloader Middleware。

  • 其中Downloader Middleware有三个核心方法:

    • process_request(request,spider)
    • process_response(request,response,spider)
    • process_exception(request,exception,spider)

① process_request(request,spider)

  • 当每个request通过下载中间件时,该方法被调用,这里有一个要求,该方法必须返回以下三种中的任意一种:None,返回一个Response对象返回一个Request对象raise IgnoreRequest。三种返回值的作用是不同的。

  • None:Scrapy将继续处理该request,执行其他的中间件的相应方法,直到合适的下载器处理函数(download handler)被调用,该request被执行(其response被下载)。

  • Response对象:Scrapy将不会调用任何其他的process_request()或process_exception() 方法,或相应地下载函数;其将返回该response。 已安装的中间件的 process_response() 方法则会在每个response返回时被调用。

  • Request对象:Scrapy则停止调用 process_request方法并重新调度返回的request。当新返回的request被执行后, 相应地中间件链将会根据下载的response被调用。

  • raise一个IgnoreRequest异常:则安装的下载中间件的 process_exception() 方法会被调用。如果没有任何一个方法处理该异常, 则request的errback(Request.errback)方法会被调用。如果没有代码处理抛出的异常,则该异常被忽略且不记录。

② process_response(request, response, spider)

  • process_response的返回值也是有三种:response对象request对象,或者raise一个IgnoreRequest异常

  • 如果其返回一个Response(可以与传入的response相同,也可以是全新的对象), 该response会被在链中的其他中间件的 process_response() 方法处理。

  • 如果其返回一个 Request 对象,则中间件链停止, 返回的request会被重新调度下载。处理类似于 process_request() 返回request所做的那样。

  • 如果其抛出一个 IgnoreRequest 异常,则调用request的errback(Request.errback)。

  • 如果没有代码处理抛出的异常,则该异常被忽略且不记录(不同于其他异常那样)。

  • 这里我们写一个简单的例子还是上面的项目,我们在中间件中继续添加如下代码:

...
def process_response(self, request, response, spider):
    response.status = 201
    return response
...

③ process_exception(request, exception, spider)

  • 当下载处理器(download handler)或 process_request() (下载中间件)抛出异常(包括 IgnoreRequest 异常)时,Scrapy调用 process_exception()。

  • process_exception() 也是返回三者中的一个: 返回 None 、 一个 Response 对象、或者一个 Request 对象。

  • 如果其返回 None ,Scrapy将会继续处理该异常,接着调用已安装的其他中间件的 process_exception() 方法,直到所有中间件都被调用完毕,则调用默认的异常处理。

  • 如果其返回一个 Response 对象,则已安装的中间件链的 process_response() 方法被调用。Scrapy将不会调用任何其他中间件的 process_exception() 方法。

实战案例:

# 在命令行下直接运行scrapy shell命令爬取信息,报403错误
$ scrapy shell https://book.douban.com/top250 
>>> response.status
>>> 403
  • ① 新建一个项目douban,命令如下所示:
scrapy startproject douban
  • ② 新建一个Spider类,名字为dbbook,命令如下所示:
cd douban
scrapy genspider dbbook book.douban.com
  • 编写爬虫代码。如下:
# -*- coding: utf-8 -*-
import scrapy

class DbbookSpider(scrapy.Spider):
    name = 'dbbook'
    allowed_domains = ['book.douban.com']
    start_urls = ['https://book.douban.com/top250?start=0']

    def parse(self, response):
        #print("状态:")
        pass
  • ③ 执行爬虫命令,排除错误。

$ scrapy crawl dbbook               #结果返回403错误  (服务器端拒绝访问)。

原因分析:默认scrapy框架请求信息中的User-Agent的值为:Scrapy/1.5.0(http://scrapy.org).

解决方案:我们可以在settings.py配置文件中:设置 USER_AGENT 或者DEFAULT_REQUEST_HEADERS信息

USER_AGENT = 'Opera/9.80(WindowsNT6.1;U;en)Presto/2.8.131Version/11.11'

或者
...
DEFAULT_REQUEST_HEADERS = {
   'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
   'Accept-Language': 'en',
   'User-Agent':'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.139 Safari/537.36',
}
...
  • ④ 开启Downloader Middleware中间件

  • 在项目的settings.py配置文件中:开启设置DOWNLOADER_MIDDLEWARES信息:

DOWNLOADER_MIDDLEWARES = {
    'douban.middlewares.DoubanDownloaderMiddleware': 543,
}
def process_request(self, request, spider):
        #输出header头信息
        print(request.headers)
        #伪装浏览器用户
        request.headers['user-agent']='Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.139 Safari/537.36'
        return None

4.5 Spider中间件(Middleware)

  • Spider中间件是介入到Scrapy的spider处理机制的钩子框架,您可以添加代码来处理发送给 Spiders 的response及spider产生的item和request。

激活spider中间件

  • 要启用spider中间件,您可以将其加入到 SPIDER_MIDDLEWARES 设置中。 该设置是一个字典,键位中间件的路径,值为中间件的顺序(order)。
  • 样例:
SPIDER_MIDDLEWARES = {
    'myproject.middlewares.CustomSpiderMiddleware': 543,
}
  • SPIDER_MIDDLEWARES 设置会与Scrapy定义的 SPIDER_MIDDLEWARES_BASE 设置合并(但不是覆盖), 而后根据顺序(order)进行排序,最后得到启用中间件的有序列表: 第一个中间件是最靠近引擎的,最后一个中间件是最靠近spider的。

  • 关于如何分配中间件的顺序请查看 SPIDER_MIDDLEWARES_BASE 设置,而后根据您想要放置中间件的位置选择一个值。 由于每个中间件执行不同的动作,您的中间件可能会依赖于之前(或者之后)执行的中间件,因此顺序是很重要的。

  • 如果您想禁止内置的(在 SPIDER_MIDDLEWARES_BASE 中设置并默认启用的)中间件, 您必须在项目的 SPIDER_MIDDLEWARES 设置中定义该中间件,并将其值赋为 None 。 例如,如果您想要关闭off-site中间件:

SPIDER_MIDDLEWARES = {
    'myproject.middlewares.CustomSpiderMiddleware': 543,
    'scrapy.contrib.spidermiddleware.offsite.OffsiteMiddleware': None,
}
  • 最后,请注意,有些中间件需要通过特定的设置来启用。更多内容请查看相关中间件文档。

编写自己的spider中间件

  • 编写spider中间件十分简单。每个中间件组件是一个定义了以下一个或多个方法的Python类:

class scrapy.contrib.spidermiddleware.SpiderMiddleware

process_spider_input(response, spider)    当response通过spider中间件时,该方法被调用,处理该response。

process_spider_input() 应该返回 None 或者抛出一个异常。

如果其返回 None ,Scrapy将会继续处理该response,调用所有其他的中间件直到spider处理该response。

如果其跑出一个异常(exception),Scrapy将不会调用任何其他中间件的 process_spider_input() 方法,并调用request的errback。 errback的输出将会以另一个方向被重新输入到中间件链中,使用 process_spider_output() 方法来处理,当其抛出异常时则带调用 process_spider_exception() 。

参数: response (Response 对象) – 被处理的response spider (Spider 对象) – 该response对应的spider process_spider_output(response, result, spider)   当Spider处理response返回result时,该方法被调用。

process_spider_output() 必须返回包含 Request 或 Item 对象的可迭代对象(iterable)。

参数: response (Response 对象) – 生成该输出的response

        result (包含 Request 或 Item 对象的可迭代对象(iterable)) – spider返回的result

        spider (Spider 对象) – 其结果被处理的spider

process_spider_exception(response, exception, spider) 当spider或(其他spider中间件的) process_spider_input() 跑出异常时, 该方法被调用。

process_spider_exception() 必须要么返回 None , 要么返回一个包含 Response 或 Item 对象的可迭代对象(iterable)。

如果其返回 None ,Scrapy将继续处理该异常,调用中间件链中的其他中间件的 process_spider_exception() 方法,直到所有中间件都被调用,该异常到达引擎(异常将被记录并被忽略)。

如果其返回一个可迭代对象,则中间件链的 process_spider_output() 方法被调用, 其他的 process_spider_exception() 将不会被调用。

参数: response (Response 对象) – 异常被抛出时被处理的response

         exception (Exception 对象) – 被跑出的异常

         spider (Spider 对象) – 抛出该异常的spider

process_start_requests(start_requests, spider) 0.15 新版功能.

该方法以spider 启动的request为参数被调用,执行的过程类似于 process_spider_output() ,只不过其没有相关联的response并且必须返回request(不是item)。

其接受一个可迭代的对象(start_requests 参数)且必须返回另一个包含 Request 对象的可迭代对象。

注解

当在您的spider中间件实现该方法时, 您必须返回一个可迭代对象(类似于参数start_requests)且不要遍历所有的 start_requests。 该迭代器会很大(甚至是无限),进而导致内存溢出。 Scrapy引擎在其具有能力处理start request时将会拉起request, 因此start request迭代器会变得无限,而由其他参数来停止spider( 例如时间限制或者item/page记数)。

参数: start_requests (包含 Request 的可迭代对象) – start requests spider (Spider 对象) – start requests所属的spider

 

4.6 ItemPipeline的使用

  • 当Item在Spider中被收集之后,它将会被传递到Item Pipeline,一些组件会按照一定的顺序执行对Item的处理。

  • 每个item pipeline组件(有时称之为“Item Pipeline”)是实现了简单方法的Python类。他们接收到Item并通过它执行一些行为,同时也决定此Item是否继续通过pipeline,或是被丢弃而不再进行处理。

  • 以下是item pipeline的一些典型应用:

    • 清理HTML数据
    • 验证爬取的数据(检查item包含某些字段)
    • 查重(并丢弃)
    • 将爬取结果保存到数据库中

如何编写你自己的item pipeline

  • 编写你自己的item pipeline很简单,每个item pipiline组件是一个独立的Python类,同时必须实现以下方法:

process_item(item, spider)

  • 每个item pipeline组件都需要调用该方法,这个方法必须返回一个 Item (或任何继承类)对象, 或是抛出 DropItem 异常,被丢弃的item将不会被之后的pipeline组件所处理。
  • 参数:

    • item (Item 对象) – 被爬取的item
    • spider (Spider 对象) – 爬取该item的spider
  • 此外,他们也可以实现以下方法:

open_spider(spider)

  • 当spider被开启时,这个方法被调用。
  • 参数: spider (Spider 对象) – 被开启的spider

close_spider(spider)

  • 当spider被关闭时,这个方法被调用
  • 参数: spider (Spider 对象) – 被关闭的spider

样例:

验证价格,同时丢弃没有价格的item

  • 让我们来看一下以下这个假设的pipeline,它为那些不含税(price_excludes_vat 属性)的item调整了 price 属性,同时丢弃了那些没有价格的item:
from scrapy.exceptions import DropItem

class PricePipeline(object):
    vat_factor = 1.15
    def process_item(self, item, spider):
        if item['price']:
            if item['price_excludes_vat']:
                item['price'] = item['price'] * self.vat_factor
            return item
        else:
            raise DropItem("Missing price in %s" % item)

将item写入JSON文件:

  • 以下pipeline将所有(从所有spider中)爬取到的item,存储到一个独立地 items.jl 文件,每行包含一个序列化为JSON格式的item:
import json

class JsonWriterPipeline(object):

    def __init__(self):
        self.file = open('items.jl', 'wb')

    def process_item(self, item, spider):
        line = json.dumps(dict(item)) + "\n"
        self.file.write(line)
        return item
  • 注解:JsonWriterPipeline的目的只是为了介绍怎样编写item pipeline,如果你想要将所有爬取的item都保存到同一个JSON文件, 你需要使用 Feed exports 。

去重

  • 一个用于去重的过滤器,丢弃那些已经被处理过的item。让我们假设我们的item有一个唯一的id,但是我们spider返回的多个item中包含有相同的id:
from scrapy.exceptions import DropItem

class DuplicatesPipeline(object):

    def __init__(self):
        self.ids_seen = set()

    def process_item(self, item, spider):
        if item['id'] in self.ids_seen:
            raise DropItem("Duplicate item found: %s" % item)
        else:
            self.ids_seen.add(item['id'])
            return item

启用一个Item Pipeline组件:

  • 为了启用一个Item Pipeline组件,你必须将它的类添加到 ITEM_PIPELINES 配置,就像下面这个例子:
  • 分配给每个类的整型值,确定了他们运行的顺序,item按数字从低到高的顺序,通过pipeline,通常将这些数字定义在0-1000范围内。
ITEM_PIPELINES = {
    'myproject.pipelines.PricePipeline': 300,
    'myproject.pipelines.JsonWriterPipeline': 800,
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章