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是將提取和資料的過濾放到同一個函式當中,將資料的提取和資料的分離分成兩個部分,使程式碼看起來更加的清晰,程式碼更加的整潔。也可以將資料的處理函式單獨定義,也可以對一個數據使用多個處理的函式,這樣的話對程式碼的重用有著非常好的實現。