如何使用scrapy中的ItemLoader提取数据?
生活随笔
收集整理的這篇文章主要介紹了
如何使用scrapy中的ItemLoader提取数据?
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
如何使用scrapy中的ItemLoader提取數據?
1. 簡述
- 我們在用scrapy爬取數據時,首先就要明確我們要爬取什么數據。scrapy提供了Item對象這種簡單的容器,我們可以通過Item定義提取數據的格式,需要爬取哪些字段,其提供了類似于字典的API以及用于聲明可用字段的簡單語法。如下所示:
- 下面以爬取伯樂在線文章詳情頁為范例:http://blog.jobbole.com/all-posts/
- Item字段說明:
- Field 對象指明了每個字段的元數據(metadata)。例如上面例子中 content 字段中指明了該字段的序列化函數為str。
- 可以為每個字段指明任何類型的元數據。Field 對象對接受的值沒有任何限制。Field 對象中保存的每個鍵可以由多個組件使用,并且只有這些組件知道這個鍵的存在。設置 Field 對象的主要目的就是在一個地方定義好所有的元數據。
- 需要注意的是,用來聲明item的 Field 對象并沒有被賦值為class的屬性。 不過可以通過 Item.fields 屬性進行訪問。
- 然后在spider.py中,按照一定的規則來進行數據的提取,如下:
- 但是當項目很大,提取的字段數以百計,那么各種提取規則會越來越多,按照這種方式來做,維護的工作將會是一場噩夢!
- 所以scrapy就提供了ItemLoader這樣一個容器,在這個容器里面可以配置item中各個字段的提取規則。可以通過函數分析原始數據,并對Item字段進行賦值,非常的便捷。
- 可以這么來看 Item 和 Itemloader:Item提供保存抓取到數據的容器,而 Itemloader提供的是填充容器的機制。
- Itemloader提供的是一種靈活,高效的機制,可以更方便的被spider或source format (HTML, XML, etc)擴展并重寫,更易于維護,尤其是分析規則特別復雜繁多的時候。
2. 環境
- 系統:win7
- Scrapy 1.4.0
- python 3.6.1
3. ItemLoader使用步驟
3.1. 實例化ItemLoader對象
# 文件 boleSpider.py from scrapy.loader import ItemLoader- 要使用Itemloader,必須先將它實例化。可以使用類似字典的對象或者我們之前定義的Item對象來進行實例化。
- 參數說明:重要的參數有兩個
- 第一個參數:item對象, 傳遞進來的 Item是之前定義的,也可以是一個類似字典的對象。特別需要注意的是,傳遞的是一個實例,不是類名。……(當然不使用對象也可以,當不用對象進行實例化的時候,Item會自動使用ItemLoader.default_item_class 屬性中指定的Item 類在Item Loader constructor中實例化)
- 第二個參數:response,指定用于提取數據的源數據。
3.2. ItemLoader填充數據的三種方法
- 實例化ItemLoader對象之后,接下來,就要開始收集數值到ItemLoader了。ItemLoader提供了三個重要的方法將數據填充進來:
- 使用說明:
- 第一個參數:指定字段名,如title。
- 第二個參數:指定對應的提取規則,或者傳值。
- 前面調用add_xpath等只是將提取的數據收集起來。最終,當所有數據被收集起來之后,還需要調用 ItemLoader.load_item() 方法, 實際上填充并且返回了之前通過調用 add_xpath(),add_css(),and add_value() 所提取和收集到的數據。
- 特別注意:默認情況下,這些字段填入的全部是list類型。就算是傳值,傳遞了一個url,但是結果依然是一個list。
- 從boleSpider.py核心代碼來看,我們可以對每個字段進行配置,匹配映射,非常的清晰,大大方便了可配置性和可維護性。
- 但是實際項目中,一個字段的提取一般不會是直接配置一個規則,還需要更進一步的處理。那如何添加其他處理方法呢?接著往下看…
3.3. ItemLoader填充數據面臨的問題。
- 從上面的示例中,可以看到,存在兩個問題:
- 第一,提取的數據,填充進去的對象都是List類型。而我們大部分的需求是要取第一個數值,取List中的第一個非空元素,那么如何實現取第一個呢?
- 第二,在做item字段解析時,經常需要再進一步解析,過濾出我們想要的數值,例如用正則表達式將 $10 price中的數字10提取出來。那么又如何對字段加一些處理函數呢?
3.4. 輸入處理器input_processor和輸出處理器output_processor
- 首先來改寫一下articleDetailItem的定義:
- 然后在 boleSpider.py 中提取數據:
- Field 字段事實上有兩個參數:
- 第一個是輸入處理器(input_processor) ,當這個item,title這個字段的值傳過來時,可以在傳進來的值上面做一些預處理。
- 第二個是輸出處理器(output_processor) , 當這個item,title這個字段被預處理完之后,輸出前最后的一步處理。
總結一下,每個字段的數據的處理過程是:
- 第一步, 通過 add_xpath(), add_css() 或者 add_value() 方法),提取到數據。
- 第二步,將提取到的數據,傳遞到輸入處理器(input_processor)中進行處理,處理結果被收集起來,并且保存在ItemLoader內(但尚未分配給該Item)。
- 第三步,最后調用輸出處理器(output_processor)來處理之前收集到的數據(這是最后一步對數據的處理)。然后再存入到Item中,輸出處理器的結果是被分配到Item的最終值。
- 第四步,收集到所有的數據后, 調用ItemLoader.load_item() 方法來填充,并得到填充后的 Item 對象。
需要注意的是:input_processor和output_processor都是可調用對象,調用時傳入需要被分析的數據, 處理后返回分析得到的值。因此你可以使用任意函數作為輸入、輸出處理器。唯一需注意的是它們必須接收一個(并且只是一個)迭代器性質的參數。
3.5. 處理原來的兩個問題
- 再回到原來的問題,如何解決:
3.5.1. 如何取第一個?
# 文件items.pyimport scrapy# TakeFirst()是Scrapy提供的內置處理器,用于提取List中的第一個非空元素 class articleDetailItem(scrapy.Item):# 文章鏈接地址url = scrapy.Field(# 轉換前是'url': ['http://blog.jobbole.com/113771/']# 轉換后是'url': 'http://blog.jobbole.com/113699/'output_processor = TakeFirst())3.3.2. 如何在字段上加一些處理函數?
# 文件items.py import datetime import scrapy# 定義一個時間處理轉換函數 # 將 '\r\n\r\n 2018/03/06 · ' 轉換成 datetime.date(2018, 3, 14) def date_convert(value):try:create_date = datetime.datetime.strptime(value, "%Y/%m/%d").date()except Exception as e:create_date = datetime.datetime.now().date()return create_date# 用于存儲解析文章的詳細信息 class articleDetailItem(scrapy.Item):# 文章創建時間create_date = scrapy.Field(# 轉換前是'create_date':'\r\n\r\n 2018/03/14 · '# 轉換后是'create_date': datetime.date(2018, 3, 14),input_processor = MapCompose(date_convert),output_processor = TakeFirst())3.6. scrapy內置的處理器
- 參考源碼: E:\Miniconda\Lib\site-packages\scrapy\loader\processors.py
- 從上面的例子來看,我們可以自定義一下處理函數,作為輸入輸出處理器,但是Scrapy還提供了一些常用的處理器。如MapCompose(能把多個函數執行的結果按順序組合起來,產生最終的輸出,通常用于輸入處理器),TakeFirst(取第一個非空的元素)。
3.6.1. TakeFirst
- 返回第一個非空(non-null/ non-empty)值,常用于單值字段的輸出處理器,無參數。
3.6.2. Identity
- 最簡單的處理器,不進行任何處理,直接返回原來的數據。無參數。
3.6.3. Join
- 返回用分隔符連接后的值。分隔符默認為空格。不接受Loader contexts。
- 當使用默認分隔符的時候,這個處理器等同于如下這個:
3.6.4. Compose
- 用給定的多個函數的組合,來構造的處理器。list對象(注意不是指list中的元素),依次被傳遞到第一個函數,然后輸出,再傳遞到第二個函數,一個接著一個,直到最后一個函數返回整個處理器的輸出。
- 默認情況下,當遇到None值(list中有None值)的時候停止處理。可以通過傳遞參數stop_on_none = False改變這種行為。
- 每個函數可以選擇接收一個loader_context參數。
3.6.5. MapCompose
- 與Compose處理器類似,區別在于各個函數結果在內部傳遞的方式(會涉及到list對象解包的步驟):
- 輸入值是被迭代的處理的,List對象中的每一個元素被單獨傳入,第一個函數進行處理,然后處理的結果被連接起來形成一個新的迭代器,并被傳入第二個函數,以此類推,直到最后一個函數。最后一個函數的輸出被連接起來形成處理器的輸出。
- 每個函數能返回一個值或者一個值列表,也能返回None(會被下一個函數所忽略)
- 這個處理器提供了很方便的方式來組合多個處理單值的函數。因此它常用于輸入處理器,因為傳遞過來的是一個List對象。
- 與Compose處理器類似,它也能接受Loader context。
3.7. 重用和擴展ItemLoaders
3.7.1. 添加默認的處理機制
- 從上面的信息來看,ItemLoaders是非常靈活的,但是假設有個需求,所有的字段,我們都要去取第一個,那么如果有300個字段,我們就要添加300次,每個都要寫,就會覺得很麻煩。那么有沒有辦法統一設置呢,答案是有的,如下:
- 如果想要實現每個字段都只取第一個,那么可以定義一個自己的ItemLoader類:ArticleItemLoader(繼承自ItemLoader類)
- 我們首先可以看一下原始的 ItemLoader 的定義:
- 可以定義一個自己的ItemLoader類:ArticleItemLoader,繼承自ItemLoader類, 同時改寫(重寫)default_output_processor
- 然后在boleSpider中使用時,我們就不能再簡單的使用原有的ItemLoader,而是使用我們自己定義的 ArticleItemLoader 來填充數據:
3.7.2. 重寫,覆蓋默認的處理機制
- 上面我們實現了所有字段都只取第一個的功能,但是如果有一些字段,我不需要取第一個,而是有其他的處理方式呢?
- 那就需要重寫這個字段的輸出處理器(output_processor)。 下面的例子是,首先在輸入處理器中將 “評論” 這樣的字符過濾掉,然后將list中所有的元素用”,” 連接起來,成為一個字符串。
- 而如果,有些字段我們不想做任何處理,也不想去取第一個元素,那么我們怎么做呢?
- 因為,目前所有的字段都默認設置為去取第一個非空元素,所以,我們需要將這個處理去掉。這個地方尤其要引起重視,因為很容易遺忘自己有這個默認設置。處理方式如下:
總結
以上是生活随笔為你收集整理的如何使用scrapy中的ItemLoader提取数据?的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: word2019如何清除折叠黑色三角形的
- 下一篇: cocos2d-x 移动MM平台SDK接