Python版本管理:pyenv和pyenv-virtualenv
Scrapy爬蟲入門教程一 安裝和基本使用
Scrapy爬蟲入門教程二 官方提供Demo
Scrapy爬蟲入門教程三 命令行工具介紹和示例
Scrapy爬蟲入門教程四 Spider(爬蟲)
Scrapy爬蟲入門教程五 Selectors(選擇器)
Scrapy爬蟲入門教程六 Items(項(xiàng)目)
Scrapy爬蟲入門教程七 Item Loaders(項(xiàng)目加載器)
Scrapy爬蟲入門教程八 交互式 shell 方便調(diào)試
Scrapy爬蟲入門教程九 Item Pipeline(項(xiàng)目管道)
Scrapy爬蟲入門教程十 Feed exports(導(dǎo)出文件)
Scrapy爬蟲入門教程十一 Request和Response(請(qǐng)求和響應(yīng))
Scrapy爬蟲入門教程十二 Link Extractors(鏈接提取器)
開發(fā)環(huán)境:
Python 3.6.0 版本
(當(dāng)前最新)
Scrapy 1.3.2 版本
(當(dāng)前最新)
Item Pipeline(項(xiàng)目管道)
在項(xiàng)目被蜘蛛抓取后,它被發(fā)送到項(xiàng)目管道,它通過(guò)順序執(zhí)行的幾個(gè)組件來(lái)處理它。
每個(gè)項(xiàng)目管道組件(有時(shí)稱為“Item Pipeline”)是一個(gè)實(shí)現(xiàn)簡(jiǎn)單方法的Python類。他們接收一個(gè)項(xiàng)目并對(duì)其執(zhí)行操作,還決定該項(xiàng)目是否應(yīng)該繼續(xù)通過(guò)流水線或被丟棄并且不再被處理。
項(xiàng)目管道的典型用途是:
- 清理HTML數(shù)據(jù)
- 驗(yàn)證抓取的數(shù)據(jù)(檢查項(xiàng)目是否包含特定字段)
- 檢查重復(fù)(并刪除)
- 將刮取的項(xiàng)目存儲(chǔ)在數(shù)據(jù)庫(kù)中
編寫自己的項(xiàng)目管道
每個(gè)項(xiàng)目管道組件是一個(gè)Python類,必須實(shí)現(xiàn)以下方法:
process_item(self, item, spider)
對(duì)于每個(gè)項(xiàng)目管道組件調(diào)用此方法。process_item() 必須:返回一個(gè)帶數(shù)據(jù)的dict,返回一個(gè)Item (或任何后代類)對(duì)象,返回一個(gè)Twisted Deferred或者raise DropItemexception。丟棄的項(xiàng)目不再由其他管道組件處理。
參數(shù):
- item(Itemobject或dict) - 剪切的項(xiàng)目
- Spider(Spider對(duì)象) - 抓取物品的蜘蛛
另外,它們還可以實(shí)現(xiàn)以下方法:
open_spider(self, spider)
當(dāng)蜘蛛打開時(shí)調(diào)用此方法。
參數(shù):
- 蜘蛛(Spider對(duì)象) - 打開的蜘蛛
close_spider(self, spider)
當(dāng)蜘蛛關(guān)閉時(shí)調(diào)用此方法。
參數(shù):
- 蜘蛛(Spider對(duì)象) - 被關(guān)閉的蜘蛛
from_crawler(cls, crawler)
如果存在,則調(diào)用此類方法以從a創(chuàng)建流水線實(shí)例Crawler。它必須返回管道的新實(shí)例。Crawler對(duì)象提供對(duì)所有Scrapy核心組件(如設(shè)置和信號(hào))的訪問(wèn); 它是管道訪問(wèn)它們并將其功能掛鉤到Scrapy中的一種方式。
參數(shù):
- crawler(Crawlerobject) - 使用此管道的crawler
項(xiàng)目管道示例
價(jià)格驗(yàn)證和丟棄項(xiàng)目沒(méi)有價(jià)格
讓我們來(lái)看看以下假設(shè)的管道,它調(diào)整 price那些不包括增值稅(price_excludes_vat屬性)的項(xiàng)目的屬性,并刪除那些不包含價(jià)格的項(xiàng)目:
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)
將項(xiàng)目寫入JSON文件
以下管道將所有抓取的項(xiàng)目(來(lái)自所有蜘蛛)存儲(chǔ)到單個(gè)items.jl文件中,每行包含一個(gè)項(xiàng)目,以JSON格式序列化:
import json
class JsonWriterPipeline(object):
def open_spider(self, spider):
self.file = open('items.jl', 'wb')
def close_spider(self, spider):
self.file.close()
def process_item(self, item, spider):
line = json.dumps(dict(item)) + "\n"
self.file.write(line)
return item
注意
JsonWriterPipeline的目的只是介紹如何編寫項(xiàng)目管道。如果您真的想要將所有抓取的項(xiàng)目存儲(chǔ)到JSON文件中,則應(yīng)使用Feed導(dǎo)出。
將項(xiàng)目寫入MongoDB
在這個(gè)例子中,我們使用pymongo將項(xiàng)目寫入MongoDB。MongoDB地址和數(shù)據(jù)庫(kù)名稱在Scrapy設(shè)置中指定; MongoDB集合以item類命名。
這個(gè)例子的要點(diǎn)是顯示如何使用from_crawler()
方法和如何正確清理資源:
import pymongo
class MongoPipeline(object):
collection_name = 'scrapy_items'
def __init__(self, mongo_uri, mongo_db):
self.mongo_uri = mongo_uri
self.mongo_db = mongo_db
@classmethod
def from_crawler(cls, crawler):
return cls(
mongo_uri=crawler.settings.get('MONGO_URI'),
mongo_db=crawler.settings.get('MONGO_DATABASE', 'items')
)
def open_spider(self, spider):
self.client = pymongo.MongoClient(self.mongo_uri)
self.db = self.client[self.mongo_db]
def close_spider(self, spider):
self.client.close()
def process_item(self, item, spider):
self.db[self.collection_name].insert(dict(item))
return item
拍攝項(xiàng)目的屏幕截圖
此示例演示如何從方法返回Deferredprocess_item()。它使用Splash來(lái)呈現(xiàn)項(xiàng)目網(wǎng)址的屏幕截圖。Pipeline請(qǐng)求本地運(yùn)行的Splash實(shí)例。在請(qǐng)求被下載并且Deferred回調(diào)觸發(fā)后,它將項(xiàng)目保存到一個(gè)文件并將文件名添加到項(xiàng)目。
import scrapy
import hashlib
from urllib.parse import quote
class ScreenshotPipeline(object):
"""Pipeline that uses Splash to render screenshot of
every Scrapy item."""
SPLASH_URL = "http://localhost:8050/render.png?url={}"
def process_item(self, item, spider):
encoded_item_url = quote(item["url"])
screenshot_url = self.SPLASH_URL.format(encoded_item_url)
request = scrapy.Request(screenshot_url)
dfd = spider.crawler.engine.download(request, spider)
dfd.addBoth(self.return_item, item)
return dfd
def return_item(self, response, item):
if response.status != 200:
# Error happened, return item.
return item
# Save screenshot to file, filename will be hash of url.
url = item["url"]
url_hash = hashlib.md5(url.encode("utf8")).hexdigest()
filename = "{}.png".format(url_hash)
with open(filename, "wb") as f:
f.write(response.body)
# Store filename in item.
item["screenshot_filename"] = filename
return item
復(fù)制過(guò)濾器
用于查找重復(fù)項(xiàng)目并刪除已處理的項(xiàng)目的過(guò)濾器。假設(shè)我們的項(xiàng)目具有唯一的ID,但是我們的蜘蛛會(huì)返回具有相同id的多個(gè)項(xiàng)目:
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
激活項(xiàng)目管道組件
要激活項(xiàng)目管道組件,必須將其類添加到 ITEM_PIPELINES設(shè)置,類似于以下示例:
ITEM_PIPELINES = {
'myproject.pipelines.PricePipeline': 300,
'myproject.pipelines.JsonWriterPipeline': 800,
}
您在此設(shè)置中分配給類的整數(shù)值確定它們運(yùn)行的??順序:項(xiàng)目從較低值到較高值類。通常將這些數(shù)字定義在0-1000范圍內(nèi)。