scrapy第一发——基础巩固
目标:爬取
http://books.toscrape.com"
将该网站上50页,每页20本的书的价格、等级、书名、书的ISBN号等爬取下来并保存在mongoDB数据库里面。
该网站如图:
首先建立一个爬虫项目,新建一个spider文件,name取值为"book"
class BookSpider(scrapy.Spider): name = 'book'
整个项目的文件结构如下
重写start_requests函数
def start_requests(self): url = "http://books.toscrape.com" yield Request(url, self.parse_urls)
再写一个callback函数——parse_urls,用于接受处理start_requests里面发出的Request请求所得到的response。该函数主要有两个功能,第一个功能是解析出“下一页”的url,第二个功能是解析出“每一页上20本书”的url;并对这些url发出请求。
来看一看这个函数怎么写
self.parse_urls:
def parse_urls(self , response): le = LinkExtractor(restrict_css="ul.pager li.next") leBook = LinkExtractor(restrict_css="ol.row div.image_container") next = le.extract_links(response) bookLinks = leBook.extract_links(response) # 解析每一页上20本书的links if bookLinks: for link in bookLinks: yield Request(link.url, self.parse_book) # 解析下一页的links if next: link = next[0].url yield Request(link, self.parse_urls)
这里用到了LIinkExtractor,使用LinkExtractor提取出url还是很方便的。具体怎么用就不讲了。
可以看到,解析出下一页的url后,又将callback设置为self.parse_urls,继续使用该函数用来处理“下一页”;用self.parse_book函数处理每一个书本的内容页。
self.parse_book:
# 挖掘每一本书的信息 def parse_book(self , response): sel = response.css('div.product_main') title = sel.xpath('./h1/text()').extract_first()# 得到书名 price = sel.css('p.price_color::text').extract_first() # 得到书的价格 rank = sel.css('p.star-rating::attr(class)').re_first('star-rating ([A-Za-z]+)') # 得到书的排名等级 sel = response.css('table.table.table-striped') ISBN = sel.xpath('(.//tr)[1]/td/text()').extract_first() # 得到书的ISBN号 surplus = sel.xpath('(.//tr)[last()-1]/td/text()').re_first('\((\d+) available\)') # 得到书的库存量 reviewers = sel.xpath('(.//tr)[last()]/td/text()').extract_first() # 得到书的reviewers数量 single = BooksItem() single['title'] = title single['price'] = price single['rank'] = rank single['ISBN'] = ISBN single['surplus'] = surplus single['reviewers'] = reviewers return single
self.parse_book里面使用到了BooksItem对象,BooksItem定义在items.py文件里面。
items.py
import scrapy from scrapy import Item , Field class BooksItem(Item): # define the fields for your item here like: # name = scrapy.Field() title = Field() price = Field() rank = Field() ISBN = Field() surplus = Field() reviewers = Field()
分别定义了书名、价格、等级、ISBN、剩余量、观看者这六个字段。
在scrapy可以使用Item来传递数据。
写到这里可以直接使用命令“scrapy crawl book -o fileName.csv”来运行该爬虫程序了,最后数据会保存在fileName.csv文件里面。
不过前面说好了要保存在mongoDB里面呢,咱们还得修改一下piplines.py文件。在保存在数据库之前,先进行一个处理——书本去重,这里使用书名作为去重标准。
在piplines.py文件里面实现去重类:DuplicatesPipeline
# 实现书本的去重 class DuplicatesPipeline(object): def __init__(self): self.book_set = set() def process_item(self, item, spider): title = item['title'] if title in self.book_set: raise DropItem("Duplicate book found: %s " % item) self.book_set.add(title) return item
这样就实现了书本去重,(注意这里的return item,它会将item数据返回给另外下一级的item pipeline继续处理,如果有的话)
去重了我还不满足,我还想将英镑转换成人民币,那么就再实现一个item pipeline——PriceConvertPipeline
# 实现书本的价格转换 class PriceConvertPipeline(object): exchange_rate = 8.5309 def process_item(self, item, spider): # 实现货币的汇率转换 price = float(item['price'][1:]) * self.exchange_rate item['price'] = '¥ %.2f' % price return item
然后我们再讲最后得到的item存入数据库里面
class MongoDBPipeline(object): @classmethod def from_crawler(cls, crawler): cls.DB_URI = crawler.settings.get('MONGO_DB_URI' , 'mongodb://localhost:27017/') cls.DB_NAME = crawler.settings.get('scrapy_data' , 'MONGO_DB_NAME') return cls() 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[spider.name] post = dict(item) if isinstance(item, Item) else item collection.insert_one(post) return item
(每实现一个item pipeline需要在settings.py里面加入该pipeline)如图:
ITEM_PIPELINES = { 'books.pipelines.DuplicatesPipeline' : 100, 'books.pipelines.PriceConvertPipeline' : 200, 'books.pipelines.BooksPipeline': 300, 'books.pipelines.MongoDBPipeline' : 400, }后面的键值取值为0~1000,越小表示执行的优先权越大。
这样整份工作就完成了。