目錄
一、基本框架介紹
二、安裝與基本命令
三、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爬取豆瓣圖書Top250信息
-
使用shell命令直接爬取報403錯誤
# 在命令行下直接運行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,
}