記得n年前專案需要一個靈活的爬蟲工具,就組織了一個小團隊用Java實現了一個爬蟲框架,可以根據目標網站的結構、地址和需要的內容,做簡單的配置開發,即可實現特定網站的爬蟲功能。因為要考慮到各種特殊情形,開發還耗了不少人力。後來發現了Python下有這個Scrapy工具,瞬間覺得之前做的事情都白費了。對於一個普通的網路爬蟲功能,Scrapy完全勝任,並把很多複雜的程式設計都包裝好了。本文會介紹如何Scrapy構建一個簡單的網路爬蟲。
一個基本的爬蟲工具,它應該具備以下幾個功能:
我們來看下Scrapy怎麼做到這些功能的。首先準備Scrapy環境,你需要安裝Python(本文使用v2.7)和pip,然後用pip來安裝lxml和scrapy。個人強烈建議使用virtualenv來安裝環境,這樣不同的專案之間不會衝突。詳細步驟這裡就不贅述了。對於Mac使用者要注意,當使用pip安裝lxml時,會出現類似於的下面錯誤:
Error: #include “xml/xmlversion.h” not found
解決這個問題,你需要先安裝Xcode的command line tools,具體的方法是在命令列執行下面的命令即可。
1 |
$ xcode-select --install |
環境安裝好之後,我們來用Scrapy實現一個簡單的爬蟲,抓取本部落格網站的文章標題,地址和摘要。
- 建立工程
1 |
$ scrapy startproject my_crawler |
該命令會在當前目錄下建立一個名為”my_crawler”的工程,工程的目錄結構如下
1 2 3 4 5 6 7 8 |
my_crawler |- my_crawler | |- spiders | | |- __init__.py | |- items.py | |- pipelines.py | |- setting.py |- scrapy.cfg |
- 設定待抓取內容的欄位,本例中就是文章的標題,地址和摘要
修改”items.py”檔案,在”MyCrawlerItem”類中加上如下程式碼:
1 2 3 4 5 6 7 8 |
# -*- coding: utf-8 -*- import scrapy class MyCrawlerItem(scrapy.Item): title = scrapy.Field() # 文章標題 url = scrapy.Field() # 文章地址 summary = scrapy.Field() # 文章摘要 pass |
- 編寫網頁解析程式碼
在”my_crawler/spiders”目錄下,建立一個名為”crawl_spider.py”檔案(檔名可以任意取)。程式碼如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
# -*- coding: utf-8 -*- import scrapy from scrapy.linkextractors import LinkExtractor from scrapy.spiders import CrawlSpider, Rule from my_crawler.items import MyCrawlerItem class MyCrawlSpider(CrawlSpider): name = 'my_crawler' # Spider名,必須唯一,執行爬蟲命令時使用 allowed_domains = ['bjhee.com'] # 限定允許爬的域名,可設定多個 start_urls = [ "http://www.bjhee.com", # 種子URL,可設定多個 ] rules = ( # 對應特定URL,設定解析函式,可設定多個 Rule(LinkExtractor(allow=r'/page/[0-9]+'), # 指定允許繼續爬取的URL格式,支援正則 callback='parse_item', # 用於解析網頁的回撥函式名 follow=True ), ) def parse_item(self, response): # 通過XPath獲取Dom元素 articles = response.xpath('//*[@id="main"]/ul/li') for article in articles: item = MyCrawlerItem() item['title'] = article.xpath('h3[@class="entry-title"]/a/text()').extract()[0] item['url'] = article.xpath('h3[@class="entry-title"]/a/@href').extract()[0] item['summary'] = article.xpath('div[2]/p/text()').extract()[0] yield item |
對於XPath不熟悉的朋友,可以通過Chrome的debug工具獲取元素的XPath。
- 讓我們測試下爬蟲的效果
在命令列中輸入:
1 |
$ scrapy crawl my_crawler |
注意,這裡的”my_crawler”就是你在”crawl_spider.py”檔案中起的Spider名。
沒過幾秒鐘,你就會看到要抓取的欄位內容列印在控制檯上了。就是這麼神奇!Scrapy將HTTP(S)請求,內容下載,待抓取和已抓取的URL佇列的管理都封裝好了。你的主要工作基本上就是設定URL規則及編寫解析的方法。
我們將抓取的內容儲存為JSON檔案:
1 |
$ scrapy crawl my_crawler -o my_crawler.json -t json |
你可以在當前目錄下,找到檔案”my_crawler.json”,裡面儲存的就是我們要抓取的欄位資訊。(引數”-t json”可以省去)
- 將結果儲存到資料庫
這裡我們採用MongoDB,你需要先安裝Python的MongoDB庫”pymongo”。編輯”my_crawler”目錄下的”pipelines.py”檔案,在”MyCrawlerPipeline”類中加上如下程式碼:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
# -*- coding: utf-8 -*- import pymongo from scrapy.conf import settings from scrapy.exceptions import DropItem class MyCrawlerPipeline(object): def __init__(self): # 設定MongoDB連線 connection = pymongo.Connection( settings['MONGO_SERVER'], settings['MONGO_PORT'] ) db = connection[settings['MONGO_DB']] self.collection = db[settings['MONGO_COLLECTION']] # 處理每個被抓取的MyCrawlerItem項 def process_item(self, item, spider): valid = True for data in item: if not data: # 過濾掉存在空欄位的項 valid = False raise DropItem("Missing {0}!".format(data)) if valid: # 也可以用self.collection.insert(dict(item)),使用upsert可以防止重複項 self.collection.update({'url': item['url']}, dict(item), upsert=True) return item |
再開啟”my_crawler”目錄下的”settings.py”檔案,在檔案末尾加上pipeline的設定:
1 2 3 4 5 6 7 8 9 10 11 |
ITEM_PIPELINES = { 'my_crawler.pipelines.MyCrawlerPipeline': 300, # 設定Pipeline,可以多個,值為執行優先順序 } # MongoDB連線資訊 MONGO_SERVER = 'localhost' MONGO_PORT = 27017 MONGO_DB = 'bjhee' MONGO_COLLECTION = 'articles' DOWNLOAD_DELAY=2 # 如果網路慢,可以適當加些延遲,單位是秒 |
- 執行爬蟲
1 |
$ scrapy crawl my_crawler |
別忘了啟動MongoDB並建立”bjhee”資料庫哦。現在你可以在MongoDB裡查詢到記錄了。
總結下,使用Scrapy來構建一個網路爬蟲,你需要做的就是:
- “items.py”中定義爬取欄位
- 在”spiders”目錄下建立你的爬蟲,編寫解析函式和規則
- “pipelines.py”中對爬取後的結果做處理
- “settings.py”設定必要的引數
其他的事情,Scrapy都幫你做了。下圖就是Scrapy具體工作的流程。怎麼樣?開始寫一個自己的爬蟲吧。
本例中的程式碼可以在這裡下載。