1. 程式人生 > >Python爬蟲-Scrapy的item loader

Python爬蟲-Scrapy的item loader

使用Scrapy爬取伯樂線上的文章,將爬取的資料儲存到MySQL資料庫中。

建立專案

使用Scrapy命令來建立專案。

梳理整體邏輯關係

我們先來分析一下整體的流程,我們想要獲取,文章的圖片,標題,釋出的時間,詳情頁的連結,點贊數,收藏數,評論數。我們首先需要獲取的是每一篇文章的詳情頁連結,圖片地址,以及,下一頁的地址。而後進入到文章的詳情頁,去獲取文章的標題,釋出日期,點贊數,評論數,收藏數,然後將這些資料儲存到MySQL資料庫中。OK!我們這樣編寫程式碼。

blog.py

class BlogSpider(scrapy.Spider):
    name = 'blog'
    allowed_domains = ['blog.jobbole.com']
    start_urls = ['http://blog.jobbole.com/all-posts/']
    def parse(self, response):
        item_list=response.xpath('//div[@class="post floated-thumb"]')
        for item in item_list:
            # 獲取圖片的連結
            image=item.xpath('.//div[@class="post-thumb"]/a/img/@src').extract_first('')
            # 獲取詳情頁的連結
            url=item.xpath('.//a[@class="archive-title"]/@href').extract_first('')
            # 將詳情頁連結,以及圖片連結傳遞給下一個方法
            yield  scrapy.Request(url=url,meta={'img':image},callback=self.get_detail_with_url)
            # 獲取下一頁的連結
        next_url=response.xpath('//a[@class="next page-numbers"]/@href').extract()
        if len(next_url)!=0:
            page_url=next_url[0]
            yield  scrapy.Request(url=page_url,callback=self.parse)
    def get_detail_with_url(self,response):
        # 接收圖片的連結
        img=response.meta['img']
        print(img)
        # 獲取標題
        title=response.xpath('//div[@class="entry-header"]/h1/text()').extract_first('')
        print(title)
        # 獲取時間
        date_time=response.xpath('//div[@class="entry-meta"]/p[@class="entry-meta-hide-on-mobile"]/text()').extract_first('').strip()
        time=date_time.split('·')[0]
        print(time)
        # 詳情頁地址
        detail_url=response.url
        print(detail_url)
        # 獲取點贊數
        dian_zan=response.xpath('//h10/text()').extract_first('')
        print(dian_zan)
        # 獲取收藏數
        book_mark=response.xpath('//span[@class=" btn-bluet-bigger href-style bookmark-btn  register-user-only "]/text()').extract_first('')
        # 對數字進行單獨的取出
        book_mark_array=book_mark.split(' ')
        book_mark_num=0
        if len(book_mark_array[1])!=0:
            book_mark_num=int(book_mark_array[1])
        print(book_mark_num)
        # 獲取評論數
        comment=response.xpath('//a[@href="#article-comment"]/span/text()').extract_first('')
        # 對數字進行單獨的取出
        comment_arrat=comment.split(' ')
        comment_num=0
        if len(comment_arrat[1])!=0:
            comment_num=int(comment_arrat[1])
        print(comment_num)
        print('------------------------------------------------------')
        item=JobboleItem()
        item['img']=img
        item['title']=title
        item['date_time']=time
        item['detail_url']=detail_url
        item['dian_zan']=dian_zan
        item['book_mark']=book_mark_num
        item['comment']=comment_num
        yield item

items.py

class JobboleItem(scrapy.Item):
    # define the fields for your item here like:
    # name = scrapy.Field()
    img=scrapy.Field()
    title=scrapy.Field()     
    date_time=scrapy.Field()
    detail_url=scrapy.Field()
    dian_zan=scrapy.Field()
    book_mark=scrapy.Field()
    comment=scrapy.Field()
    pass

建立MySQL資料庫

pipelines.py

import pymysql
class JobbolePipeline(object):
#連線資料庫
    def __init__(self):
        self.connect=pymysql.connect(host='localhost',user='root',password='123456',db='jobbole',port=3306)
        self.cursor=self.connect.cursor()
    def process_item(self, item, spider):
#往資料庫裡面寫入資料
        self.cursor.execute('insert into blog(img,title,detail_url,time,dian_zan,book_mark,comment)VALUES ("{}","{}","{}","{}","{}","{}","{}")'
                            .format(item['img'],item['title'],item['detail_url'],item['date_time'],item['dian_zan'],item['book_mark'],item['comment']))
        self.connect.commit()
        return item
    def close_spider(self,spider):
        self.cursor.close()
        self.connect.close()

settings.py

BOT_NAME = 'jobbole'

SPIDER_MODULES = ['jobbole.spiders']
NEWSPIDER_MODULE = 'jobbole.spiders'

ROBOTSTXT_OBEY = False

ITEM_PIPELINES = {
   'jobbole.pipelines.JobbolePipeline': 300,
}

輸入命令:scrapy crawl blog,開啟資料庫,重新整理一下。

這樣便算是完成了。但是我們可以看到,在blog.py中取出資料我們顯得非常的麻煩,我們不僅要取出,然後剝離提取等等,感覺很混亂,所以,我們想要讓這個程式碼順序呈現的更加清晰,我們需要使用itemloader。

-------------------------------------------------------------------------------------------------------------------------------------------------------------------------

blog.py

import scrapy
from ..items import JobboleItem
from ..items import  ArticleItemLoader

class BlogSpider(scrapy.Spider):
    name = 'blog'
    allowed_domains = ['blog.jobbole.com']
    start_urls = ['http://blog.jobbole.com/all-posts/']
    def parse(self, response):
        item_list=response.xpath('//div[@class="post floated-thumb"]')
        for item in item_list:
            # 獲取圖片的連結
            image=item.xpath('.//div[@class="post-thumb"]/a/img/@src').extract_first('')
            # 獲取詳情頁的連結
            url=item.xpath('.//a[@class="archive-title"]/@href').extract_first('')
            # 將詳情頁連結,以及圖片連結傳遞給下一個方法
            yield  scrapy.Request(url=url,meta={'img':image},callback=self.get_detail_with_url)
            # 獲取下一頁的連結
        next_url=response.xpath('//a[@class="next page-numbers"]/@href').extract()
        if len(next_url)!=0:
            page_url=next_url[0]
            yield  scrapy.Request(url=page_url,callback=self.parse)
    def get_detail_with_url(self,response):
     #建立itemLoader的例項化物件,需要傳入兩個引數
     #第一個值為item的例項化物件
     #第二個值為網頁的原始碼
        item_loader=ArticleItemLoader(item=JobboleItem(),response=response)
     #第一個值是設定field的名稱
     #第二個值是xpath路徑
        item_loader.add_xpath('title','//div[@class="entry-header"]/h1/text()')
        item_loader.add_value('img',[response.meta['img']])
        item_loader.add_xpath('date_time','//div[@class="entry-meta"]/p[@class="entry-meta-hide-on-mobile"]/text()')
        item_loader.add_value('detail_url',response.url)
        item_loader.add_xpath('dian_zan','//div[@class="post-adds"]//h10/text()')
        item_loader.add_xpath('book_mark','//span[@class=" btn-bluet-bigger href-style bookmark-btn  register-user-only "]/text()')
        item_loader.add_xpath('comment','//a[@href="#article-comment"]/span/text()')
        item=item_loader.load_item()
        yield item

items.py

import scrapy
from scrapy.loader import  ItemLoader
from scrapy.loader.processors import MapCompose,TakeFirst
import re
採用tmloader是資料分離資料的另外一種方式

def changeTitle(value):
    value='標題:'+value
    return value
def getNewTime(value):
    newTime=value.split('·')[0].strip()
    return newTime
def getNum(value):
    pattern=re.compile(r'\d+')
    result=re.findall(pattern,value)
    if result:
        return int(result[0])
    else:
        return 0
# 使用itemloader需要先繼承Iitmloader
class ArticleItemLoader(ItemLoader):
  #設定輸出內容的型別,TakeFirst()獲取所有資料的第一條資料  
    default_output_processor=TakeFirst()
class JobboleItem(scrapy.Item):
    # define the fields for your item here like:
    # name = scrapy.Field()
    img=scrapy.Field(
        input_processor=MapCompose
    )
    title=scrapy.Field(       
        input_processor=MapCompose(changeTitle)
    )
    date_time=scrapy.Field(
        input_processor=MapCompose(getNewTime)
    )
    detail_url=scrapy.Field()
    dian_zan=scrapy.Field()
    book_mark=scrapy.Field(
        input_processor=MapCompose(getNum)
    )
    comment=scrapy.Field(
        input_processor=MapCompose(getNum)
    )
    pass

pipelines.py和settings.py 還是照寫

輸入命令:scrapy crawl blog ,按下回車鍵。進入資料庫,點選重新整理。

既然我們已經有了item,那為什麼還有去使用itemloader呢,我們可以看到不管是使用xpath,都需要我們對資料進行正則的處理,會使我們的維護工作變得比較困難。使用itemloader是將提取和資料的過濾放到同一個函式當中,將資料的提取和資料的分離分成兩個部分,使程式碼看起來更加的清晰,程式碼更加的整潔。也可以將資料的處理函式單獨定義,也可以對一個數據使用多個處理的函式,這樣的話對程式碼的重用有著非常好的實現。