項(xiàng)目文檔:
項(xiàng)目簡介:
爬取某小說網(wǎng)首頁中的全部小說,并儲存到數(shù)據(jù)庫中
項(xiàng)目版本 :python2.7.12
項(xiàng)目源碼:
源碼已上傳 github: 源碼github
項(xiàng)目總覽:
1. 爬取小說首頁中全部小說url
2. 爬取小說詳情內(nèi)容蠢正,章節(jié)url
3. 爬取章節(jié)信息
項(xiàng)目流程:
1. 爬取首頁中的所有小說url
1. 獲得首頁html
2. 解析html
1) 獲得小說url
3. 儲存到數(shù)據(jù)庫
1) 儲存小說url
2. 爬取小說詳情內(nèi)容嚣崭,章節(jié)url
1. 獲得小說詳情html
2. 解析html
1) 獲得小說詳情信息
2) 獲得小說章節(jié)url
3. 儲存到數(shù)據(jù)庫
1) 儲存小說詳情信息
2) 儲存章節(jié)url
3. 爬取章節(jié)信息
1. 獲得章節(jié)html
2. 解析html
1) 獲得章節(jié)信息
3. 儲存到數(shù)據(jù)庫
1) 儲存章節(jié)信息
由上面流程可看出每個(gè)流程均可抽象為三個(gè)層次
1. 獲得html
2. 解析html
3. 存儲信息到數(shù)據(jù)庫
因此可以抽象出三個(gè)功能:下載功能雹舀,解析功能说榆,存儲功能
項(xiàng)目中異常的處理:
項(xiàng)目中主要是 url的下載签财,解析中可能會出現(xiàn)一些異常,我們定義 出現(xiàn)此類異常則跑出ValueError異常模庐,并跳過當(dāng)前url,進(jìn)行下一個(gè)url的爬取油宜,避免程序的崩潰
項(xiàng)目結(jié)構(gòu):
ESBook 項(xiàng)目根目錄
bin 保存二進(jìn)制可執(zhí)行文件
books 項(xiàng)目目錄 存放py文件
data 存放下載的小說封面
docs 存儲項(xiàng)目文檔 使用sphinx生成的項(xiàng)目文檔
tests 測試目錄
README 項(xiàng)目說明
requirements.txt 項(xiàng)目依賴第三方包
setup.py python 安裝腳本
Books目錄結(jié)構(gòu)
Callblack 業(yè)務(wù)邏輯掂碱,網(wǎng)頁的解析處理(這個(gè)就是抽象出來的解析功能)
Logs 項(xiàng)目日志
Util 通用性工具
Bookconfig.py 項(xiàng)目配置文件
link_crawler.py 鏈接爬取模塊
main.py 啟動入口模塊
uitl目錄結(jié)構(gòu):
bookDB 數(shù)據(jù)庫相關(guān)操作(抽象出來的存儲功能)
Downloaders 下載功能的簡單封裝(抽象出來的下載功能)
Logger 日志功能
Logging.conf 日志配置文件
mongoQueue 基于mongoDB的url隊(duì)列
Multiprocessloghandler 支持多進(jìn)程的TimedRotatingFileHandler
Tools 工具模塊
下面看一下項(xiàng)目配置文件Bookconfig.py
將項(xiàng)目配置統(tǒng)一到一個(gè)配置文件中,方便后續(xù)的更改
#coding=utf-8
"""
項(xiàng)目配置模塊
數(shù)據(jù)庫配置
mongodb數(shù)據(jù)庫連接的創(chuàng)建
urls_table 集合的創(chuàng)建以及索引 book_url 的創(chuàng)建
urls_table 小說url表單慎冤,儲存爬取的小說url
cha_urls_table 集合的創(chuàng)建以及索引 chapter_url的創(chuàng)建
cha_urls_table 章節(jié)url表單疼燥,儲存爬取的小說章節(jié)url
books_table 集合的創(chuàng)建以及索引 book_id 的創(chuàng)建
books_table 小說表單,儲存爬取的小說詳情內(nèi)容
chapters_table 集合的創(chuàng)建以及聯(lián)合索引 book_id -- chapter_id 的創(chuàng)建
chapters_table 章節(jié)表單蚁堤,儲存爬取的小說章節(jié)內(nèi)容
日志配置
consloe_logger 輸出日志到控制臺 info級別
error_logger 輸出日志到日志文件 和 控制臺 error級別
url狀態(tài) :數(shù)據(jù)庫集合中url的狀態(tài)
OUTSTANAING = 0 未處理
PROCESSING = 1 待處理
COMPLETE = 2 已處理
ERROR = 3 產(chǎn)生異常的url
"""
import pymongo
from books.util.logger import MyLogger
OUTSTANAING = 0 # 未處理
PROCESSING = 1 # 待處理
COMPLETE = 2 # 已處理
ERROR = 3 # 產(chǎn)生異常的url
#數(shù)據(jù)庫連接創(chuàng)建
client = pymongo.MongoClient('localhost',27017)
book_db = client['book']
# 小說url表單,儲存爬取的小說url
BOOK_URLS_TABLE = book_db['urls_table']
BOOK_URLS_FIELD = 'book_url'
BOOK_URLS_TABLE.create_index([(BOOK_URLS_FIELD,pymongo.DESCENDING)],unique=True)
# 章節(jié)url表單披诗,儲存爬取的小說章節(jié)url
CHA_URLS_TABLE = book_db['cha_urls_table']
CHA_URLS_FIELD = 'chapter_url'
CHA_URLS_TABLE.create_index([(CHA_URLS_FIELD,pymongo.DESCENDING)],unique=True)
# 小說表單撬即,儲存爬取的小說詳情內(nèi)容
BOOKS_TABLE = book_db['books_table']
BOOKS_FIELD = 'book_id'
BOOKS_TABLE.create_index([(BOOKS_FIELD,pymongo.DESCENDING)],unique=True)
# 章節(jié)表單,儲存爬取的小說章節(jié)信息
# 小說id呈队,章節(jié)id 聯(lián)合索引
CHAPTERS_TABLE = book_db['chapters_table']
CHAPTERS_FIELD = 'chapter_id'
# 復(fù)合唯一索引剥槐,由book_id -- chapter_id 確認(rèn)一條記錄的唯一性
CHAPTERS_TABLE.create_index([(BOOKS_FIELD, pymongo.DESCENDING), (CHAPTERS_FIELD, pymongo.DESCENDING)], unique=True)
# 日志
# consloe_logger 輸出日志到控制臺 info級別
# error_logger 輸出日志到日志文件 和 控制臺 error級別
CONSOLE_LOGGER = MyLogger.getLogger('consolelogger')
ERROR_LOGGER = MyLogger.getLogger('errorlogger')
我們在看一下 link_crawler.py 模塊
link_crawler.py 模塊是項(xiàng)目的主體模塊。
link_crawler.py 模塊主要分三部分
1. 小說首頁的爬取
2. 小說詳情頁的爬取
3. 小說章節(jié)頁的爬取
小說首頁的爬取
scrape_callblack即為解析小說首頁的具體實(shí)現(xiàn)
def book_crawler(seen_url,delay=5,num_retries=3,encoding='utf-8',headers=None,scrape_callblack=None,cache=None):
"""小說首頁爬取宪摧,獲得首頁中所有小說url粒竖,并存儲到urls隊(duì)列中
:param seen_url 小說主頁url
:param delay 請求間隔時(shí)間
:param num_retries 下載錯誤時(shí)重試次數(shù)
:param encoding 網(wǎng)頁編碼
:param headers HTTP請求頭信息
:param scrape_callblack 回調(diào)函數(shù),用于處理具體業(yè)務(wù)邏輯
:param cache 數(shù)據(jù)緩存
:raise ValueError url解析失敗則拋出
"""
# 待爬取URL
crawl_queue = seen_url if isinstance(seen_url,list) else [seen_url]
# urls隊(duì)列几于,存儲小說url
book_queue = MongoQueue(book_config.BOOK_URLS_TABLE,book_config.BOOK_URLS_FIELD)
# 下載功能
D = Downloader(delay=delay,num_retries=num_retries,headers=headers,encoding=encoding,cache=cache)
while crawl_queue:
try:
url = crawl_queue.pop()
try:
html = D(url)
# 解析功能--解析小說首頁蕊苗,返回首頁中的所有小說urls
if scrape_callblack:
links = scrape_callblack(url,html)
# 將小說url添加到urls隊(duì)列中
if links:
[book_queue.push(link) for link in links]
else:
#print '請?zhí)砑踊卣{(diào)處理函數(shù)'
break
except ValueError as e:
error_info = u'出現(xiàn)錯誤跳過當(dāng)前url 信息為:%s %s ' % (e.message, url)
conlose_logger.error(error_info)
except KeyError as e:
conlose_logger.info(u'小說主頁爬取已完成 ')
break
小說詳情頁的爬取
其中scrape_callblack為 解析小說詳情頁的具體實(shí)現(xiàn),
小說urls則直接從小說urls隊(duì)列中獲取沿彭。
def book_detail_crwler(delay=5,num_retries=3,encoding='utf-8',headers=None,scrape_callblack=None,cache=None):
"""小說詳情頁爬取,將小說詳情儲存到數(shù)據(jù)庫中朽砰,并將爬取到的章節(jié)url存儲到urls隊(duì)列中
:param delay 請求間隔時(shí)間
:param num_retries 下載錯誤時(shí)重試次數(shù)
:param encoding 網(wǎng)頁編碼
:param headers HTTP請求頭信息
:param scrape_callblack 回調(diào)函數(shù),用于處理具體業(yè)務(wù)邏輯
:param cache 數(shù)據(jù)緩存
:raise ValueError 詳情頁解析失敗則拋出
"""
#小說url隊(duì)列,獲得小說url
book_queue = MongoQueue(book_config.BOOK_URLS_TABLE,book_config.BOOK_URLS_FIELD)
#章節(jié)url隊(duì)列锅移,儲存爬取的章節(jié)url
chap_queue = MongoQueue(book_config.CHA_URLS_TABLE,book_config.CHA_URLS_FIELD)
D = Downloader(delay=delay, num_retries=num_retries, headers=headers, encoding=encoding, cache=cache)
while True:
try:
url = book_queue.pop()
try:
html = D(url)
# 解析功能--解析小說詳情頁并將小說詳情儲存到數(shù)據(jù)庫中熔掺,返回其中的所有小說章節(jié)urls
if scrape_callblack:
chap_links = scrape_callblack(url,html)
# 將章節(jié)url添加到urls隊(duì)列中
[chap_queue.push(link) for link in chap_links]
else:
#print '請?zhí)砑踊卣{(diào)處理函數(shù)'
break
except ValueError as e:
# 設(shè)置當(dāng)前url狀態(tài)為 ERROR
book_queue.set_error(url)
error_info = u'出現(xiàn)錯誤跳過當(dāng)前url %s 信息為:%s ' % (url,e.message)
error_logger.error(error_info)
except KeyError as e:
#print '處理完成',e
conlose_logger.info(u'小說鏈接爬取已完成 ')
break
else:
#正常處理完成,設(shè)置url狀態(tài)為 已處理
book_queue.complete(url)
小說章節(jié)頁的爬取
scrape_callblack為解析章節(jié)頁面的具體實(shí)現(xiàn)
def chpater_crwler(delay=5, num_retries=3, encoding='utf-8', headers=None, scrape_callblack=None, cache=None):
"""章節(jié)信息爬取非剃,并儲存到數(shù)據(jù)庫
:param delay 請求間隔時(shí)間
:param num_retries 下載錯誤時(shí)重試次數(shù)
:param encoding 網(wǎng)頁編碼
:param headers HTTP請求頭信息
:param scrape_callblack 回調(diào)函數(shù),用于處理具體業(yè)務(wù)邏輯
:param cache 數(shù)據(jù)緩存
:raise ValueError 章節(jié)頁解析失敗則拋出
"""
# 章節(jié)urls隊(duì)列
chap_queue = MongoQueue(book_config.CHA_URLS_TABLE, book_config.CHA_URLS_FIELD)
D = Downloader(delay=delay, num_retries=num_retries, headers=headers, encoding=encoding, cache=cache)
while True:
try:
url = chap_queue.pop()
try:
html = D(url)
# 解析功能--解析小說章節(jié)頁推沸,獲得章節(jié)內(nèi)容备绽,并儲存到數(shù)據(jù)庫中
if scrape_callblack:
scrape_callblack(url,html)
else:
#print '請?zhí)砑踊卣{(diào)函數(shù)'
break
except ValueError as e:
# 設(shè)置當(dāng)前url狀態(tài)為 ERROR
chap_queue.set_error(url)
error_info = u'出現(xiàn)錯誤跳過當(dāng)前url %s 信息為:%s ' % (url,e.message)
error_logger.error(error_info)
except KeyError as e:
conlose_logger.info(u'小說鏈接爬取已完成 ')
break
else:
# 正常處理完成,設(shè)置url狀態(tài)為 已處理
chap_queue.complete(url)
main.py 模塊為 程序入口鬓催,也是link_crawler.py的多進(jìn)程版
book_detail_crawler()與chpater_crwler()是對小說詳情頁爬取與章節(jié)頁爬取功能的包裝肺素,方便使用多進(jìn)程時(shí)候的調(diào)用。
replace_img()
用來替換不正常的封面圖片為默認(rèn)封面圖片
def book_detail_crawler():
"""小說詳情頁爬取"""
link_crawler.book_detail_crwler(headers={}, scrape_callblack=BookDetailCallblack())
def chpater_crwler():
"""章節(jié)頁爬取"""
link_crawler.chpater_crwler(headers={}, scrape_callblack=ChapterSpiderCallblack())
def process_crawler(func):
"""啟動進(jìn)程"""
pool = multiprocessing.Pool(processes=8)
for i in range(8):
pool.apply_async(func)
pool.close()
pool.join()
def start():
"""啟動爬蟲"""
#爬取小說url
book_config.CONSOLE_LOGGER.info(u'開始 -- 爬取主頁小說')
url = 'http://www.biquge.com/'
link_crawler.book_crawler(url,headers={},scrape_callblack=BookSpiderCallblack())
time.sleep(10)
#小說詳情爬取
book_crawler = link_crawler.book_crawler
book_config.CONSOLE_LOGGER.info(u'開始 -- 小說詳情爬取')
process_crawler(book_detail_crawler)
#替換不正常圖片為默認(rèn)封面圖片
replace_img()
time.sleep(10)
#章節(jié)信息爬取
book_config.CONSOLE_LOGGER.info(u'開始 -- 章節(jié)詳情爬取')
process_crawler(chpater_crwler)