Scrapy爬蟲入門實(shí)例

在搭建好了Scrapy的開發(fā)環(huán)境后(如果配置過(guò)程中遇到問題胰坟,請(qǐng)參考上一篇文章
搭建Scrapy爬蟲的開發(fā)環(huán)境
或者在博客里留言),我們開始演示爬取實(shí)例。

我們?cè)噲D爬取論壇-東京版的主題貼说庭。該網(wǎng)
站需要登錄后才能查看帖子附帶的大圖,適合演示登錄過(guò)程焙糟。

1. 定義item

我們需要保存標(biāo)題口渔、帖子詳情、帖子詳情的url穿撮、圖片列表缺脉,所以定義item如下:

class RentItem(scrapy.Item):
    """item類"""

    title = scrapy.Field()          # 標(biāo)題
    rent_desc = scrapy.Field()      # 描述
    url = scrapy.Field()            # 詳情的url
    pic_list = scrapy.Field()       # 圖片列表

2. 使用FormRequest模擬登錄

首先我們需要分析頁(yè)面,找到登錄的form悦穿,以及需要提交的數(shù)據(jù)(用Fiddler或Firebug分析請(qǐng)求即可)攻礼,
然后使用Scrapy提供FormRequest.from_response()模擬頁(yè)面的登錄過(guò)程,主要代碼如下:

# 需要登錄栗柒,使用FormRequest.from_response模擬登錄
    if "id='lsform'" in response.body:
        logging.info("in parse, need to login, url: {0}".format(response.url))
        form_data = {
            "handlekey": "ls",
            "quickforward": "yes",
            "username": "loginname",
            "password": "passwd"
        }
        request = FormRequest.from_response(
                response=response,
                headers=self.headers,
                formxpath="http://form[contains(@id, 'lsform')]",
                formdata=form_data,
                callback=self.parse_list
                )
    else:
        logging.info("in parse, NOT need to login, url: {0}"
                     .format(response.url))
        request = Request(url=response.url,
                          headers=self.headers,
                          callback=self.parse_list,
                          )

如果請(qǐng)求的頁(yè)面需要登錄礁扮,則通過(guò)xpath定位到對(duì)應(yīng)的form知举,將登錄需要的數(shù)據(jù)作為參數(shù),提交登錄太伊,
在callback對(duì)應(yīng)的回調(diào)方法里雇锡,處理登錄成功后的爬取邏輯。

3. 使用XPath提取頁(yè)面數(shù)據(jù)

Scrapy使用XPath或CSS表達(dá)式分析頁(yè)面結(jié)構(gòu)僚焦,由基于lxml的Selector提取數(shù)據(jù)锰提。XPath或者CSS都可
以,另外BeautifulSoup
分析HTML/XML文件非常方便芳悲,這里采用XPath分析頁(yè)面立肘,請(qǐng)參考
zvon-XPath 1.0 Tutorial,示例豐富且易
懂名扛,看完這個(gè)入門教程谅年,常見的爬取需求基本都能滿足。我這里簡(jiǎn)單解釋一下幾個(gè)重要的點(diǎn):

  • /表示絕對(duì)路徑肮韧,即匹配從根節(jié)點(diǎn)開始融蹂,./表示當(dāng)前路徑,//表示匹配任意開始節(jié)點(diǎn)弄企;

  • *是通配符殿较,可以匹配任意節(jié)點(diǎn);

  • 在一個(gè)節(jié)點(diǎn)上使用[]桩蓉,如果是數(shù)字n表示匹配第n個(gè)element淋纲,如果是@表示匹配屬性,還可以使用函數(shù)院究,
    比如常用的contains()表示包含洽瞬,starts-with()表示字符串起始匹配等。

  • 在取節(jié)點(diǎn)的值時(shí)业汰,text()只是取該節(jié)點(diǎn)下的值伙窃,而不會(huì)取該節(jié)點(diǎn)的子節(jié)點(diǎn)的值,而.則會(huì)取包括子節(jié)點(diǎn)
    在內(nèi)的所有值样漆,比如:

<div>Welcome to <strong>Chengdu</strong></div>

sel.xpath("div/text()")     // Welcome to
sel.xpath("div").xpath("string(.)")     // Welcome to Chengdu

4. 不同的spider使用不同的pipeline

我們可能有很多的spider为障,不同的spider爬取的數(shù)據(jù)的結(jié)構(gòu)不一樣,對(duì)應(yīng)的存儲(chǔ)格式也不盡相同放祟,因此
我們會(huì)定義多個(gè)pipeline鳍怨,讓不同的spider使用不同的pipeline。

首先我們需要定義一個(gè)decorator跪妥,表示如果spider的pipeline屬性中包含了添加該注解的pipeline鞋喇,
則執(zhí)行該pipeline,否則跳過(guò)該pipeline:

def check_spider_pipeline(process_item_method):
    """該注解用在pipeline上

    :param process_item_method:
    :return:
    """
    @functools.wraps(process_item_method)
    def wrapper(self, item, spider):

        # message template for debugging
        msg = "{1} {0} pipeline step".format(self.__class__.__name__)

        # if class is in the spider"s pipeline, then use the
        # process_item method normally.
        if self.__class__ in spider.pipeline:
            logging.info(msg.format("executing"))
            return process_item_method(self, item, spider)

        # otherwise, just return the untouched item (skip this step in
        # the pipeline)
        else:
            logging.info(msg.format("skipping"))
            return item

    return wrapper

然后眉撵,我們還需要在所有pipeline類的回調(diào)方法process_item()上添加該decrator注解:

@check_spider_pipeline
def process_item(self, item, spider):

最后侦香,在spider類中添加一個(gè)數(shù)組屬性pipeline落塑,里面是所有與該spider對(duì)應(yīng)的pipeline,比如:

# 應(yīng)該交給哪個(gè)pipeline去處理
pipeline = set([
    pipelines.RentMySQLPipeline,
])

5. 將爬取的數(shù)據(jù)保存到mysql

數(shù)據(jù)存儲(chǔ)的邏輯在pipeline中實(shí)現(xiàn)罐韩,可以使用twisted adbapi以線程池的方式與數(shù)據(jù)庫(kù)交互憾赁。首
先從setttings中加載mysql配置:

@classmethod
def from_settings(cls, settings):
    """加載mysql配置"""

    dbargs = dict(
        host=settings["MYSQL_HOST"],
        db=settings["MYSQL_DBNAME"],
        user=settings["MYSQL_USER"],
        passwd=settings["MYSQL_PASSWD"],
        charset="utf8",
        use_unicode=True
    )

    dbpool = adbapi.ConnectionPool("MySQLdb", **dbargs)
    return cls(dbpool)

然后在回調(diào)方法process_item中使用dbpool保存數(shù)據(jù)到mysql:

@check_spider_pipeline
def process_item(self, item, spider):
    """pipeline的回調(diào).

    注解用于pipeline與spider之間的對(duì)應(yīng),只有spider注冊(cè)了該pipeline散吵,pipeline才
    會(huì)被執(zhí)行
    """

    # run db query in the thread pool缠沈,在獨(dú)立的線程中執(zhí)行
    deferred = self.dbpool.runInteraction(self._do_upsert, item, spider)
    deferred.addErrback(self._handle_error, item, spider)
    # 當(dāng)_do_upsert方法執(zhí)行完畢,執(zhí)行以下回調(diào)
    deferred.addCallback(self._get_id_by_guid)

    # at the end, return the item in case of success or failure
    # deferred.addBoth(lambda _: item)
    # return the deferred instead the item. This makes the engine to
    # process next item (according to CONCURRENT_ITEMS setting) after this
    # operation (deferred) has finished.
    time.sleep(10)
    return deferred

6. 將圖片保存到七牛云

查看七牛的python接口即可错蝴,這里要說(shuō)明的是,上傳圖片的時(shí)候颓芭,不要使用BucketManager的
bucket.fetch()接口顷锰,因?yàn)榻?jīng)常上傳失敗,建議使用put_data()接口亡问,比如:

def upload(self, file_data, key):
    """通過(guò)二進(jìn)制流上傳文件

    :param file_data:   二進(jìn)制數(shù)據(jù)
    :param key:         key
    :return:
    """
    try:
        token = self.auth.upload_token(QINIU_DEFAULT_BUCKET)
        ret, info = put_data(token, key, file_data)
    except Exception as e:
        logging.error("upload error, key: {0}, exception: {1}"
                      .format(key, e))

    if info.status_code == 200:
        logging.info("upload data to qiniu ok, key: {0}".format(key))
        return True
    else:
        logging.error("upload data to qiniu error, key: {0}".format(key))
        return False

7. 項(xiàng)目部署

部署可以使用scrapydscrapyd-client官紫。
首先安裝:

$ pip install scrapyd
$ pip install scrapyd-client

啟動(dòng)scrapyd:

$ sudo scrapyd &

修改部署的配置文件scrapy.cfg:

[settings]
default = scrapy_start.settings

[deploy:dev]
url = http://localhost:6800/
project = scrapy_start

其中dev表示target,scrapy_start表示project州藕,部署即可:

$ scrapyd-deploy dev -p scrapy_start

ok束世,這篇入門實(shí)例的重點(diǎn)就這么多,項(xiàng)目的源碼在gitlab床玻。

參考

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末毁涉,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子锈死,更是在濱河造成了極大的恐慌贫堰,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,290評(píng)論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件待牵,死亡現(xiàn)場(chǎng)離奇詭異其屏,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)缨该,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,107評(píng)論 2 385
  • 文/潘曉璐 我一進(jìn)店門偎行,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人贰拿,你說(shuō)我怎么就攤上這事蛤袒。” “怎么了膨更?”我有些...
    開封第一講書人閱讀 156,872評(píng)論 0 347
  • 文/不壞的土叔 我叫張陵汗盘,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我询一,道長(zhǎng)隐孽,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,415評(píng)論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮都办,結(jié)果婚禮上蛛倦,老公的妹妹穿的比我還像新娘及皂。我一直安慰自己,他們只是感情好碍拆,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,453評(píng)論 6 385
  • 文/花漫 我一把揭開白布浩习。 她就那樣靜靜地躺著摹迷,像睡著了一般近哟。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,784評(píng)論 1 290
  • 那天缆镣,我揣著相機(jī)與錄音董瞻,去河邊找鬼。 笑死川队,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的睬澡。 我是一名探鬼主播固额,決...
    沈念sama閱讀 38,927評(píng)論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼煞聪!你這毒婦竟也來(lái)了斗躏?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,691評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤昔脯,失蹤者是張志新(化名)和其女友劉穎啄糙,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體云稚,經(jīng)...
    沈念sama閱讀 44,137評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡隧饼,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,472評(píng)論 2 326
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了静陈。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片燕雁。...
    茶點(diǎn)故事閱讀 38,622評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖鲸拥,靈堂內(nèi)的尸體忽然破棺而出拐格,到底是詐尸還是另有隱情,我是刑警寧澤刑赶,帶...
    沈念sama閱讀 34,289評(píng)論 4 329
  • 正文 年R本政府宣布捏浊,位于F島的核電站,受9級(jí)特大地震影響撞叨,放射性物質(zhì)發(fā)生泄漏金踪。R本人自食惡果不足惜浊洞,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,887評(píng)論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望热康。 院中可真熱鬧沛申,春花似錦、人聲如沸姐军。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,741評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)奕锌。三九已至著觉,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間惊暴,已是汗流浹背饼丘。 一陣腳步聲響...
    開封第一講書人閱讀 31,977評(píng)論 1 265
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留辽话,地道東北人肄鸽。 一個(gè)月前我還...
    沈念sama閱讀 46,316評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像油啤,于是被迫代替她去往敵國(guó)和親典徘。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,490評(píng)論 2 348

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

  • scrapy學(xué)習(xí)筆記(有示例版) 我的博客 scrapy學(xué)習(xí)筆記1.使用scrapy1.1創(chuàng)建工程1.2創(chuàng)建爬蟲模...
    陳思煜閱讀 12,668評(píng)論 4 46
  • scrapy是python最有名的爬蟲框架之一益咬,可以很方便的進(jìn)行web抓取逮诲,并且提供了很強(qiáng)的定制型,這里記錄簡(jiǎn)單學(xué)...
    bomo閱讀 2,097評(píng)論 1 11
  • Python版本管理:pyenv和pyenv-virtualenvScrapy爬蟲入門教程一 安裝和基本使用Scr...
    inke閱讀 35,223評(píng)論 7 93
  • 如果你已經(jīng)是為人父母了幽告,你想的最多的是什么梅鹦? 生活中,我們經(jīng)橙咚可以聽到身邊的朋友說(shuō)自己的小孩怎么讓他們操心齐唆,這樣那...
    雨中風(fēng)箏閱讀 627評(píng)論 1 2
  • 感恩的心 營(yíng)養(yǎng)是什么?就是讓生命可以健康成長(zhǎng)的那份需要冻河。當(dāng)然蝶念,它的形式很多很多。 前些天芋绸,走在我們所處小城市的街道...
    語(yǔ)文行者閱讀 579評(píng)論 1 12