1.spider開發(fā)流程:
- 最簡單的Spider只需4個步驟:
1).繼承scrapy.Spider派诬;
2).為Spider取名盟萨;
3).設置爬取的起始點吝羞;
4).實現(xiàn)頁面解析函數(shù)兰伤。
其中,Spider是一個基類钧排,后面我們使用到的所有其他爬蟲都需要繼承這個Spider基類敦腔,例如:CrawlSpider,XMLFeedSpider恨溜,CSVFeedSpider符衔,SitemapSpider等找前,這些類全部位于scrapy\spiders目錄之下。
實際上設置完爬取起始點后判族,默認由start_reqeusts()方法構建Request對象躺盛,然后默認指定由parse方法作為頁面解析函數(shù)。如果我們希望為Request添加特定的請求頭部或想為Request指定特定的頁面解析函數(shù)形帮,可以考慮在構建的Spider類中實現(xiàn)start_requests方法槽惫,即可覆蓋基類Spider的start_requests方法。例如辩撑,在第一章的基礎上進行修改:
import scrapy
class Books(scrapy.Spider):
name = 'books'
#start_urls = ['http://books.toscrape.com/']
#實現(xiàn)start_requests方法界斜,替代start_urls這個類屬性
def start_requests(self):
yield scrapy.Request(url="http://books.toscrape.com/",
callback=self.parse_book, #此時改用parse_book作為回調函數(shù)
headers={'User-Agent':'Mozilla/5.0'},
dont_filter=True)
def parse_book(self,response):
infos = response.xpath('//article')
for info in infos:
title = info.xpath("h3/a/@title").extract()[0]
price = info.xpath('div/p[@class="price_color"]/text()').extract()[0]
yield {'title': title, 'price': price}
所以,設置爬取的起爬點有兩種方法:
- 定義start_urls屬性
- 改寫start_requests方法
而第四個步驟合冀,頁面解析函數(shù)需要完成以下兩個工作:
1).提取數(shù)據(jù)各薇,將數(shù)據(jù)封裝后(Item或字典)提交給Scrapy引擎;
2).提取鏈接水慨,并用鏈接構造新的Request對象提交給Scrapy引擎得糜;其中,提取鏈接的方法包括使用選擇器或使用LinkExtractor晰洒。
2.常用方法
1)提取常用方法
.extract() 對結果以列表的形式進行返回
.extract_first() 對extract()返回的結果列表取第一個元素朝抖。
.re() #對結果使用正則表達式進行再提取
.re_first() #返回第一個re()結果。
2)調用selector的方法
selector類的實現(xiàn)位于scrapy.selector模塊谍珊,通過創(chuàng)建對象即可使用css或xpath解析方法治宣。
from scrapy.selector import Selector
class Book(scrapy.Spider):
...
def parse(self,response):
selector = Selector(response)
infos = selector.xpath("http://h1")
...
當然,實際開發(fā)中砌滞,我們無需創(chuàng)建Selector對象侮邀,因為當我們第一次訪問Response對象的selector屬性時,Response對象會自動創(chuàng)建Selector對象贝润,同時在Response對象中內置了selector對象的css和xpath方法以供使用绊茧。
class Book(scrapy.Spider):
...
def parse(self,response):
infos = response.xpath("http://h1")
3)使用Item封裝數(shù)據(jù)(items.py)
相對于使用字典來維護數(shù)據(jù)信息,使用item封裝數(shù)據(jù)打掘,有以下好處:
①清楚了解數(shù)據(jù)中包含哪些字段华畏;
②包含對字段名字的檢測;
③方便攜帶元數(shù)據(jù)尊蚁,用于傳遞給其他組件的信息亡笑;
- 數(shù)據(jù)段的基類:Item基類
- 描述數(shù)據(jù)包含哪些字段的類:FIeld類
在items.py中這樣寫:
from scrapy import Item,Field
class BooksItem(Item):
title = Field()
price = Field()
在project為books,spiders文件夾下的books.py下這樣寫:
from books.items import BooksItem #引入items.py中創(chuàng)建的對象
def parse_book(self,response):
infos = response.xpath('//article')
book = BooksItem() #實例化BooksItem()
for info in infos:
book['title'] = info.xpath("h3/a/@title").extract()[0]
book['price'] = info.xpath('div/p[@class="price_color"]/text()').extract()[0]
yield book #返回book
4)使用Item Pipeline處理數(shù)據(jù)(pipelines.py)
Item Pipeline的幾種典型應用:
- 清洗數(shù)據(jù)
- 驗證數(shù)據(jù)的有效性
- 過濾重復的數(shù)據(jù)
- 將數(shù)據(jù)存入數(shù)據(jù)庫
①Item Pipeline不需要繼承特定基類横朋,只需要實現(xiàn)特定方法仑乌,例如:process_item、open_spider、close_spider晰甚。
②一個Item Pipeline必須實現(xiàn)一個process_item(item,spider)方法衙传,該方法用來處理每一項由Spider爬取到的數(shù)據(jù),其中兩個參數(shù):
item: 爬取到的一項數(shù)據(jù)(Item或字典)
spider:爬取此項數(shù)據(jù)的Spider對象
例如將Sharp Objects,£47.82
中的英鎊轉換成人民幣Sharp Objects,¥406.47
压汪。
代碼為:
class PriceConverterPipeline(object):
exchange_rate = 8.5 #英鎊對人民幣匯率
def process_item(self, item, spider):
price = item['price'][1:] * self.exchange_rate
item['price'] = price
return item
寫入MongoDB的代碼粪牲,方式一:
import pymongo
class MongoDBPipeline(object):
def __init__(self):
client = pymongo.MongoClient('localhost',27017)
test = client['test']
book = test['book']
self.post = book
def process_item(self,item,spider):
info = dict(item)
self.post.insert(info)
return item
寫入MongoDB的代碼,方式二:
import pymongo
class MongoDBPipeline(object):
DB_URI = 'mongodb://localhost:27017/'
DB_NAME = 'test'
def open_spider(self,spider):
self.client = pymongo.MongoClient(self.DB_URI)
self.db = self.client[self.DB_NAME]
def close_spider(self,spider):
self.client.close()
def process_item(self, item, spider):
collection = self.db['book']
post = dict(item)
collection.insert_one(post)
return item
過濾重復數(shù)據(jù)止剖,這里以書名作為主鍵判斷重復項,實際上應該以ISBN編號為主鍵落君,只是前面僅爬取了書名和價格穿香。
from scrapy.exceptions import DropItem
class DuplicatesPipeline(object):
def __init__(self):
self.book_set = set()
def process_item(self,item,spider):
name = item['name']
if name in self.book_set:
raise DropItem('Duplicate book found:%s' %item)
self.book_set.add(name)
return item
由于Item Pipeline是可選的組件,想要啟用某個Item Pipeline绎速,需要在settings.py中可對Item Pipeline進行設置皮获。
例如:
ITEM_PIPELINES = {
'books.pipelines.PriceConverterPipeline': 300,
'books.pipelines.MongoDBPipeline': 500,
'books.pipelines.DuplicatesPipeline': 400,
}
其中,字典中的key為導入路徑纹冤,后面的value是0~1000的數(shù)字洒宝。如果同時啟動多個Pipeline,優(yōu)先處理數(shù)字最小的Pipeline萌京。
5)使用LinkExtractor提取鏈接
提取鏈接信息有兩種方法雁歌,簡單少量的鏈接使用Selector就足夠了,而對于大量的鏈接或者復雜規(guī)則的鏈接知残,使用LinkExtractor更方便靠瞎。
下面是代碼的比較:
- Selector()
next_url = response.xpath('//li[@class="next"]/a/@href').extract()[0]
if next_url:
next_url = response.urljoin(next_url)
yield scrapy.Request(next_url,callback=self.parse)
- LinkExtractor()
from scrapy.linkextractors import LinkExtractor
next = LinkExtractor(restrict_xpaths='//li[@class="next"]') #LinkExtractor中添加限制條件,如果為空會提取頁面的所有鏈接
links = next.extract_links(response) #返回一個Link對象的列表求妹,里面包含鏈接乏盐。
if links:
next_url = links[0].url #next_url[0]可獲取Link對象,Link對象的url屬性就是絕對地址制恍,無需自己構建相對地址父能。
yield scrapy.Request(next_url,callback=self.parse)
6)使用Exporter導出數(shù)據(jù)(settings.py)
- 可以使用命令行參數(shù)指定
- 通過配置文件指定
命令行: scrapy crawl -o books.csv
scrapy crawl -o books.csv -t csv
## -t可以省略
配置文件:
選項 | 含義 | 示例 |
---|---|---|
FEED_URI | 導出文件路徑 | FEED_URI = 'books.csv' |
FEED_FORMAT | 導出數(shù)據(jù)格式 | FEED_FORMAT = 'csv' |
FEED_EXPORT_ENCODING | 導出文件編碼方式 | FEED_EXPORT_ENCODING='gbk' |
FEED_EXPORT_FIELDS | 指定導出哪些字段并排序 | FEED_EXPORT_FIELDS={'title','price'} |
FEED_EXPORTERS | 用戶自定義Exporter字典,一般用于添加新的導出數(shù)據(jù)格式 | FEED_EXPORTERS ={‘excel’:'項目名.新設置的py文件名.ExcelItemExporter'} |