在利用Scrapy框架開發網頁爬蟲的過程中,成功取得想要蒐集的資料後,下一個步驟就是資料的儲存,像是存入資料庫或是檔案中等,這時候,就會需要使用到Scrapy框架的item資料模型及pipeline資料模型管道模組(Module),來幫助開發人員建立好維護的資料處理方式。
所以本文將延續[Scrapy教學5]掌握Scrapy框架重要的XPath定位元素方法文章所建立的Scrapy網頁爬蟲專案,來和大家分享如何將爬取到的網頁資料,存入MySQL資料庫,其中的重點包含:
- Scrapy spider網頁爬蟲
- Scrapy item資料模型
- Scrapy pipeline資料模型管道
一、Scrapy spider網頁爬蟲
假設本文想要來蒐集INSIDE硬塞的網路趨勢觀察網站-AI新聞網頁的「文章標題」、「發佈日期」及「作者」,這時候可以在這三個地方點擊滑鼠右鍵,選擇「檢查」,來檢視HTML原始碼,如下圖:
開啟Scrapy網頁爬蟲專案,在spiders / inside.py的parse()方法(Method)中,利用[Scrapy教學5]掌握Scrapy框架重要的XPath定位元素方法文章中分享的xpath()方法(Method),來爬取網頁中所有的「文章標題」、「發佈日期」及「作者」,如下範例:
import scrapy class InsideSpider(scrapy.Spider): name = 'inside' allowed_domains = ['www.inside.com.tw'] start_urls = ['https://www.inside.com.tw/tag/ai'] def parse(self, response): #爬取文章標題 post_titles = response.xpath( "//h3[@class='post_title']/a[@class='js-auto_break_title']/text()" ).getall() #爬取發佈日期 post_dates = response.xpath( "//li[@class='post_date']/span/text()" ).getall() #爬取作者 post_authors = response.xpath( "//span[@class='post_author']/a/text()" ).getall()
有收看[Scrapy教學5]掌握Scrapy框架重要的XPath定位元素方法文章就會知道,getall()方法(Method)回傳的是一個串列(List),所以要取得其中的資料,就需要透過迴圈來進行讀取,如下範例:
import scrapy class InsideSpider(scrapy.Spider): name = 'inside' allowed_domains = ['www.inside.com.tw'] start_urls = ['https://www.inside.com.tw/tag/ai'] def parse(self, response): # 爬取文章標題 post_titles = response.xpath( "//h3[@class='post_title']/a[@class='js-auto_break_title']/text()" ).getall() # 爬取發佈日期 post_dates = response.xpath( "//li[@class='post_date']/span/text()" ).getall() # 爬取作者 post_authors = response.xpath( "//span[@class='post_author']/a/text()" ).getall() for data in zip(post_titles, post_dates, post_authors): NewsScraperItem = { "post_title": data[0], "post_date": data[1], "post_author": data[2] } yield NewsScraperItem
以上範例第26行,使用了zip()函數將爬取的「文章標題」、「發佈日期」及「作者」串列(List)資料,打包為一個一個的元組(Tuple),如下範例:
('千萬美金規模!台灣 AI 晶片新創耐能獲得鴻海、華邦電戰略投資', '2021/01/20', 'Chris'), ('微軟新專利:跟《黑鏡》一樣的模擬真人聊天 AI!', '2021/01/15', 'Chris'), ('宏正自動科技 ATEN 攜手 CKmates,導入 AWS 雲端遷移服務提升全球網站運作 3 倍效能', '2021/01/21', '廣編企劃'), ...
而在迴圈讀取的過程中,就需要定義一個物件,來分別儲存每一個元組(Tuple)的「文章標題」、「發佈日期」及「作者」資料,以便能夠在「資料模型管道檔案(pipeline.py)」中,進行後續資料處理使用。
這時候就需要像第27行一樣,利用「資料模型檔案(items.py)」中的NewsScraperItem類別(Class)來裝載資料,並且使用「post_title」、「post_date」及「post_author」三個欄位,來分別裝載元組(Tuple)中的爬取資料,接著,透過yield關鍵字回傳到「資料模型管道檔案(pipeline.py)」中進行運用。
而NewsScraperItem類別(Class)的「post_title」、「post_date」及「post_author」三個屬性欄位,目前尚未定義,這也就是接下來所要進行的步驟。
二、Scrapy item資料模型
Scrapy框架的item資料模型,也就是在剛剛範例中所使用的NewsScraperItem類別(Class),主要就是用來定義在「資料模型管道檔案(pipeline.py)」中,資料處理時所會使用到的欄位。
而本文在「資料模型管道檔案(pipeline.py)」中所要進行的資料處理,就是存入MySQL資料庫,其中就會使用到「post_title」、「post_date」及「post_author」三個欄位,這時候就需要在「資料模型檔案(items.py)」中定義,如下範例:
import scrapy class NewsScraperItem(scrapy.Item): # define the fields for your item here like: # name = scrapy.Field() post_title = scrapy.Field() post_date = scrapy.Field() post_author = scrapy.Field()
完成NewsScraperItem資料模型的定義後,接下來就可以在「資料模型管道檔案(pipeline.py)」中,來存取其中所裝載的資料。
三、Scrapy pipeline資料模型管道
在開始將Scrapy網頁爬蟲取得的資料寫入MySQL資料庫前,大家可以先參考[Python實戰應用]掌握Python連結MySQL資料庫的重要操作文章的第二節安裝MySQL,接下來,就可以建立本文所需的「insidedb」資料庫,如下圖:
同樣點擊Apply按鈕就完成了,如下圖:
MYSQL_HOST = 'localhost' MYSQL_DATABASE = 'insidedb' MYSQL_USERNAME = 'root' MYSQL_PASSWORD = '密碼'
另外,也需要將以下預設為註解的「資料模型管道設定」開啟,如下範例:
ITEM_PIPELINES = { 'news_scraper.pipelines.NewsScraperPipeline': 300, }
設定完成後,開啟「資料模型管道檔案(pipeline.py)」,就可以來開發網頁爬蟲後續資料處理的動作,本文則以存入MySQL資料庫為例,所以需引入settings及pymysql模組(Module),如下範例:
from itemadapter import ItemAdapter from news_scraper import settings import pymysql
接著,在NewsScraperPipeline類別(Class)中,新增一個建構式(Constructor),來初始化MySQL的資料庫連線,如下範例:
from itemadapter import ItemAdapter from news_scraper import settings import pymysql class NewsScraperPipeline: def __init__(self): self.connect = pymysql.connect( host=settings.MYSQL_HOST, db=settings.MYSQL_DATABASE, user=settings.MYSQL_USERNAME, passwd=settings.MYSQL_PASSWORD, charset='utf8' ) self.cursor = self.connect.cursor()
以上範例,使用pymysql模組(Module)的connect()方法(Method),傳入剛剛在settings.py檔案中所設定的MySQL資料庫連線資訊,來建立連線物件,以及利用cursor()方法(Method)建立cursor物件,以便能夠對資料庫進行操作。
接下來,在process_item()方法(Method)中,就可以來開發Scrapy網頁爬蟲存入資料到MySQL資料庫的邏輯,如下範例:
from itemadapter import ItemAdapter from news_scraper import settings import pymysql class NewsScraperPipeline: def __init__(self): self.connect = pymysql.connect( host=settings.MYSQL_HOST, db=settings.MYSQL_DATABASE, user=settings.MYSQL_USERNAME, passwd=settings.MYSQL_PASSWORD, charset='utf8' ) self.cursor = self.connect.cursor() def process_item(self, item, spider): sql = 'INSERT INTO posts(post_title, post_date, post_author)VALUES(%s,%s,%s) ' data = (item['post_title'], item['post_date'], item['post_author']) self.cursor.execute(sql, data) return item
首先,第22行定義寫入的SQL指令,接著,第24行利用item資料模型來存取爬到的網頁資料,並且打包為一個元組(Tuple),第26行將兩者傳入execute()方法(Method)執行。
由於有多筆資料的寫入,所以最後一定有一個確定(commit)的動作,確保所有資料都正確寫入,這時候就可以再增加一個Scrapy框架的close_spider()內建方法(Method),在網頁爬蟲執行結束時,進行確定(commit)的動作及關閉資料庫的連線,如下範例:
from itemadapter import ItemAdapter from news_scraper import settings import pymysql class NewsScraperPipeline: def __init__(self): self.connect = pymysql.connect( host=settings.MYSQL_HOST, db=settings.MYSQL_DATABASE, user=settings.MYSQL_USERNAME, passwd=settings.MYSQL_PASSWORD, charset='utf8' ) self.cursor = self.connect.cursor() def process_item(self, item, spider): sql = 'INSERT INTO posts(post_title, post_date, post_author)VALUES(%s,%s,%s) ' data = (item['post_title'], item['post_date'], item['post_author']) self.cursor.execute(sql, data) return item def close_spider(self, spider): self.connect.commit() self.connect.close()
最後,利用以下的指令來執行Scrapy框架的inside網頁爬蟲,如下:
$ scrapy crawl inside
開啟MySQL資料庫,就可以看到Scrapy網頁爬蟲成功將爬取的資料寫入資料庫中,如下圖:
四、小結
想要在Scrapy網頁爬蟲框架中,將爬取的資料存入外部資料庫(如:MySQL),就會需要使用到Scrapy框架的item及pipeline兩個模組(Module)功能,而各自主要負責的任務如下:
- item.py:定義在pipeline.py檔案中所會使用到的資料處理欄位。
- pipeline.py:開發網頁爬蟲後續的資料處理動作。
也就是因為Scrapy框架有這樣的分工架構,所以對於大型的網頁爬蟲專案,能夠有一定的維護性。希望本文的實作能夠幫助到大家,歡迎分享給身邊對Python網頁爬蟲有興趣的朋友,有任何的問題或想法,可以在底下留言和我分享唷。
如果您喜歡我的文章,請幫我按五下Like(使用Google或Facebook帳號免費註冊),支持我創作教學文章,回饋由LikeCoin基金會出資,完全不會花到錢,感謝大家。
不要用sqlite用sqlalchemy會不會比較好?
回覆刪除另外scrapy有整合orm套件的做法嗎?像flask_sqlachemy或django
請問依照您的步驟下來
回覆刪除pipelines.py裡面的process_item函數是不會被啟動的耶
這樣好像就沒有寫進去資料庫裡面了??
再麻煩幫忙看看謝謝
Zip在python 2 or 3 使用上, 會有些微不同, 可以檢查看看你的環境是不是這裡出了問題
刪除