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,
}

 

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