目標:
這裏是在入門進階(1)的代碼基礎,並需要滿足下面幾個條件
1. 爬取正確的數據
(1) 對爬取的數據進行格式轉換
(2) 根據自定義規則,拆分標題、章節數
2. 爬取的數據存入數據庫
(1) 新建數據庫
(2) settings.py中配置數據連接信息
(3) 引入已經寫好的mysql.py
(4) 判斷數據庫是否存在,避免爬取的數據重複
(5) 新數據存庫
一、使用mysql
1. 引入 mysql.py
我這裏直接引用別人寫好的 mysql.py 數據庫連接工具,也可以自己手寫
"""
數據庫工具類
# """
import pymysql
import traceback
from DBUtils.PooledDB import PooledDB
from scrapy.utils.project import get_project_settings
class MysqlUtil(object):
# 獲取setting文件中的配置
settings = get_project_settings()
config = {
'host': settings.get('MYSQL_HOST'),
'port': settings.get('MYSQL_PORT'),
'database': settings.get('MYSQL_DATABASE'),
'user': settings.get('MYSQL_USER'),
'password': settings.get('MYSQL_PASSWORD'),
'charset': settings.get('MYSQL_CHARSET')
}
"""
MYSQL數據庫對象,負責產生數據庫連接 , 此類中的連接採用連接池實現獲取連接對象:conn = Mysql.getConn()
釋放連接對象;conn.close()或del conn
"""
# 連接池對象
__pool = None
def __init__(self):
# 數據庫構造函數,從連接池中取出連接,並生成操作遊標
self._conn = MysqlUtil.get_conn()
self._cursor = self._conn.cursor()
# 獲取鏈接
@staticmethod
def get_conn():
"""
@summary: 靜態方法,從連接池中取出連接
@return MySQLdb.connection
"""
if MysqlUtil.__pool is None:
__pool = PooledDB(creator=pymysql, mincached=1, maxcached=100, host=MysqlUtil.config['host'], port=MysqlUtil.config['port'], user=MysqlUtil.config['user'], passwd=MysqlUtil.config['password'], db=MysqlUtil.config['database'], charset='utf8')
return __pool.connection()
# 查詢所有數據
def get_all(self, sql, param=None):
"""
@summary: 執行查詢,並取出所有結果集
@param sql:查詢SQL,如果有查詢條件,請只指定條件列表,並將條件值使用參數[param]傳遞進來
@param param: 可選參數,條件列表值(元組/列表)
@return: result list(字典對象)/boolean 查詢到的結果集
"""
try:
if param is None:
count = self._cursor.execute(sql)
else:
count = self._cursor.execute(sql, param)
if count > 0:
result = self._cursor.fetchall()
else:
result = False
return result
except Exception as e:
traceback.print_exc(e)
# 查詢某一個數據
def get_one(self, sql, param=None):
"""
@summary: 執行查詢,並取出第一條
@param sql:查詢SQL,如果有查詢條件,請只指定條件列表,並將條件值使用參數[param]傳遞進來
@param param: 可選參數,條件列表值(元組/列表)
@return: result list/boolean 查詢到的結果集
"""
try:
if param is None:
count = self._cursor.execute(sql)
else:
count = self._cursor.execute(sql, param)
if count > 0:
result = self._cursor.fetchone()
else:
result = False
return result
except Exception as e:
traceback.print_exc(e)
# 查詢數量
def get_count(self, sql, param=None):
"""
@summary: 執行查詢,返回結果數
@param sql:查詢SQL,如果有查詢條件,請只指定條件列表,並將條件值使用參數[param]傳遞進來
@param param: 可選參數,條件列表值(元組/列表)
@return: result list/boolean 查詢到的結果集
"""
try:
if param is None:
count = self._cursor.execute(sql)
else:
count = self._cursor.execute(sql, param)
return count
except Exception as e:
traceback.print_exc(e)
# 查詢部分
def get_many(self, sql, num, param=None):
"""
@summary: 執行查詢,並取出num條結果
@param sql:查詢SQL,如果有查詢條件,請只指定條件列表,並將條件值使用參數[param]傳遞進來
@param num:取得的結果條數
@param param: 可選參數,條件列表值(元組/列表)
@return: result list/boolean 查詢到的結果集
"""
try:
if param is None:
count = self._cursor.execute(sql)
else:
count = self._cursor.execute(sql, param)
if count > 0:
result = self._cursor.fetchmany(num)
else:
result = False
return result
except Exception as e:
traceback.print_exc(e)
# 插入一條數據
def insert_one(self, sql, value):
"""
@summary: 向數據表插入一條記錄
@param sql:要插入的SQL格式
@param value:要插入的記錄數據tuple/list
@return: insertId 受影響的行數
"""
try:
row_count = self._cursor.execute(sql, value)
self.end("commit")
return row_count
except Exception as e:
traceback.print_exc(e)
self.end("rollback")
# 插入多條數據
def insert_many(self, sql, values):
"""
@summary: 向數據表插入多條記錄
@param sql:要插入的SQL格式
@param values:要插入的記錄數據tuple(tuple)/list[list]
@return: count 受影響的行數
"""
try:
row_count = self._cursor.executemany(sql, values)
self.end("commit")
return row_count
except Exception as e:
traceback.print_exc(e)
self.end("rollback")
# def __get_insert_id(self):
# """
# 獲取當前連接最後一次插入操作生成的id,如果沒有則爲0
# """
# self._cursor.execute("SELECT @@IDENTITY AS id")
# result = self._cursor.fetchall()
# return result[0]['id']
# 執行sql
def __query(self, sql, param=None):
try:
if param is None:
count = self._cursor.execute(sql)
else:
count = self._cursor.execute(sql, param)
self.end("commit")
return count
except Exception as e:
traceback.print_exc(e)
self.end("rollback")
# 更新
def update(self, sql, param=None):
"""
@summary: 更新數據表記錄
@param sql: SQL格式及條件,使用(%s,%s)
@param param: 要更新的 值 tuple/list
@return: count 受影響的行數
"""
return self.__query(sql, param)
# 刪除
def delete(self, sql, param=None):
"""
@summary: 刪除數據表記錄
@param sql: SQL格式及條件,使用(%s,%s)
@param param: 要刪除的條件 值 tuple/list
@return: count 受影響的行數
"""
return self.__query(sql, param)
def begin(self):
"""
@summary: 開啓事務
"""
self._conn.autocommit(0)
def end(self, option='commit'):
"""
@summary: 結束事務
"""
if option == 'commit':
self._conn.commit()
else:
self._conn.rollback()
def dispose(self, is_end=1):
"""
@summary: 釋放連接池資源
"""
if is_end == 1:
self.end('commit')
else:
self.end('rollback')
self._cursor.close()
self._conn.close()
2. 新建數據庫
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for fiction
-- ----------------------------
DROP TABLE IF EXISTS `fiction`;
CREATE TABLE `fiction` (
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主鍵id',
`chapter` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '小說章節數',
`title` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '小說標題',
`content` longtext CHARACTER SET utf8 COLLATE utf8_general_ci COMMENT '小說內容',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 35 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
SET FOREIGN_KEY_CHECKS = 1;
3. 配置數據庫連接
在settings.py 中添加下面的數據庫連接配置
# MYSQL SETTINGS
MYSQL_HOST = '127.0.0.1'
MYSQL_USER = 'root'
MYSQL_PASSWORD = '123456'
MYSQL_DATABASE = 'mypython'
MYSQL_PORT = 3306
4. 對代碼進行編寫
根據頁面分析,小說頁面的末端存在【下一頁】,根據爬蟲自動觸發 不斷的循環爬取小說
// 這裏爬取了 小說的:章節數、標題、內容
由於 章節數、標題 在同一個div中,所以需要自定義規則進行拆分
先根據章節數,從數據庫查詢是否存在該小說,如果不存在,就對提取出來的數據進行存庫;如果存在,則不做操作
import scrapy
# 引入mysql工具類
from ..mysql import MysqlUtil
# 運行scrapy項目的命令: scrapy crawl test
class myclass(scrapy.Spider):
# 爬蟲名
name ='test'
# 爬取的鏈接
start_urls = ["http://book.zongheng.com/chapter/885037/58155562.html"]
def __init__(self):
self.pool = MysqlUtil()
def parse(self, response):
# 標題章節div(包括章節數、本章名稱)
title_1 = response.xpath("//div[@class='title_txtbox']/text()").extract()[0]
# 1.提取標題,章節數以外的文字
if "章" in str(title_1):
title = str(title_1)[str(title_1).index("章") + 1:]
else:
title = str(title_1)
# 2.提取章節數,根據"章"字對標題進行截取
if "章" in str(title_1):
chapter = str(title_1)[0:str(title_1).index("章") + 1]
else:
chapter = "-"
# 3.內容 content
content_1 = response.xpath("//div[@class='content']//text()").extract()
# 數組轉字符串: String = "".join(arrary)
# 字符串清除空格: String.strip()
# 數組轉字符串,並清空內容裏的空格
content = "".join(content_1).strip()
# 數據入庫
selectSql = """select * from fiction where chapter = %s """
params1 = (chapter)
returnData = self.pool.get_one(selectSql, params1)
if returnData == False:
insertSql = """insert into fiction(chapter, title, content)values(%s, %s, %s)"""
params = (chapter, title, content)
self.pool.insert_one(insertSql, params)
# 下一頁
link = response.xpath("//div[@class='chap_btnbox']//a[@class='nextchapter']/@href").extract()[0]
print(link)
# 重複循環爬取
yield scrapy.Request(link, callback=self.parse, dont_filter=True)
4. 啓動scrapy爬蟲項目
# scrapy crawl 爬蟲名
scrapy crawl test
慢慢等着,然後數據庫就開始進數據了~~~