分布式異步爬蟲框架:hannibal

閑的無聊爬了下維基百科有關(guān)古羅馬的數(shù)據(jù),爬取模式是分布式+增量爬取。數(shù)據(jù)爬完了項目卻沒有停手济丘,因為個人興趣開始研究python3.5加入的異步特性,經(jīng)過一段時間的添添補補洽蛀,一個簡單的小爬蟲就這樣誕生了~

本框架基于asyncio摹迷,aiohttp及redis(分布式模式需要)。目前已上架git和pypi郊供,名字取自畢生對抗羅馬共和國的迦太基名將漢尼拔峡碉。

git地址:JorgenLiu/hannibal

閑話少說,下面是框架的介紹驮审。

Mission

框架以每個url作為一個爬取單位鲫寄,使用時需將url封裝成任務(wù)Mission,序列化后放置入爬取隊列疯淫。

關(guān)于此種設(shè)計思路的解釋:
誠然大部分爬取工作只是不同的url地来,但目前遇到的一種情況是對某個相同的url通過POST不同的參數(shù)來獲取不同的數(shù)據(jù),故而將url封裝成Mission用來爬取熙掺。

Mission類的定義:

class Mission(object):
    def __init__(self, unique_tag, url, method='GET', data=None, data_type='data'):
        self.unique_tag = str(unique_tag)
        self.url = url
        if method not in ['GET', 'POST', 'PUT', 'DELETE']:
            raise ValueError('invalid method')
        else:
            self.method = method
        self.data = data
        if data_type not in ['json', 'data']:
            raise ValueError('invalid data type')
        else:
            self.data_type = data_type

unique_tag字段取代了url未斑,作為每個任務(wù)的唯一標(biāo)識。Mission類實現(xiàn)了serialize和deserialize方法币绩,分別負(fù)責(zé)對任務(wù)進行序列化和反序列化蜡秽,并分別在存入和取出任務(wù)時進行調(diào)用府阀。以下為定義Mission的示例:
方法為GET時:

Mission(unique_tag='1', url='http://httpbin.org/get?t=1')

方法為POST時:

Mission(unique_tag='4377', url='https://www.xytzq.cn:9443/tzq/pc/project/getProjectInfo',
                    method='POST',
                    data={'projectid': 4377})

Mission定義后,需加入采集隊列內(nèi)载城〖∷疲可通過調(diào)用隊列的 init_queue([mission_list]) (初始化queue,將一個Mission列表序列化后加入隊列)方法或enqueue (將單個Mission加入隊列)方法加入隊列诉瓦。

Collector

Collector類負(fù)責(zé)進行網(wǎng)頁內(nèi)容的爬取川队,目前實現(xiàn)的模式有兩種:

  1. 根據(jù)預(yù)設(shè)好的url列表進行抓取。
  2. 根據(jù)一個起點url睬澡,抓取后進行解析固额,將解析出的url列表添加至爬取隊列中進行增量抓取。

目前已實現(xiàn)兩種Collector:

  1. LocalCollector煞聪,本地運行的單進程采集節(jié)點斗躏,聲明時需傳入一個異步的解析函數(shù),負(fù)責(zé)對頁面內(nèi)容進行解析昔脯。
  2. DistributeCollector啄糙,通過redis隊列與解析節(jié)點進行交互的分布式采集節(jié)點,只負(fù)責(zé)爬取頁面云稚,并將爬取的頁面內(nèi)容放入解析隊列隧饼,由解析節(jié)點進行解析。

聲明LocalCollector需要以下參數(shù):

  1. mission_queue静陈,任務(wù)隊列
  2. href_pool燕雁,鏈接去重池
  3. parser_function,一個異步函數(shù)鲸拥,接受一個response對象拐格,可通過調(diào)用extract_json或extract_html方法從response對象中提取json或普通html數(shù)據(jù)。LocalCollector不需要額外的解析節(jié)點刑赶,會調(diào)用這個異步的解析函數(shù)對爬取的響應(yīng)體進行解析捏浊,從中提取數(shù)據(jù)進行操作。
  4. cache_size撞叨,默認(rèn)為3金踪,控制請求并發(fā)數(shù)。

DistributeCollector聲明所需參數(shù)和LocalCollector基本一致谒所,但不需要定義parser_function,取而代之的是一個額外的parse_queue沛申,爬取的結(jié)果會被序列化后放入parse_queue這個解析隊列中劣领,等待解析節(jié)點進行解析。

Collector支持注冊錯誤處理器铁材。注冊錯誤處理器需傳入一個錯誤狀態(tài)碼(int)和一個錯誤處理函數(shù)尖淘。錯誤處理函數(shù)接受一個response對象和一個url奕锌,可對錯誤進行處理。

Collector支持注冊爬取前中間件村生。中間件是一個同步函數(shù)惊暴,接受一個mission對象,將在爬取前執(zhí)行趁桃×苫埃可注冊多個中間件,執(zhí)行順序與注冊順序相同卫病。

以下是聲明一個爬取httpbin的LocalCollector的案例:

from hannibal.spider import LocalCollector
from hannibal.util import MemPool, MemQueue, extract_json, Mission

pool = MemPool(name='http_bin')
queue = MemQueue(name='http_bin', limited=True)


async def collect_function(response):
    json_obj = await extract_json(response)
    print(json_obj)


def demo_handler(response, url):
    print('handel 400')


def demo_before_collect_middleware(mission):
    print('this is url: %s' % mission.url)


def demo_before_collect_middleware1(mission):
    print('this is tag: %s' % mission.unique_tag)


def ping_http_bin():
    url_list = [Mission(unique_tag=i, url='http://httpbin.org/get?t=%d' % i) for i in range(1, 500)]
    queue.init_queue(url_list)
    collector = LocalCollector(mission_queue=queue, href_pool=pool, parse_function=collect_function, cache_size=10)
    collector.register_middleware(demo_before_collect_middleware)
    collector.register_middleware(demo_before_collect_middleware1)
    collector.register_error_handler(400, demo_handler)
    collector.conquer()


if __name__ == '__main__':
    ping_http_bin()
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末油啤,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子蟀苛,更是在濱河造成了極大的恐慌益咬,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,734評論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件帜平,死亡現(xiàn)場離奇詭異幽告,居然都是意外死亡,警方通過查閱死者的電腦和手機裆甩,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,931評論 3 394
  • 文/潘曉璐 我一進店門冗锁,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人淑掌,你說我怎么就攤上這事蒿讥。” “怎么了抛腕?”我有些...
    開封第一講書人閱讀 164,133評論 0 354
  • 文/不壞的土叔 我叫張陵芋绸,是天一觀的道長。 經(jīng)常有香客問我担敌,道長摔敛,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,532評論 1 293
  • 正文 為了忘掉前任全封,我火速辦了婚禮马昙,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘刹悴。我一直安慰自己行楞,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,585評論 6 392
  • 文/花漫 我一把揭開白布土匀。 她就那樣靜靜地躺著子房,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上证杭,一...
    開封第一講書人閱讀 51,462評論 1 302
  • 那天田度,我揣著相機與錄音,去河邊找鬼解愤。 笑死镇饺,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的送讲。 我是一名探鬼主播奸笤,決...
    沈念sama閱讀 40,262評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼李茫!你這毒婦竟也來了揭保?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,153評論 0 276
  • 序言:老撾萬榮一對情侶失蹤魄宏,失蹤者是張志新(化名)和其女友劉穎秸侣,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體宠互,經(jīng)...
    沈念sama閱讀 45,587評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡味榛,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,792評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了予跌。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片搏色。...
    茶點故事閱讀 39,919評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖券册,靈堂內(nèi)的尸體忽然破棺而出频轿,到底是詐尸還是另有隱情,我是刑警寧澤烁焙,帶...
    沈念sama閱讀 35,635評論 5 345
  • 正文 年R本政府宣布航邢,位于F島的核電站,受9級特大地震影響骄蝇,放射性物質(zhì)發(fā)生泄漏膳殷。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,237評論 3 329
  • 文/蒙蒙 一九火、第九天 我趴在偏房一處隱蔽的房頂上張望赚窃。 院中可真熱鬧,春花似錦岔激、人聲如沸勒极。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,855評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽辱匿。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間掀鹅,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,983評論 1 269
  • 我被黑心中介騙來泰國打工媒楼, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留乐尊,地道東北人。 一個月前我還...
    沈念sama閱讀 48,048評論 3 370
  • 正文 我出身青樓划址,卻偏偏與公主長得像扔嵌,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子夺颤,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,864評論 2 354