Python爬蟲Scrapy(二)_入門案例

本章將從案例開始介紹python scrapy框架,更多內(nèi)容請參考:python學(xué)習(xí)指南

入門案例

學(xué)習(xí)目標(biāo)

  • 創(chuàng)建一個Scrapy項目
  • 定義提取的結(jié)構(gòu)化數(shù)據(jù)(Item)
  • 編寫爬取網(wǎng)站的Spider并提取出結(jié)構(gòu)化數(shù)據(jù)(Item)
  • 編寫Item Pipelines來存儲提取到的Item(即結(jié)構(gòu)化數(shù)據(jù))

一关霸、新建項目(scrapy startproject)

  • 在開始爬取之前勿锅,必須創(chuàng)建一個新的Scrapy項目云石。進入自定義的項目目錄中,運行下列命令:
scrapy startproject cnblogSpider
  • 其中,cnblogSpider為項目名稱嚼隘,可以看到將會創(chuàng)建一個cnblogSpider文件夾寄月,目錄結(jié)構(gòu)大致如下:
scrapy目錄結(jié)構(gòu)

scrapy.cfg:項目部署文件
cnblogSpider/: 該項目的python模塊辜膝,之后可以在此加入代碼
cnblogSpider/items.py: 項目中的item文件。
cnblogSpider/pipelines.py: 項目中的Pipelines文件漾肮。
cnblogSpider/settings.py: 項目的配置文件厂抖。
cnblogSpider/spiders/: 放置Spider代碼的目錄。

二克懊、明確目標(biāo)(mySpider/items.py)

我們打算抓瘸栏ā:"http://www.cnblogs.com/miqi1992/default.html?page=2" 網(wǎng)站里博客地址七蜘、標(biāo)題、創(chuàng)建時間墙懂、文本橡卤。

  1. 打開cnblogSpider目錄下的items.py

  2. item定義結(jié)構(gòu)化數(shù)據(jù)字段,用來保存爬取到的數(shù)據(jù)损搬,有點像Python中的dict,但是提供了一些額外的保護減少錯誤碧库。

  3. 可以通過創(chuàng)建一個scrapy.item類,并且定義類型為scrapy.Field的類屬性來定義一個Item(可以理解成類似于ORM的映射關(guān)系)巧勤。

  4. 接下來嵌灰,創(chuàng)建一個CnblogspiderItem類,和模型item模型(model)颅悉。

import scrapy


class CnblogspiderItem(scrapy.Item):
    # define the fields for your item here like:
    url = scrapy.Field()
    time = scrapy.Field()
    title = scrapy.Field()
    content = scrapy.Field()

三沽瞭、制作爬蟲(spiders/cnblogsSpider.py)

爬蟲功能主要分兩步:

1. 爬數(shù)據(jù)

  • 在當(dāng)前目錄下輸入命令,將在cnblogSpider/spiders目錄下創(chuàng)建一個名為cnblog的爬蟲剩瓶,并制定爬取域的范圍:
scrapy genspider cnblog "cnblogs.com"
  • 打開cnblogSpider/spiders目錄下的cnblog驹溃,默認增加了下列代碼:
# -*- coding: utf-8 -*-
import scrapy


class CnblogSpider(scrapy.Spider):
    name = 'cnblog'
    allowed_domains = ['cnblogs.com']
    start_urls = ['http://cnblogs.com/']

    def parse(self, response):
        pass

其實也可以由我們自行創(chuàng)建cnblog.py并編寫上面的代碼,只不過使用命令可以免去編寫固定代碼的麻煩

要建立一個Spider,你必須用scrapy.Spider類創(chuàng)建一個子類儒搭,并確定了三個強制的屬性和一個方法吠架。

  • name = "": 這個爬蟲的識別名稱,必須是唯一的搂鲫,在不同的爬蟲必須定義不同的名字傍药。
  • allow_domains=[]: 是搜索的域名范圍,也就是爬蟲的約束區(qū)域魂仍,規(guī)定爬蟲只爬取這個域名下的網(wǎng)頁拐辽,不存在的URL會被忽略。
  • start_urls=():爬取的URL元祖/列表擦酌。爬蟲從這里開始爬取數(shù)據(jù)俱诸,所以,第一次下載的數(shù)據(jù)將會從這些urls開始赊舶。其他子URL將會從這些起始URL中繼承性生成睁搭。
  • parse(self, response):解析的方法,每個初始URL完成下載后將被調(diào)用笼平,調(diào)用的時候傳入從每一個URL傳回的Response對象來作為唯一參數(shù)园骆,主要作用如下:
    1. 負責(zé)解析返回的網(wǎng)頁數(shù)據(jù)(respose.body),提取結(jié)構(gòu)化數(shù)據(jù)(生成item)
    2. 生成需要下一頁的URL請求

將start_urls的值改為需要爬取的第一個url

start_urls=("http://www.cnblogs.com/miqi1992/default.html?page=2")

修改parse()方法

def parse(self, response):
    filename = "cnblog.html"
    with open(filename, 'w') as f:
        f.write(response.body)

然后運行一下看看,在cnblogSpider目錄下運行:

scrapy crawl cnblog

是的寓调,就是cnblog,看上面代碼锌唾,它是CnblogSpider類的name屬性,也就是scrapy genspider命令的唯一爬蟲名。

運行之后晌涕,如果打印的日志出現(xiàn)[scrapy]INFO: Spider closed(finished)滋捶,代表執(zhí)行完成。之后當(dāng)前文件夾中就出現(xiàn)了一個cnblog.html文件余黎,里面就是我們剛剛要爬取的網(wǎng)頁的全部源代碼信息重窟。

#注意,Python2.x默認編碼環(huán)境是ASCII惧财,當(dāng)和取回的數(shù)據(jù)編碼格式不一致時厨姚,可能會造成亂碼;
#我們可以指定保存內(nèi)容的編碼格式煮甥,一般情況下噩死,我們可以在代碼最上方添加:
import os
reload(sys)
sys.setdefaultencoding('utf-8')
#這三行代碼是Python2.x里面解決中文編碼的萬能鑰匙惶室,警告這么多年的吐槽后Python3學(xué)乖了斋枢,默認編碼是Unicode了

2.爬數(shù)據(jù)

  • 爬取整個網(wǎng)頁完畢帘靡,接下來就是取過程了,首先觀察頁面源碼:


    頁面結(jié)構(gòu)
<div class="day">
    <div class="dayTitle">...</div>
    <div class="postTitle">...</div>
    <div class="postCon">...</div>
</div>
  • XPath表達式如下:
    • 所有文章:.//*[@class='day']
    • 文章發(fā)表時間:.//*[@class='dayTitle']/a/text()
    • 文章標(biāo)題內(nèi)容:.//*[@class='postTitle']/a/text()
    • 文章摘要內(nèi)容:.//*[@class='postCon']/div/text()
    • 文章鏈接:.//*[@class='postTitle']/a/@href

是不是一目了然瓤帚?直接上XPath開始提取數(shù)據(jù)吧描姚。

  • 我們之前在cnblogSpider/items.py里定義了一個CnblogItem類。這里引入進來
from cnblogSpider.items import CnblogspiderItem
  • 然后將我們得到的數(shù)據(jù)封裝到一個CnblogspiderItem對象中戈次,可以保存每個博客的屬性:

form cnblogSpider.items import CnblogspiderItem

def parse(self, response):
        # print(response.body)
        # filename = "cnblog.html"
        # with open(filename, 'w') as f:
        #     f.write(response.body)

        #存放博客的集合
        items = []

        for each in response.xpath(".//*[@class='day']"):
            item = CnblogspiderItem()
            url = each.xpath('.//*[@class="postTitle"]/a/@href').extract()[0]
            title = each.xpath('.//*[@class="postTitle"]/a/text()').extract()[0]
            time = each.xpath('.//*[@class="dayTitle"]/a/text()').extract()[0]
            content = each.xpath('.//*[@class="postCon"]/div/text()').extract()[0]

            item['url'] = url
            item['title'] = title
            item['time'] = time
            item['content'] = content 
            
            items.append(item)

        #直接返回最后數(shù)據(jù)
        return items
  • 我們暫時先不處理管道轩勘,后面會詳細介紹。

保存數(shù)據(jù)

scrapy保存信息的最簡單的方法主要有四種怯邪, -o 輸出指定格式的文件绊寻,命令如下:

#json格式,默認為Unicode編碼
scrapy crawl cnblog -o cnblog.json

#json lines格式悬秉,默認為Unicode編碼
scrapy crawl cnblog -o cnblog.jsonl

#csv逗號表達式澄步,可用excel打開
scrapy crawl cnblog -o cnblog.csv

#xml格式
scrapy crawl cnblog -o cnblog.xml

思考

如果將代碼改成下面形式,結(jié)果完全一樣

請思考yield在這里的作用:

form cnblogSpider.items import CnblogspiderItem

def parse(self, response):
        # print(response.body)
        # filename = "cnblog.html"
        # with open(filename, 'w') as f:
        #     f.write(response.body)

        #存放博客的集合
        # items = []

        for each in response.xpath(".//*[@class='day']"):
            item = CnblogspiderItem()
            url = each.xpath('.//*[@class="postTitle"]/a/@href').extract()[0]
            title = each.xpath('.//*[@class="postTitle"]/a/text()').extract()[0]
            time = each.xpath('.//*[@class="dayTitle"]/a/text()').extract()[0]
            content = each.xpath('.//*[@class="postCon"]/div/text()').extract()[0]

            item['url'] = url
            item['title'] = title
            item['time'] = time
            item['content'] = content 
            
            # items.append(item)
            #將獲取到的數(shù)據(jù)交給pipelines
            yield item

        #直接返回最后數(shù)據(jù),不經(jīng)過pipelines
        #return items

參考:

  1. Python參考手冊
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末和泌,一起剝皮案震驚了整個濱河市村缸,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌武氓,老刑警劉巖梯皿,帶你破解...
    沈念sama閱讀 221,576評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異县恕,居然都是意外死亡东羹,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,515評論 3 399
  • 文/潘曉璐 我一進店門弱睦,熙熙樓的掌柜王于貴愁眉苦臉地迎上來百姓,“玉大人,你說我怎么就攤上這事况木±萋#” “怎么了旬迹?”我有些...
    開封第一講書人閱讀 168,017評論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長求类。 經(jīng)常有香客問我奔垦,道長,這世上最難降的妖魔是什么尸疆? 我笑而不...
    開封第一講書人閱讀 59,626評論 1 296
  • 正文 為了忘掉前任椿猎,我火速辦了婚禮,結(jié)果婚禮上寿弱,老公的妹妹穿的比我還像新娘犯眠。我一直安慰自己,他們只是感情好症革,可當(dāng)我...
    茶點故事閱讀 68,625評論 6 397
  • 文/花漫 我一把揭開白布筐咧。 她就那樣靜靜地躺著,像睡著了一般噪矛。 火紅的嫁衣襯著肌膚如雪量蕊。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,255評論 1 308
  • 那天艇挨,我揣著相機與錄音残炮,去河邊找鬼。 笑死缩滨,一個胖子當(dāng)著我的面吹牛势就,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播脉漏,決...
    沈念sama閱讀 40,825評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼蛋勺,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了鸠删?” 一聲冷哼從身側(cè)響起抱完,我...
    開封第一講書人閱讀 39,729評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎刃泡,沒想到半個月后巧娱,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,271評論 1 320
  • 正文 獨居荒郊野嶺守林人離奇死亡烘贴,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,363評論 3 340
  • 正文 我和宋清朗相戀三年禁添,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片桨踪。...
    茶點故事閱讀 40,498評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡老翘,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情铺峭,我是刑警寧澤墓怀,帶...
    沈念sama閱讀 36,183評論 5 350
  • 正文 年R本政府宣布,位于F島的核電站卫键,受9級特大地震影響傀履,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜莉炉,卻給世界環(huán)境...
    茶點故事閱讀 41,867評論 3 333
  • 文/蒙蒙 一钓账、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧絮宁,春花似錦梆暮、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,338評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至治专,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間遭顶,已是汗流浹背张峰。 一陣腳步聲響...
    開封第一講書人閱讀 33,458評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留棒旗,地道東北人喘批。 一個月前我還...
    沈念sama閱讀 48,906評論 3 376
  • 正文 我出身青樓,卻偏偏與公主長得像铣揉,于是被迫代替她去往敵國和親饶深。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,507評論 2 359

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