Scrapy框架總結(jié)(1)

@TOC

Scrapy簡介

較為流行的python爬蟲框架喘先。
本文著重將記錄本人入門Scrapy時的所有精煉總結(jié)(除了一些書、官方文檔凫碌,同時也會借鑒一些比較好的blog的內(nèi)容死陆,因?yàn)闀鴮懙奶鷿俜轿臋n又搞得和過家家一樣其屏,亂的不行喇勋,根本沒法看)。希望能給大家?guī)韼椭诵校瑨伌u引玉川背。
如果爬下來的數(shù)據(jù)還不會分析,建議先看本人上一篇博文《BeautifulSoup總結(jié)及contents內(nèi)容分析》

Scrapy架構(gòu)

架構(gòu)如下圖所示:

Scrapy框架圖

圖中綠色線條代表了數(shù)據(jù)流向蛤袒。其他幾個則是其組件熄云。
//以下是我個人對這些組件的理解,并非官方文檔解釋

  • 引擎(Scrapy Engine):負(fù)責(zé)整個數(shù)據(jù)流走向
  • 調(diào)度器(scheduler):負(fù)責(zé)將Request入隊(duì)妙真,并在需要時提供給引擎
  • 下載器(DownLoader): 負(fù)責(zé)提交Request缴允,并獲得對應(yīng)網(wǎng)站的Response,將其提交給spider下一步處理珍德。(可以根據(jù)用戶定義的<kbd>下載中間件</kbd>中的配置進(jìn)行自定義下載)
  • 蜘蛛(Spider):負(fù)責(zé)處理網(wǎng)站返回的Response练般,提取Item 或者是 需要繼續(xù)跟進(jìn)的URL
  • 數(shù)據(jù)管道(Item pipeline): 去重、過濾锈候、加工和存儲Item
  • 下載中間件(Downloader middlewares):自定義擴(kuò)展下載功能的組件
  • Spider 中間件 (Spider middlewares):自定義擴(kuò)展Engine和Spider中間通信的功能組件

Scrapy運(yùn)作流程

1 引擎:Hi薄料!Spider, 你要處理哪一個網(wǎng)站?
2 Spider:老大要我處理xxxx.com泵琳。
3 引擎:你把第一個需要處理的URL給我吧摄职。
4 Spider:給你,第一個URL是xxxxxxx.com获列。
5 引擎:Hi谷市!調(diào)度器,我這有request請求你幫我排序入隊(duì)一下击孩。
6 調(diào)度器:好的迫悠,正在處理你等一下。
7 引擎:Hi溯壶!調(diào)度器及皂,把你處理好的request請求給我甫男。
8 調(diào)度器:給你,這是我處理好的request
9 引擎:Hi验烧!下載器板驳,你按照老大的下載中間件的設(shè)置幫我下載一下這個request請求
10 下載器:好的!給你碍拆,這是下載好的東西若治。(如果失敗:sorry感混,這個request下載失敗了端幼。然后引擎告訴調(diào)度器,這個request下載失敗了弧满,你記錄一下婆跑,我們待會兒再下載)
11 引擎:Hi!Spider庭呜,這是下載好的東西滑进,并且已經(jīng)按照老大的下載中間件處理過了,你自己處理一下(注意募谎!這兒responses默認(rèn)是交給def parse()這個函數(shù)處理的)
12 Spider:(處理完畢數(shù)據(jù)之后對于需要跟進(jìn)的URL)扶关,Hi!引擎数冬,我這里有兩個結(jié)果节槐,這個是我需要跟進(jìn)的URL,還有這個是我獲取到的Item數(shù)據(jù)拐纱。
13 引擎:Hi 铜异!管道 我這兒有個item你幫我處理一下!調(diào)度器秸架!這是需要跟進(jìn)URL你幫我處理下熙掺。然后從第四步開始循環(huán),直到獲取完老大需要全部信息咕宿。
14 管道``調(diào)度器:好的,現(xiàn)在就做蜡秽!
參考: https://segmentfault.com/a/1190000013178839

項(xiàng)目文件目錄結(jié)構(gòu)

在命令行中府阀,執(zhí)行 scrapy startproject <項(xiàng)目名稱>即會在所在當(dāng)前目錄下創(chuàng)建項(xiàng)目目錄以及相關(guān)文件。

項(xiàng)目名
|—— 項(xiàng)目名
|   |—— _init_.py   #包定義
|   |—— items.py    #模型定義
|   |—— middlewares.py  #中間件定義
|   |—— pipelines.py    #管道定義
|   |—— settings.py     #配置文件芽突。編程方式控制的配置文件
|   |—— spider
|       |—— _init_.py   #默認(rèn)蜘蛛代碼文件
|——— scrapy.cfg #運(yùn)行配置文件试浙。該文件存放的目錄為根目錄。模塊名的字段定義了項(xiàng)目的設(shè)置

最基本的Scrapy爬蟲制作流程

1寞蚌、新建項(xiàng)目
2田巴、明確目標(biāo):主要是編寫Item.py
3钠糊、制作爬蟲:主要是編寫spider.py
4、存儲內(nèi)容:主要是編寫pipelines.py

實(shí)戰(zhàn)

環(huán)境安裝

//建議直接安裝壹哺,不要用conda創(chuàng)建一個環(huán)境再安裝抄伍。
//因?yàn)閟crapy命令需要在全局使用,這樣才能在任何文件夾輕松調(diào)用管宵。
pip install Scrapy
//驗(yàn)證是否安裝成功截珍。運(yùn)行該命令出現(xiàn)圖中內(nèi)容即為成功。
scrapy
在這里插入圖片描述

1箩朴、新建項(xiàng)目

項(xiàng)目介紹:從中新網(wǎng)爬取新聞供稿的標(biāo)題岗喉、鏈接、內(nèi)容和日期炸庞,并以json形式保存到本地钱床。

// 需要先cd到你想要存放該項(xiàng)目的路徑下
scrapy startproject chinanews_crawler
在這里插入圖片描述

2、明確目標(biāo)

先查看以下目標(biāo)網(wǎng)站內(nèi)容:
中新網(wǎng):http://www.chinanews.com/rss/
然后用chrome的開發(fā)者工具埠居,查看以下我們需要的鏈接的位置查牌。

在這里插入圖片描述

我們發(fā)現(xiàn),那些鏈接是被放在一個<iframe>元素內(nèi)拐格,也就是當(dāng)前展示的其實(shí)是兩個頁面組成的僧免,直接爬取該網(wǎng)站是拿不到鏈接的。于是捏浊,查看<ifreame>元素懂衩,發(fā)現(xiàn)src="http://www.chinanews.com/rss/rss_2.html"。于是金踪,打開該網(wǎng)頁浊洞,找到真正的鏈接所在。
我們繼續(xù)F12查看其元素胡岔。
在這里插入圖片描述

發(fā)現(xiàn)所有的新聞頻道鏈接都在一個<a>標(biāo)簽中法希。并且打開這些頻道后,就是即時新聞的內(nèi)容了,也就是到達(dá)了我們的目標(biāo)且轨。如下圖:
在這里插入圖片描述

然后叙赚,我們要的是title,link,description,pubDate。
最后屋剑,我們可以開始編寫代碼了。

文件:items.py
from scrapy.item import Item, Field


class ChinaNewsItem(Item):
    title = Field()  # 標(biāo)題
    link = Field()  # 詳情鏈接
    desc = Field()  # 新聞綜述
    pub_date = Field()  # 發(fā)布日期

3诗眨、制作爬蟲

可以直接在spider/文件夾下唉匾,編輯那個 init 文件,或者新建一個py文件匠楚,再者在命令行中巍膘,<kbd>scrapy genspider 爬蟲名 目標(biāo)網(wǎng)站厂财。</kbd>
無論那種方式,其實(shí)都是生成一個爬蟲的類峡懈。

執(zhí)行:scrapy genspider newsCrawler chinanews.com
打開新生成的spider/newsCrawler.py文件璃饱,內(nèi)容如下:

# -*- coding: utf-8 -*-
from scrapy.spider import Spider

class EasyNewsCrawlerItem(Spider):
    name = 'newsCrawler'  # 爬蟲名,啟動時需要用到逮诲,scrapy crawl newsCrawler帜平,就是啟動該爬蟲(對照運(yùn)行流程中第一步中,引擎:Hi,spider,你要處理哪個網(wǎng)站梅鹦。
    allowed_domains = ['chinanews.com']  # 允許爬蟲搜索的域名范圍
    start_urls = ['http://chinanews.com/']  # 起始爬取位置

    def parse(self, response):
        """解析函數(shù)裆甩。response就是下載器下載好的頁面內(nèi)容,爬蟲就在該網(wǎng)站中齐唆,提取所需的Item"""
        pass

根據(jù)<kbd>2嗤栓、明確目標(biāo)</kbd>中,Item的內(nèi)容箍邮,我們發(fā)現(xiàn)茉帅,直接爬取并不能實(shí)現(xiàn)。因?yàn)椋?a target="_blank" rel="nofollow">http://www.chinanews.com/rss/rss_2.html 這個頁面只有鏈接锭弊,要打開這些鏈接堪澎,才能看到正真的內(nèi)容。于是味滞,我們需要再寫一個解析的函數(shù)樱蛤,處理真正網(wǎng)站的內(nèi)容。

def parse_feed(self, response):
    """二次解析剑鞍,本次目標(biāo):解析出最后結(jié)果昨凡,包裝成Item"""
    rss_page = BeautifulSoup(response.body, "lxml")
    items = rss_page.find_all('item')
    for item in items:
        newsItem = EasyNewsCrawlerItem()
        newsItem['title'] = item.title.text
        # link是個自閉和標(biāo)簽,不能用item.link.text蚁署,原因可以看我上一篇博文
        newsItem['link'] = item.contents[2] 
        newsItem['description'] = item.title.text
        newsItem['pubDate'] = item.pubdate.text
        yield newsItem

最后便脊,我們稍微處理一下第一個解析函數(shù),將二次解析定位轉(zhuǎn)一下就行了光戈。下面給出spider的完整代碼哪痰。

# -*- coding: utf-8 -*-
from scrapy.spider import Spider
from scrapy.http import Request
from bs4 import BeautifulSoup
from ..items import EasyNewsCrawlerItem


class NewscrawlerSpider(Spider):
    name = 'newsCrawler'  # 爬蟲名,啟動時需要用到久妆,scrapy crawl newsCrawler妒御,就是啟動該爬蟲(對照運(yùn)行流程中第一步中,引擎:Hi,spider,你要處理哪個網(wǎng)站镇饺。
    allowed_domains = ['chinanews.com']  # 允許爬蟲搜索的域名范圍
    start_urls = ['http://www.chinanews.com/rss/rss_2.html']  # 起始爬取位置

    def parse(self, response):
        """解析函數(shù)。response就是下載器下載好的頁面內(nèi)容送讲,爬蟲就在該網(wǎng)站中奸笤,提取所需的Item"""
        """本次目標(biāo):解析出href中的鏈接惋啃,然后留給下一個解析函數(shù)繼續(xù)解析"""
        # 不熟悉BeautifulSoup的可以看我上一個博文
        rss_page = BeautifulSoup(response.body, "lxml")
        """拿到該網(wǎng)站后,先找到所有<a>標(biāo)簽监右,然后把其中的href的內(nèi)容保存起來边灭。"""
        rss_links = set([item['href'] for item in rss_page.find_all("a")])  # 用set是為了濾掉重復(fù)鏈接
        for link in rss_links:
            yield Request(url=link, callback=self.parse_feed)

    def parse_feed(self, response):
        """二次解析,本次目標(biāo):解析出最后結(jié)果健盒,包裝成Item"""
        rss_page = BeautifulSoup(response.body, "lxml")
        items = rss_page.find_all('item')
        for item in items:
            newsItem = EasyNewsCrawlerItem()
            newsItem['title'] = item.title.text
            # link是個自閉和標(biāo)簽绒瘦,不能用item.link.text,原因可以看我上一篇博文
            newsItem['link'] = item.contents[2]
            newsItem['description'] = item.title.text
            newsItem['pubDate'] = item.pubdate.text
            yield newsItem

            """如果是item.pubDate.text會失敗并報錯扣癣,然后我查了一下文檔惰帽,發(fā)現(xiàn):"""
            """
            如果同樣的代碼在不同環(huán)境下結(jié)果不同,可能是因?yàn)閮蓚€環(huán)境下使用不同的解析器造成的.
            例如這個環(huán)境中安裝了lxml,而另一個環(huán)境中只有html5lib, 解析器之間的區(qū)別中說明了原因.
            修復(fù)方法是在 BeautifulSoup 的構(gòu)造方法中中指定解析器 
            因?yàn)镠TML標(biāo)簽是 大小寫敏感 的,所以3種解析器再出來文檔時都將tag和屬性轉(zhuǎn)換成小寫.
            """
            """結(jié)論:beautifulSoup會將tag統(tǒng)一變成小寫"""

4、 存儲內(nèi)容

存儲數(shù)據(jù)的方法有以下幾種:

  1. 通過pipeline(管道)存儲
  2. 全局性指定父虑。setting.py文件中配置存儲選項(xiàng)
  3. 動態(tài)指定该酗。命令行啟動時添加-o參數(shù)

<kbd>方法1:管道存儲</kbd>
為了加深對管道的理解,體現(xiàn)其功能士嚎,這里寫了三個管道呜魄,分別對應(yīng)過濾功能,加工功能莱衩,存儲功能爵嗅。每個管道都是一個擁有process_item方法的類。
同時笨蚁,管道寫好了后睹晒,要在setting.py文件中將管道配置一下,主要是控制 Item經(jīng)過管道的順序赚窃,可以取值為0-1000册招,值越小優(yōu)先級越高。

文件:pipelines.py

# -*- coding: utf-8 -*-

from scrapy.exceptions import DropItem
import time
from bs4 import BeautifulSoup
import json


class PreservationPipeline(object):
    """過濾性管道勒极。只通過最近一個小時以內(nèi)的新聞"""

    def process_item(self, item, spider):
        # <pubDate>2018-12-06 16:09:03<pubDate>
        # 先將字符串轉(zhuǎn)為時間戳
        newsTime = time.mktime(time.strptime(item['pub_date'], '%Y-%m-%d %H:%M:%S'))
        # 獲取當(dāng)前時間戳
        nowTime = time.time()
        if (nowTime - newsTime) / (60 * 60) > 1:
            raise DropItem("%s ,Not Fresh!" % item)  # 超過一個小時是掰,丟棄
        return item


class CleanPipeline(object):
    """加工性管道。刪除掉所有的\r\n符號"""

    def process_item(self, item, spider):
        def clear_html(text):
            html = BeautifulSoup(text)
            return html.get_text().replace('\n', '')

        item['desc'] = clear_html(item['desc'])
        return item


class JsonFeedPipeline(object):
    """存儲管道辱匿。存儲到指定的Json文件中去"""

    def __init__(self):
        self.json_file = open('pipResult.json', 'w')
        self.json_file.write("[\n")

    def process_item(self, item, spider):
        line = json.dumps(dict(item)) + ",\n"
        # BeautifulSoup會統(tǒng)一為Unicode編碼键痛,需要重新編碼一下
        self.json_file.write(line.encode('utf-8').decode("unicode_escape"))
        return item

    def close_spider(self, spider):
        self.json_file.write("\n]")
        self.json_file.close()

文件: setting.py

# Configure item pipelines
# See https://doc.scrapy.org/en/latest/topics/item-pipeline.html
ITEM_PIPELINES = {
    'chinanews_crawler.pipelines.PreservationPipeline': 300,
    'chinanews_crawler.pipelines.CleanPipeline': 301,
    'chinanews_crawler.pipelines.JsonFeedPipeline': 302,
}

這樣,整個爬蟲系統(tǒng)就寫好了匾七,直接在項(xiàng)目根目錄下絮短,運(yùn)行命令 scrapy crawl newsCrawler,即可看到pipResult.json文件昨忆。

<kbd>方法2:全局性指定</kbd>
在setting.py文件中丁频,直接加上以下幾項(xiàng)。

FEED_URI = "result.json"    # 保存文件名
FEED_FORMAT = "json"    # 保存文件格式
FEED_EXPORT_ENCODING = 'utf-8'  # 保存文件的編碼

這樣,整個爬蟲系統(tǒng)就寫好了席里,直接在項(xiàng)目根目錄下叔磷,運(yùn)行命令 scrapy crawl newsCrawler,即可看到result.json文件奖磁。

<kbd>方法3:動態(tài)指定</kbd>
在Scrapy命令中加入-o的輸出參數(shù)即可改基。(本人覺得還不如方法2,每次命令都得加咖为,然后還不利于后人查看代碼秕狰。因此此方法本人并未嘗試)

本項(xiàng)目僅供學(xué)習(xí)參考,所有步驟與本人遇到的坑躁染,本人給予了解釋注釋 鸣哀。因此,請不要都測試同一網(wǎng)站褐啡,以免引起不必要的麻煩诺舔,謝謝。
@copyright Dawn
編輯于:2018/12/6

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末备畦,一起剝皮案震驚了整個濱河市低飒,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌懂盐,老刑警劉巖褥赊,帶你破解...
    沈念sama閱讀 222,378評論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異莉恼,居然都是意外死亡拌喉,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,970評論 3 399
  • 文/潘曉璐 我一進(jìn)店門俐银,熙熙樓的掌柜王于貴愁眉苦臉地迎上來尿背,“玉大人,你說我怎么就攤上這事捶惜√锩辏” “怎么了?”我有些...
    開封第一講書人閱讀 168,983評論 0 362
  • 文/不壞的土叔 我叫張陵吱七,是天一觀的道長汽久。 經(jīng)常有香客問我,道長踊餐,這世上最難降的妖魔是什么景醇? 我笑而不...
    開封第一講書人閱讀 59,938評論 1 299
  • 正文 為了忘掉前任,我火速辦了婚禮吝岭,結(jié)果婚禮上三痰,老公的妹妹穿的比我還像新娘吧寺。我一直安慰自己,他們只是感情好酒觅,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,955評論 6 398
  • 文/花漫 我一把揭開白布撮执。 她就那樣靜靜地躺著,像睡著了一般舷丹。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上蜓肆,一...
    開封第一講書人閱讀 52,549評論 1 312
  • 那天颜凯,我揣著相機(jī)與錄音,去河邊找鬼仗扬。 笑死症概,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的早芭。 我是一名探鬼主播彼城,決...
    沈念sama閱讀 41,063評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼退个!你這毒婦竟也來了募壕?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,991評論 0 277
  • 序言:老撾萬榮一對情侶失蹤语盈,失蹤者是張志新(化名)和其女友劉穎舱馅,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體刀荒,經(jīng)...
    沈念sama閱讀 46,522評論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡代嗤,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,604評論 3 342
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了缠借。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片干毅。...
    茶點(diǎn)故事閱讀 40,742評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖泼返,靈堂內(nèi)的尸體忽然破棺而出硝逢,到底是詐尸還是另有隱情,我是刑警寧澤符隙,帶...
    沈念sama閱讀 36,413評論 5 351
  • 正文 年R本政府宣布趴捅,位于F島的核電站,受9級特大地震影響霹疫,放射性物質(zhì)發(fā)生泄漏拱绑。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,094評論 3 335
  • 文/蒙蒙 一丽蝎、第九天 我趴在偏房一處隱蔽的房頂上張望猎拨。 院中可真熱鬧膀藐,春花似錦、人聲如沸红省。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,572評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽吧恃。三九已至虾啦,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間痕寓,已是汗流浹背傲醉。 一陣腳步聲響...
    開封第一講書人閱讀 33,671評論 1 274
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留呻率,地道東北人硬毕。 一個月前我還...
    沈念sama閱讀 49,159評論 3 378
  • 正文 我出身青樓,卻偏偏與公主長得像礼仗,于是被迫代替她去往敵國和親吐咳。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,747評論 2 361

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