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,越小表示执行的优先权越大。

这样整份工作就完成了。

猜你喜欢

转载自blog.csdn.net/f156207495/article/details/81050064