python實(shí)戰(zhàn)項(xiàng)目:爬取某小說網(wǎng)

項(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):

image.png

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 安裝腳本
image.png

Books目錄結(jié)構(gòu)
  Callblack 業(yè)務(wù)邏輯掂碱,網(wǎng)頁的解析處理(這個(gè)就是抽象出來的解析功能)
  Logs 項(xiàng)目日志
  Util 通用性工具
  Bookconfig.py 項(xiàng)目配置文件
  link_crawler.py 鏈接爬取模塊
  main.py 啟動入口模塊
image.png

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)
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末宇驾,一起剝皮案震驚了整個(gè)濱河市倍靡,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌课舍,老刑警劉巖塌西,帶你破解...
    沈念sama閱讀 218,607評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異筝尾,居然都是意外死亡捡需,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,239評論 3 395
  • 文/潘曉璐 我一進(jìn)店門筹淫,熙熙樓的掌柜王于貴愁眉苦臉地迎上來站辉,“玉大人,你說我怎么就攤上這事损姜∈伟” “怎么了?”我有些...
    開封第一講書人閱讀 164,960評論 0 355
  • 文/不壞的土叔 我叫張陵摧阅,是天一觀的道長汰蓉。 經(jīng)常有香客問我,道長逸尖,這世上最難降的妖魔是什么古沥? 我笑而不...
    開封第一講書人閱讀 58,750評論 1 294
  • 正文 為了忘掉前任,我火速辦了婚禮娇跟,結(jié)果婚禮上岩齿,老公的妹妹穿的比我還像新娘。我一直安慰自己苞俘,他們只是感情好盹沈,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,764評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般乞封。 火紅的嫁衣襯著肌膚如雪做裙。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,604評論 1 305
  • 那天肃晚,我揣著相機(jī)與錄音锚贱,去河邊找鬼。 笑死关串,一個(gè)胖子當(dāng)著我的面吹牛拧廊,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播晋修,決...
    沈念sama閱讀 40,347評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼吧碾,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了墓卦?” 一聲冷哼從身側(cè)響起倦春,我...
    開封第一講書人閱讀 39,253評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎落剪,沒想到半個(gè)月后睁本,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,702評論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡著榴,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,893評論 3 336
  • 正文 我和宋清朗相戀三年添履,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片脑又。...
    茶點(diǎn)故事閱讀 40,015評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡暮胧,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出问麸,到底是詐尸還是另有隱情往衷,我是刑警寧澤,帶...
    沈念sama閱讀 35,734評論 5 346
  • 正文 年R本政府宣布严卖,位于F島的核電站席舍,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏哮笆。R本人自食惡果不足惜来颤,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,352評論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望稠肘。 院中可真熱鬧福铅,春花似錦、人聲如沸项阴。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,934評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至略荡,卻和暖如春庵佣,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背汛兜。 一陣腳步聲響...
    開封第一講書人閱讀 33,052評論 1 270
  • 我被黑心中介騙來泰國打工巴粪, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人序无。 一個(gè)月前我還...
    沈念sama閱讀 48,216評論 3 371
  • 正文 我出身青樓验毡,卻偏偏與公主長得像,于是被迫代替她去往敵國和親帝嗡。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,969評論 2 355

推薦閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,154評論 25 707
  • # Python 資源大全中文版 我想很多程序員應(yīng)該記得 GitHub 上有一個(gè) Awesome - XXX 系列...
    aimaile閱讀 26,486評論 6 427
  • 個(gè)人博客 隨著移動互聯(lián)網(wǎng)的發(fā)展璃氢,移動端iOS開發(fā)的技術(shù)選擇也劃分了好幾個(gè)方向哟玷,有用 React-Native進(jìn)行開...
    語歌閱讀 314評論 1 1
  • 雨后的天空,是灰白的一也,照在發(fā)白的溢滿皺褶的窗簾上巢寡。有老舊的陽光從某個(gè)縫里爬出來,沾在你清晨朦朧的睡影上椰苟。你...
    獨(dú)木喬閱讀 273評論 0 0
  • 喜歡你抑月,不在需要任何理由。早起深深印在心里舆蝴。畢業(yè)季不再是分手季谦絮。 遇見是大學(xué)一道五彩,有你未來多一些精彩洁仗。...
    祎爺閱讀 216評論 0 0