爬虫框架Scrapy(13)保存数据到数据库

news/2024/7/19 11:18:47 标签: python, 爬虫

文章目录

    • 保存数据到数据库
      • (一)数据保存至 MySQL 数据库
        • 1. 安装 pymysql
        • 2. 创建数据库与表
        • 3. 实现 MySQLPipeline
      • (二)数据保存至 MongoDB 数据库
        • 1. 安装 pymongo
        • 2. 实现 MongoDBPipeline
      • (三)数据保存至 Redis 数据库
        • 1. 安装 redis
        • 2. 实现 RedisPipeline
      • (四)项目实例——爬取360图片保存到数据库
        • 1. 将数据保存到 MySQL 数据库
        • 2. 将数据保存到 MongoDB 数据库
        • 3. 运行爬虫

保存数据到数据库

在之前的章节中,曾讨论过将爬取到的数据导出到文件的相关话题,但在某些时候,我们希望将爬取到的数据存储到数据库中,这一章来学习使用 Item Pipeline 实现 Scrapy 爬虫和几种常用数据库的接口。

下面我们继续以爬取网站 [http://books.toscrape.com] 中的书籍信息为例,其中每一本书的信息包括:书名、价格、评价等级、产品编码、是否有货以及图片地址。

现在我们在第七节中所创建的项目基础上进一步完善,学习如何在爬取数据的过程中将书籍信息存储到各种数据库,这些数据库主要有:MySQL、MongoDB、Redis。

(一)数据保存至 MySQL 数据库

MySQL是一个应用极其广泛的关系型数据库,它是开源免费的,可以支持大型数据库,在个人用户和中小企业中成为技术首选。

1. 安装 pymysql

在 Python 中可以使用第三方库 pymysql 访问 MySQL 数据库,使用 pip 安装 pymysql:

$ sudo pip install pymysql

2. 创建数据库与表

使用客户端登录MySQL,创建一个供 Scrapy 使用的数据库,取名为 scrapy_db:

$ mysql -uroot -pqwe123
... 
mysql> CREATE DATABASE scrapy_db CHARACTER SET 'utf8' COLLATE 'utf8_general_ci';
Query OK, 1 row affected (0.00 sec)
mysql> USE scrapy_db;
Database changed

接下来,创建存储书籍数据的表:

mysql> create table books(
    -> name VARCHAR(256) NOT NULL,
    -> price VARCHAR(20) NOT NULL,
    -> rate INT,
    -> availability VARCHAR(10),
    -> img_url VARCHAR(256)
    -> );
Query OK, 0 rows affected (0.01 sec)

3. 实现 MySQLPipeline

在项目文件夹下的 pipelines.py 中实现 MySQLPipeline,代码如下:

python">import pymysql

class MySQLPipeline():
    def open_spider(self, spider):
        host = spider.settings.get('MYSQL_HOST')
        port = spider.settings.get('MYSQL_PORT')
        database = spider.settings.get('MYSQL_DATABASE')
        user = spider.settings.get('MYSQL_USER')
        password = spider.settings.get('MYSQL_PASSWORD')
        self.db_connect = pymysql.connect(host=host, port=port, database=database, user=user, password=password, charset='utf8')
        self.cursor = self.db_connect.cursor()

    def close_spider(self, spider):
        self.db_connect.commit()
        self.db_connect.close()

    def process_item(self, item, spider):
        self.insert_db(item)
        return item

    def insert_db(self, item):
        values = (
            # item['upc'],
            item['name'],
            item['price'],
            item['rate'],
            item['availability'],
            item['img_url'],
        )
        sql = 'INSERT INTO books VALUES (%s,%s,%s,%s,%s)'
        self.cursor.execute(sql, values)
        print("数据保存成功!")

在配置文件 settings.py 中指定我们所要使用的 MySQL 数据库,并启用 MySQLPipeline:

python">ITEM_PIPELINES = {
   ...
   'scrapy_books.pipelines.MySQLPipeline': 600,
   ...
}
...

# MySQL数据库相关配置
MYSQL_HOST = '127.0.0.1'
MYSQL_DATABASE = 'scrapy_db'
MYSQL_PORT = 3306
MYSQL_USER = 'kevin'
MYSQL_PASSWORD = '20210401'

然后执行爬虫,程序运行结束后,我们就可以在数据库中看到保存的数据:
在这里插入图片描述

结果表明,我们成功地将1000条数据存储到了 MySQL 数据库。

上述代码中,同样是先执行全部的插入语句(INSERT INTO),最后一次性调用 commit 方法提交给数据库。或许在某些情况下,我们的确需要每执行一条插入语句,就立即调用 commit 方法更新数据库,如爬取过程很长,中途可能被迫中断,这样程序就不能执行到最后的 commit 。如果在上述代码的 insert_db 方法中直接添加 self.db_conn.commit() ,又会使程序执行慢得让人无法忍受。为解决以上难题,下面讲解另一种实现方法。

Scrapy 框架自身是使用另一个 Python 框架 Twisted 编写的程序,Twisted 是一个事件驱动型的异步网络框架,鼓励用户编写异步代码,Twisted 中提供了以异步方式多线程访问数据库的模块 adbapi,使用该模块可以显著提高程序访问数据库的效率。下面我们就以这个模块改写 MySQLPipeline

python">class MySQLAsyncPipeline():
    def open_spider(self, spider):
        host = spider.settings.get('MYSQL_HOST')
        port = spider.settings.get('MYSQL_PORT')
        database = spider.settings.get('MYSQL_DATABASE')
        user = spider.settings.get('MYSQL_USER')
        password = spider.settings.get('MYSQL_PASSWORD')
        self.dbpool = adbapi.ConnectionPool('pymysql', host=host, port=port,db=database, user=user, password=password, charset='utf8')

    def close_spider(self, spider):
        self.dbpool.close()

    def process_item(self, item, spider):
        self.dbpool.runInteraction(self.insert_db, item)
        return item

    def insert_db(self, tx, item):
        values = (
            # item['upc'],
            item['name'],
            item['price'],
            item['rate'],
            item['availability'],
            item['img_url'],
        )
        sql = 'INSERT INTO books VALUES (%s,%s,%s,%s,%s)'
        tx.execute(sql, values)
        print("数据保存成功!")

上述代码解释如下:

  • adbapi.ConnectionPool() 方法可以创建一个数据库连接池对象,其中包含多个连接对象,每个连接对象在独立的线程中工作。adbapi 只是提供了异步访问数据库的编程框架,在其内部依然使用 pymysql、sqlite3 这样的库访问数据库。 ConnectionPool() 方法的第一个参数就是用来指定使用哪个库访问数据库,其他参数在创建连接对象时使用。
  • dbpool.runInteraction(insert_db, item) 以异步方式调用 instert_db 函数,dbpool 会选择连接池中的一个连接对象在独立线程中调用 insert_db ,其中参数 item 会被传给 insert_db 的第二个参数,传给 insert_db 的第一个参数是一个 Transaction 对象,其接口与 Cursor 对象类似,可以调用 execute 方法执行 SQL 语句,insert_db 执行完后,连接对象会自动调用 commit 方法。

(二)数据保存至 MongoDB 数据库

MongoDB 是一个面向文档的非关系型数据库(NoSQL),它功能强大、灵活、易于拓展,近年来在多个领域得到广泛应用。

1. 安装 pymongo

在 Python 中可以使用第三方库 pymongo 访问 MongoDB 数据库,使用 pip 安装 pymongo:

$ sudo pip install pymongo

2. 实现 MongoDBPipeline

在项目文件夹下的 pipelines.py 中实现 MongoDBPipeline,代码如下:

python">from pymongo import MongoClient
from scrapy import Item

class MongoDBPipeline():
    def open_spider(self, spider):
        db_uri = spider.settings.get('MONGODB_URI')
        db_name = spider.settings.get('MONGODB_DB_NAME')
        self.db_client = MongoClient(db_uri)
        self.db = self.db_client[db_name] # 指定数据库

    def close_spider(self, spider):
        self.db_client.close()

    def process_item(self, item, spider):
        self.insert_db(item)
        return item

    def insert_db(self, item):
        if isinstance(item, Item):
            item = dict(item)
        self.db.books.insert_one(item)

在配置文件 settings.py 中指定我们所要使用的 MongoDB 数据库,并启用 MongoDBPipeline:

python">ITEM_PIPELINES = {
   ...
   'scrapy_books.pipelines.MongoDBPipeline': 700,
   ...
}
...

# MongoDB数据库相关配置
MONGODB_URI = 'mongodb://127.0.0.1:27017'
MONGODB_DB_NAME = 'scrapy_db'

然后执行爬虫,程序运行结束后,我们就可以在数据库中看到保存的数据:

(py3env) pyvip@VIP:~$ mongo
...
> show dbs
admin           0.000GB
config          0.000GB
local           0.000GB
scrapy_db       0.000GB
student_system  0.000GB
> use scrapy_db
switched to db scrapy_db
> show collections
books
> db.books.find()
{ "_id" : ObjectId("6087e1c2870db595ab60e942"), "name" : "A Light in the Attic", "price" : "¥441.64", "rate" : 3, "availability" : true, "img_url" : "http://books.toscrape.com/media/cache/2c/da/2cdad67c44b002e7ead0cc35693c0e8b.jpg" }
{ "_id" : ObjectId("6087e1c2870db595ab60e943"), "name" : "Tipping the Velvet", "price" : "¥458.45", "rate" : 1, "availability" : true, "img_url" : "http://books.toscrape.com/media/cache/26/0c/260c6ae16bce31c8f8c95daddd9f4a1c.jpg" }
{ "_id" : ObjectId("6087e1c2870db595ab60e944"), "name" : "Soumission", "price" : "¥427.40", "rate" : 1, "availability" : true, "img_url" : "http://books.toscrape.com/media/cache/3e/ef/3eef99c9d9adef34639f510662022830.jpg" }
{ "_id" : ObjectId("6087e1c2870db595ab60e945"), "name" : "Sharp Objects", "price" : "¥407.95", "rate" : 4, "availability" : true, "img_url" : "http://books.toscrape.com/media/cache/32/51/3251cf3a3412f53f339e42cac2134093.jpg" }
{ "_id" : ObjectId("6087e1c2870db595ab60e946"), "name" : "Sapiens: A Brief History of Humankind", "price" : "¥462.63", "rate" : 5, "availability" : true, "img_url" : "http://books.toscrape.com/media/cache/be/a5/bea5697f2534a2f86a3ef27b5a8c12a6.jpg" }
{ "_id" : ObjectId("6087e1c2870db595ab60e947"), "name" : "The Requiem Red", "price" : "¥193.22", "rate" : 1, "availability" : true, "img_url" : "http://books.toscrape.com/media/cache/68/33/68339b4c9bc034267e1da611ab3b34f8.jpg" }
{ "_id" : ObjectId("6087e1c2870db595ab60e948"), "name" : "The Dirty Little Secrets of Getting Your Dream Job", "price" : "¥284.42", "rate" : 4, "availability" : true, "img_url" : "http://books.toscrape.com/media/cache/92/27/92274a95b7c251fea59a2b8a78275ab4.jpg" }
{ "_id" : ObjectId("6087e1c2870db595ab60e949"), "name" : "The Coming Woman: A Novel Based on the Life of the Infamous Feminist, Victoria Woodhull", "price" : "¥152.96", "rate" : 3, "availability" : true, "img_url" : "http://books.toscrape.com/media/cache/3d/54/3d54940e57e662c4dd1f3ff00c78cc64.jpg" }
{ "_id" : ObjectId("6087e1c2870db595ab60e94a"), "name" : "The Boys in the Boat: Nine Americans and Their Epic Quest for Gold at the 1936 Berlin Olympics", "price" : "¥192.80", "rate" : 4, "availability" : true, "img_url" : "http://books.toscrape.com/media/cache/66/88/66883b91f6804b2323c8369331cb7dd1.jpg" }
{ "_id" : ObjectId("6087e1c2870db595ab60e94b"), "name" : "The Black Maria", "price" : "¥444.89", "rate" : 1, "availability" : true, "img_url" : "http://books.toscrape.com/media/cache/58/46/5846057e28022268153beff6d352b06c.jpg" }
{ "_id" : ObjectId("6087e1c2870db595ab60e94c"), "name" : "Starving Hearts (Triangular Trade Trilogy, #1)", "price" : "¥119.35", "rate" : 2, "availability" : true, "img_url" : "http://books.toscrape.com/media/cache/be/f4/bef44da28c98f905a3ebec0b87be8530.jpg" }
{ "_id" : ObjectId("6087e1c2870db595ab60e94d"), "name" : "Shakespeare's Sonnets", "price" : "¥176.25", "rate" : 4, "availability" : true, "img_url" : "http://books.toscrape.com/media/cache/10/48/1048f63d3b5061cd2f424d20b3f9b666.jpg" }
{ "_id" : ObjectId("6087e1c2870db595ab60e94e"), "name" : "Set Me Free", "price" : "¥148.95", "rate" : 5, "availability" : true, "img_url" : "http://books.toscrape.com/media/cache/5b/88/5b88c52633f53cacf162c15f4f823153.jpg" }
{ "_id" : ObjectId("6087e1c2870db595ab60e94f"), "name" : "Scott Pilgrim's Precious Little Life (Scott Pilgrim #1)", "price" : "¥446.08", "rate" : 5, "availability" : true, "img_url" : "http://books.toscrape.com/media/cache/94/b1/94b1b8b244bce9677c2f29ccc890d4d2.jpg" }
{ "_id" : ObjectId("6087e1c2870db595ab60e950"), "name" : "Rip it Up and Start Again", "price" : "¥298.75", "rate" : 5, "availability" : true, "img_url" : "http://books.toscrape.com/media/cache/81/c4/81c4a973364e17d01f217e1188253d5e.jpg" }
{ "_id" : ObjectId("6087e1c2870db595ab60e951"), "name" : "Our Band Could Be Your Life: Scenes from the American Indie Underground, 1981-1991", "price" : "¥488.39", "rate" : 3, "availability" : true, "img_url" : "http://books.toscrape.com/media/cache/54/60/54607fe8945897cdcced0044103b10b6.jpg" }
{ "_id" : ObjectId("6087e1c2870db595ab60e952"), "name" : "Olio", "price" : "¥203.72", "rate" : 1, "availability" : true, "img_url" : "http://books.toscrape.com/media/cache/55/33/553310a7162dfbc2c6d19a84da0df9e1.jpg" }
{ "_id" : ObjectId("6087e1c2870db595ab60e953"), "name" : "Mesaerion: The Best Science Fiction Stories 1800-1849", "price" : "¥320.68", "rate" : 1, "availability" : true, "img_url" : "http://books.toscrape.com/media/cache/09/a3/09a3aef48557576e1a85ba7efea8ecb7.jpg" }
{ "_id" : ObjectId("6087e1c2870db595ab60e954"), "name" : "Libertarianism for Beginners", "price" : "¥437.89", "rate" : 2, "availability" : true, "img_url" : "http://books.toscrape.com/media/cache/0b/bc/0bbcd0a6f4bcd81ccb1049a52736406e.jpg" }
{ "_id" : ObjectId("6087e1c2870db595ab60e955"), "name" : "It's Only the Himalayas", "price" : "¥385.34", "rate" : 2, "availability" : true, "img_url" : "http://books.toscrape.com/media/cache/27/a5/27a53d0bb95bdd88288eaf66c9230d7e.jpg" }
Type "it" for more

结果表明,我们成功地将 1000 条数据存储到了 MongoDB 数据库。

(三)数据保存至 Redis 数据库

Redis是一个使用ANSI C编写的高性能Key-Value数据库,使用内存作为主存储,内存中的数据也可以被持久化到硬盘。

1. 安装 redis

在 Python 中可以使用第三方库 redis-py 访问 Redis 数据库,使用 pip 安装 redis-py:

$ sudo pip install redis

2. 实现 RedisPipeline

在项目文件夹下的 pipelines.py 中实现 RedisPipeline,代码如下:

python">import redis
from scrapy import Item

class RedisPipeline:
    def open_spider(self, spider):
        db_host = spider.settings.get('REDIS_HOST')
        db_port = spider.settings.get('REDIS_PORT')
        db_index = spider.settings.get('REDIS_DB_INDEX')
        self.db_conn = redis.StrictRedis(host=db_host, port=db_port, db=db_index)
        self.item_i = 0

    def close_spider(self, spider):
        self.db_conn.connection_pool.disconnect()

    def process_item(self, item, spider):
        item['availability'] = str(item['availability'])
        self.insert_db(item)
        return item

    def insert_db(self, item):
        if isinstance(item, Item):
            item = dict(item)

        self.item_i += 1
        self.db_conn.hmset('book:%s' % self.item_i, item)

在配置文件 settings.py 中指定我们所要使用的 Redis 数据库,并启用 RedisPipeline:

python">ITEM_PIPELINES = {
   ...
   'scrapy_books.pipelines.RedisPipeline': 750,
   ...
}
...

# Redis数据库相关配置
REDIS_HOST = '127.0.0.1'
REDIS_PORT = 6379
REDIS_DB_INDEX = 0

然后执行爬虫,程序运行结束后,我们就可以在数据库中看到保存的数据:

(py3env) pyvip@VIP:~$ redis-cli
127.0.0.1:6379> select 0
OK
127.0.0.1:6379> KEYS book:*
  1) "book:280"
  2) "book:432"
  3) "book:313"
  4) "book:38"
  5) "book:542"
  6) "book:411"
  7) "book:459"
  8) "book:482"
  9) "book:864"
 10) "book:784"
...
990) "book:19"
991) "book:136"
992) "book:502"
993) "book:96"
994) "book:405"
995) "book:651"
996) "book:78"
997) "book:35"
998) "book:296"
999) "book:386"
127.0.0.1:6379> HGETALL book:1
 1) "name"
 2) "A Light in the Attic"
 3) "rate"
 4) "3"
 5) "price"
 6) "\xef\xbf\xa5441.64"
 7) "availability"
 8) "True"
 9) "img_url"
10) "http://books.toscrape.com/media/cache/2c/da/2cdad67c44b002e7ead0cc35693c0e8b.jpg"
127.0.0.1:6379> HGETALL book:2
 1) "name"
 2) "Tipping the Velvet"
 3) "rate"
 4) "1"
 5) "price"
 6) "\xef\xbf\xa5458.45"
 7) "availability"
 8) "True"
 9) "img_url"
10) "http://books.toscrape.com/media/cache/26/0c/260c6ae16bce31c8f8c95daddd9f4a1c.jpg"
127.0.0.1:6379> HGETALL book:999
 1) "name"
 2) "1,000 Places to See Before You Die"
 3) "rate"
 4) "5"
 5) "price"
 6) "\xef\xbf\xa5222.49"
 7) "availability"
 8) "True"
 9) "img_url"
10) "http://books.toscrape.com/media/cache/d7/0f/d70f7edd92705c45a82118c3ff6c299d.jpg"

结果表明,我们成功地将1000条数据存储到了 Redis 数据库。

(四)项目实例——爬取360图片保存到数据库

在前面的章节中,我们已经爬取360摄影美图并保存在了本地。下面我们在之前的基础上进一步将数据保存在数据库中。

1. 将数据保存到 MySQL 数据库

首先确保 MySQL 已经正确安装并且正常运行。新建一个数据库,名字还是 images360_db,SQL 语句如下所示:

$ mysql -uroot -pqwe123
...
mysql> CREATE DATABASE images360_db CHARSET=utf8;

新建一个数据表,包含 id、url、title、thumb 四个字段,SQL 语句如下所示:

mysql> CREATE TABLE images (
    -> id VARCHAR(255) NULL PRIMARY KEY,
    -> url VARCHAR(255) NULL,
    -> title VARCHAR(255) NULL,
    -> thumb VARCHAR(255) NULL
    -> );

执行完 SQL 语句之后,我们就成功创建好了数据表。接下来就可以往表里存储数据了。 接下来我们实现一个 MySQLPipeline,代码如下所示:

python">import pymysql

class MySQLPipeline():
    def open_spider(self, spider):
        host = spider.settings.get('MYSQL_HOST')
        port = spider.settings.get('MYSQL_PORT')
        database = spider.settings.get('MYSQL_DATABASE')
        user = spider.settings.get('MYSQL_USER')
        password = spider.settings.get('MYSQL_PASSWORD')
        self.db_connect = pymysql.connect(host=host, port=port, database=database, user=user, password=password, charset='utf8')
        self.cursor = self.db_connect.cursor()

    def close_spider(self, spider):
        self.db_connect.commit()
        self.db_connect.close()

    def process_item(self, item, spider):
        self.insert_db(item)
        return item

    def insert_db(self, item):
        values = (
            item['id'],
            item['url'],
            item['title'],
            item['thumb'],
        )
        sql = 'INSERT INTO images VALUES (%s,%s,%s,%s)'
        self.cursor.execute(sql, values)
        print("数据保存成功!")

这里我们还需要几个 MySQL 的配置,我们在 settings.py 里添加几个变量,并启动 MySQLPipeline ,如下所示:

python">ITEM_PIPELINES = {
   'image360.pipelines.ImagePipeline': 300,
   'image360.pipelines.MySQLPipeline': 350,
}

# MySQL数据库相关配置
MYSQL_HOST = '127.0.0.1'
MYSQL_DATABASE = 'images360_db'
MYSQL_PORT = 3306
MYSQL_USER = 'kevin'
MYSQL_PASSWORD = '20210401'

这里分别定义了 MySQL 的地址、数据库名称、端口、用户名、密码。 这样,MySQLPipeline 的配置就完成了。

2. 将数据保存到 MongoDB 数据库

首先确保 MongoDB 已经正常安装并且正常运行。我们用一个 MongoPipeline 将信息保存到 MongoDB,在 pipelines.py 里添加如下类的实现:

python">from pymongo import MongoClient
from scrapy import Item

class MongoDBPipeline(object):
    def open_spider(self, spider):
        db_uri = spider.settings.get('MONGO_URI')
        db_name = spider.settings.get('MONGO_DB_NAME')
        self.db_client = MongoClient(db_uri)   # 连接数据库
        self.db = self.db_client[db_name]      # 指定数据库

    def close_spider(self, spider):
        self.db_client.close()

    def process_item(self, item, spider):
        self.insert_db(item)
        return item

    def insert_db(self, item):
        if isinstance(item, Item):
            item = dict(item)
        self.db.images.insert_one(item)

这里需要用到两个变量,MONGO_URI 和 MONGO_DB,即存储到 MongoDB 的链接地址和数据库名称。我们在 settings.py 里添加这两个变量,并启动 MongoDBPipeline ,如下所示:

python">ITEM_PIPELINES = {
   'image360.pipelines.ImagePipeline': 300,
   'image360.pipelines.MySQLPipeline': 350,
   'image360.pipelines.MongoDBPipeline': 400,
}

# MongoDB数据库相关配置
MONGO_URI = 'mongodb://127.0.0.1:27017'
MONGO_DB_NAME = 'images360_db'

这样一个保存到 MongoDB 的 Pipeline 的就创建好了。这里最主要的方法是 process_item () 方法,直接调用 Collection 对象的 insert () 方法即可完成数据的插入,最后返回 Item 对象。

3. 运行爬虫

在与 scrapy.cfg 文件相同目录下,新建文件 bin.py ,编写如下内容:

python">from scrapy import cmdline
args = "scrapy crawl books".split()
cmdline.execute(args)

然后我们运行 bin.py 文件,执行爬虫

程序运行结束之后,我们可以在 MySQL 数据库和 MongoDB 数据库中保存的图片信息:
在这里插入图片描述
在这里插入图片描述

上述文章内容如有错误,欢迎各位读者在评论区留言!


http://www.niftyadmin.cn/n/1190186.html

相关文章

Sublime 个人常用快捷键

Sublime 个人常用快捷键 Hot Key Alt F3 选中文本所以有相同项;同多次Ctrl D Ctrl L 选中整行,继续按可继续选 Ctrl Shift M 选择括号内的内容;继续则选择父括号 Ctrl Shift Space 选择区域块内的内容;和Ctrl Shift M类似 Ctrl J 合并选中的多行代码为一行 Ctrl Ent…

利用java mail发送邮件(转)

JavaMail是SUN提供给开发者在应用程序中实现邮件发送和接收功能而提供的一套标准开发类库,支持经常使用的邮件协议,如SMTP、POP3、IMAP。开发者使用JavaMail编写邮件程序时,无需考虑底层的通信细节(Socket)。JavaMail也提供了可以创建出各种复…

Python网络爬虫系列文章导航(建议收藏)

下面是我所发布所有关于 Python 爬虫系列的文章导航,便于学习爬虫的同道进行浏览和学习。 爬虫基础 爬虫基础(1)什么是网络爬虫 爬虫基础(2)网络爬虫的实现原理与技术 爬虫基础(3)发送请求之u…

hadoop之shuffle详解

Shuffle描述着数据从map task输出到reduce task输入的这段过程。 如map 端的细节图,Shuffle在reduce端的过程也能用图上标明的三点来概括。当前reduce copy数据的前提是它要从JobTracker获得有哪些map task已执行结束,这段过程不表,有兴趣的…

经典sql 面试题

http://blog.csdn.net/hwq1987/article/details/6670300 一个表中的Id有多个记录,把所有这个id的记录查出来,并显示共有多少条记录数。------------------------------------------select id, Count(*) from tb group by id having count(*)&…

Java编程思想笔记-类的初始化顺序

1、如果有父类,先初始化父类,然后初始化子类 2、先初始化静态成员变量、静态代码块(static { }包围的代码),然后初始化非静态成员变量、非静态代码块(大括号包围的代码)。静态成员变量间、非静态…

软件测试流程及管理的几点思考

怎样判断软件产品的质量 --打脸了,有个模糊的概念,但是具体的细节讲不出来,靠feeling吧 软件的出口标准大致有以下几个: 总体而言: 1、指定的kpi达标: ~Must have功能全部实现并没有严重问题 ~better…

移动web资源整理

2013年初接触移动端,简单做下总结,首先了解下移动web带来的问题 设备更新换代快——低端机遗留下问题、高端机带来新挑战浏览器厂商不统一——兼容问题多网络更复杂——弱网络,页面打开慢低端机性能差——页面操作卡顿HTML5新技术多——学习成…