Scrapy框架--通用爬蟲Broad Crawls(上)

通用爬蟲(Broad Crawls)介紹

[傳送:中文文檔介紹],里面除了介紹還有很多配置選項(xiàng)尝抖。

通用爬蟲一般有以下通用特性:

  • 其爬取大量(一般來說是無限)的網(wǎng)站而不是特定的一些網(wǎng)站懊昨。
    
  • 其不會(huì)將整個(gè)網(wǎng)站都爬取完畢窄潭,因?yàn)檫@十分不實(shí)際(或者說是不可能)完成的。相反酵颁,其會(huì)限制爬取的時(shí)間及數(shù)量嫉你。
    
  • 其在邏輯上十分簡(jiǎn)單(相較于具有很多提取規(guī)則的復(fù)雜的spider),數(shù)據(jù)會(huì)在另外的階段進(jìn)行后處理(post-processed)
    
  • 其并行爬取大量網(wǎng)站以避免被某個(gè)網(wǎng)站的限制所限制爬取的速度(為表示尊重躏惋,每個(gè)站點(diǎn)爬取速度很慢但同時(shí)爬取很多站點(diǎn))幽污。
    
  • 正如上面所述,Scrapy默認(rèn)設(shè)置是對(duì)特定爬蟲做了優(yōu)化其掂,而不是通用爬蟲油挥。不過, 鑒于其使用了異步架構(gòu)款熬,Scrapy對(duì)通用爬蟲也十分適用。 本篇文章總結(jié)了一些將Scrapy作為通用爬蟲所需要的技巧攘乒, 以及相應(yīng)針對(duì)通用爬蟲的Scrapy設(shè)定的一些建議贤牛。

創(chuàng)建默認(rèn)工程

scrapy創(chuàng)建工程是通過命令進(jìn)行,創(chuàng)建一個(gè)名為proname的scrapy工程:

scrapy startproject proname 

然后根據(jù)提示:

cd proname

然后生成爬蟲模板:

scrapy genspider lagou www.lagou.com

創(chuàng)建Broad Crawls工程

通用爬蟲的創(chuàng)建過程與默認(rèn)爬蟲創(chuàng)建過程一樣则酝,只是在生成爬蟲模板的時(shí)候命令不同殉簸,生成不同的爬蟲模板:

scrapy genspider -t crawl lagou www.lagou.com

只需要增加 -t crawl即可。


源碼邏輯解析

主要邏輯:

1.爬蟲模板主要使用的類是CrawlSpider沽讹,而它繼承的是Spider般卑。

2.Spider的入口函數(shù)是start_requests()。

3.Spider的默認(rèn)返回處理函數(shù)是parse(),它調(diào)用_parse_response(),則允許我們重載parse_start_url()和process_results()方法來對(duì)response進(jìn)行邏輯處理爽雄。

4._parse_response()會(huì)去調(diào)用爬蟲模板設(shè)置的rules=(Rule(LinkExtractor…)),將response交給LinkExtrator,LinkExtrator會(huì)根據(jù)我們傳進(jìn)來的參數(shù):

allow=(), deny=(), allow_domains=(), deny_domains=(), restrict_xpaths=(),
                 tags=('a', 'area'), attrs=('href',), canonicalize=False,
                 unique=True, process_value=None, deny_extensions=None, restrict_css=(),
                 strip=True

進(jìn)行處理摧茴,其中deny的意思是除了它以外兴猩,反向取值,比如deny=('jobs/')則在處理的時(shí)候就會(huì)略過jobs乘盖,只爬取jobs以外的規(guī)則焰檩。


在項(xiàng)目目錄的spiders文件夾下默認(rèn)生成proname.py:


import scrapy
from scrapy.linkextractors import LinkExtractor
from scrapy.spiders import CrawlSpider, Rule


class GxrcSpider(CrawlSpider):
    name = 'proname'
    allowed_domains = ['www.proname.com']
    start_urls = ['http://www.proname.com/']

    rules = (        
        Rule(LinkExtractor(allow=r'Items/'), callback='parse_item', follow=True),
    )

        def parse_item(self, response):
            i = {}

            return i

1.通過Ctrl + 鼠標(biāo)左鍵的方式跟蹤C(jī)rawlSpider類,發(fā)現(xiàn)它是繼承Spider的订框,往下看到CrawlSpider中有個(gè)parse方法析苫,那么就意味著后面寫代碼的時(shí)候不能跟之前一樣在代碼里自定義parse函數(shù)了,最好像模板給出的parse_item這種寫法。

2.解析parse函數(shù)衩侥,它調(diào)用了_parse_response方法:

    def parse(self, response):
        return self._parse_response(response, self.parse_start_url, cb_kwargs={}, follow=True)

其中的_parse_response可以說是核心函數(shù)国旷,里面的參數(shù)cb_kwargs代表著參數(shù)。通過Ctrl+左鍵跟進(jìn)_parse_response:

    def _parse_response(self, response, callback, cb_kwargs, follow=True):
        if callback:
            cb_res = callback(response, **cb_kwargs) or ()
            cb_res = self.process_results(response, cb_res)
            for requests_or_item in iterate_spider_output(cb_res):
                yield requests_or_item

        if follow and self._follow_links:
            for request_or_item in self._requests_to_follow(response):
                yield request_or_item

首先它判斷是否有callback,就是parse函數(shù)中的parse_start_url方法顿乒,這個(gè)方法是可以讓我們重載的议街,可以在里面加入想要的邏輯;

然后它還會(huì)將參數(shù)cb_kwargs傳入到callback中璧榄,往下看它還調(diào)用了process_result方法:

    def process_results(self, response, results):
        return results

這個(gè)方法什么都沒做特漩,把從parse_start_url接收到的result直接return回去,所以process_result方法也是可以重載的骨杂。


接著看:

        if follow and self._follow_links:
            for request_or_item in self._requests_to_follow(response):
                yield request_or_item

如果存在follow涂身,它就進(jìn)行循環(huán),跟進(jìn)_requests_to_follow看一看:

    def _requests_to_follow(self, response):
        if not isinstance(response, HtmlResponse):
            return
        seen = set()
        for n, rule in enumerate(self._rules):
            links = [lnk for lnk in rule.link_extractor.extract_links(response)
                     if lnk not in seen]
            if links and rule.process_links:
                links = rule.process_links(links)
            for link in links:
                seen.add(link)
                r = self._build_request(n, link)
                yield rule.process_request(r)

在 _requests_to_follow中首先判斷是否是response搓蚪,如果不是就直接返回了蛤售,如果是就設(shè)置一個(gè)set,通過set去重妒潭;然后把_rules變成一個(gè)可迭代的對(duì)象悴能,跟進(jìn)_rules:

    def _compile_rules(self):
        def get_method(method):
            if callable(method):
                return method
            elif isinstance(method, six.string_types):
                return getattr(self, method, None)

        self._rules = [copy.copy(r) for r in self.rules]
        for rule in self._rules:
            rule.callback = get_method(rule.callback)
            rule.process_links = get_method(rule.process_links)
            rule.process_request = get_method(rule.process_request)

看到:

rule.callback = get_method(rule.callback)
rule.process_links = get_method(rule.process_links)
rule.process_request = get_method(rule.process_request)

這幾個(gè)都是前面可以傳遞過來的,其中rule.process_links是從Rule類中傳遞過來的:

class Rule(object):

    def __init__(self, link_extractor, callback=None, cb_kwargs=None, follow=None, process_links=None, process_request=identity):
        self.link_extractor = link_extractor
        self.callback = callback
        self.cb_kwargs = cb_kwargs or {}
        self.process_links = process_links
        self.process_request = process_request
        if follow is None:
            self.follow = False if callback else True
        else:
            self.follow = follow

雖然process_links默認(rèn)為None雳灾,但是實(shí)際上我們?cè)谛枰臅r(shí)候可以設(shè)置的漠酿,通常出現(xiàn)在前面爬蟲模板代碼里面的

Rule(LinkExtractor(allow=r'WebPage/JobDetail.*'), callback='parse_item', follow=True,process_links='links_handle')

然后可以在url那里增加各種各樣的邏輯,這里只簡(jiǎn)單的打印輸出:

    def links_handle(self, links):
        for link in links:
            url = link.url
            print(url)
        return links

可以將url進(jìn)行其他的預(yù)處理谎亩,比如可以將url拼接到一起炒嘲、設(shè)置不同的url或者對(duì)url進(jìn)行字符切割等操作。(使用舉例:常用于大型分城市的站點(diǎn)匈庭,比如58的域名nn.58.com夫凸、wh.58.com,就可以通過這個(gè)對(duì)各個(gè)站點(diǎn)的域名進(jìn)行預(yù)匹配)


再來到_requests_to_follow方法中看處理邏輯

    def _requests_to_follow(self, response):
        if not isinstance(response, HtmlResponse):
            return
        seen = set()
        for n, rule in enumerate(self._rules):
            links = [lnk for lnk in rule.link_extractor.extract_links(response)
                     if lnk not in seen]
            if links and rule.process_links:
                links = rule.process_links(links)
            for link in links:
                seen.add(link)
                r = self._build_request(n, link)
                yield rule.process_request(r)

set去重后就yield交給了_build_request處理阱持,build_request則調(diào)用_response_downloaded進(jìn)行頁面的下載夭拌,下載后的頁面交給_parse_response

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市紊选,隨后出現(xiàn)的幾起案子啼止,更是在濱河造成了極大的恐慌,老刑警劉巖兵罢,帶你破解...
    沈念sama閱讀 219,427評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件献烦,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡卖词,警方通過查閱死者的電腦和手機(jī)巩那,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,551評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門吏夯,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人即横,你說我怎么就攤上這事噪生。” “怎么了东囚?”我有些...
    開封第一講書人閱讀 165,747評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵跺嗽,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我页藻,道長(zhǎng)桨嫁,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,939評(píng)論 1 295
  • 正文 為了忘掉前任份帐,我火速辦了婚禮璃吧,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘废境。我一直安慰自己畜挨,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,955評(píng)論 6 392
  • 文/花漫 我一把揭開白布噩凹。 她就那樣靜靜地躺著巴元,像睡著了一般。 火紅的嫁衣襯著肌膚如雪驮宴。 梳的紋絲不亂的頭發(fā)上务冕,一...
    開封第一講書人閱讀 51,737評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音幻赚,去河邊找鬼。 笑死臊旭,一個(gè)胖子當(dāng)著我的面吹牛落恼,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播离熏,決...
    沈念sama閱讀 40,448評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼佳谦,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了滋戳?” 一聲冷哼從身側(cè)響起钻蔑,我...
    開封第一講書人閱讀 39,352評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎奸鸯,沒想到半個(gè)月后咪笑,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,834評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡娄涩,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,992評(píng)論 3 338
  • 正文 我和宋清朗相戀三年窗怒,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,133評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡扬虚,死狀恐怖努隙,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情辜昵,我是刑警寧澤荸镊,帶...
    沈念sama閱讀 35,815評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站堪置,受9級(jí)特大地震影響躬存,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜晋柱,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,477評(píng)論 3 331
  • 文/蒙蒙 一优构、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧雁竞,春花似錦钦椭、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,022評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至进栽,卻和暖如春德挣,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背快毛。 一陣腳步聲響...
    開封第一講書人閱讀 33,147評(píng)論 1 272
  • 我被黑心中介騙來泰國(guó)打工格嗅, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人唠帝。 一個(gè)月前我還...
    沈念sama閱讀 48,398評(píng)論 3 373
  • 正文 我出身青樓屯掖,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親襟衰。 傳聞我的和親對(duì)象是個(gè)殘疾皇子贴铜,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,077評(píng)論 2 355

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

  • scrapy學(xué)習(xí)筆記(有示例版) 我的博客 scrapy學(xué)習(xí)筆記1.使用scrapy1.1創(chuàng)建工程1.2創(chuàng)建爬蟲模...
    陳思煜閱讀 12,703評(píng)論 4 46
  • Python版本管理:pyenv和pyenv-virtualenvScrapy爬蟲入門教程一 安裝和基本使用Scr...
    inke閱讀 35,327評(píng)論 7 93
  • 現(xiàn)如今绍坝,去各類古城名鎮(zhèn)旅行,要具備一項(xiàng)能力:不吐槽苔悦,在商業(yè)化的背景中找到非商業(yè)化的元素轩褐,用覺知去領(lǐng)悟古城鎮(zhèn)的美。在...
    莉歷閱讀 302評(píng)論 0 0
  • 今天來了一年了间坐,overwhelmingly灾挨,原來要死要活的日子都過來了邑退,原來的幻想都破滅了,也重塑了±统危現(xiàn)在一年的...
    fainner閱讀 472評(píng)論 0 0