介紹:
所謂網絡爬蟲,就是一個在網上到處或定向抓取數據的程序,當然,這種說法不夠專業,更專業的描述就是,抓取特定網站網頁的HTML數據。不過由於一個網站的網頁很多,而我們又不可能事先知道所有網頁的URL地址,所以,如何保證我們抓取到了網站的所有HTML頁面就是一個有待考究的問題了。
一般的方法是,定義一個入口頁面,然後一般一個頁面會有其他頁面的URL,於是從當前頁面獲取到這些URL加入到爬蟲的抓取隊列中,然後進入到新新頁面後再遞歸的進行上述的操作,其實說來就跟深度遍歷或廣度遍歷一樣。
上面介紹的只是爬蟲的一些概念而非搜索引擎,實際上搜索引擎的話其系統是相當複雜的,爬蟲只是搜索引擎的一個子系統而已。下面介紹一個開源的爬蟲框架Scrapy。
一、概述
Scrapy是一個用 Python 寫的 Crawler Framework ,簡單輕巧,並且非常方便,並且官網上說已經在實際生產中在使用了,不過現在還沒有 Release 版本,可以直接使用他們的 Mercurial 倉庫裏抓取源碼進行安裝。
Scrapy 使用 Twisted 這個異步網絡庫來處理網絡通訊,架構清晰,並且包含了各種中間件接口,可以靈活的完成各種需求。整體架構如下圖所示:
綠線是數據流向,首先從初始 URL 開始,Scheduler 會將其交給 Downloader 進行下載,下載之後會交給 Spider 進行分析,Spider 分析出來的結果有兩種:一種是需要進一步抓取的鏈接,例如之前分析的“下一頁”的鏈接,這些東西會被傳回 Scheduler ;另一種是需要保存的數據,它們則被送到 Item Pipeline 那裏,那是對數據進行後期處理(詳細分析、過濾、存儲等)的地方。另外,在數據流動的通道里還可以安裝各種中間件,進行必要的處理。
二、組件
1、Scrapy Engine(Scrapy引擎)
Scrapy引擎是用來控制整個系統的數據處理流程,並進行事務處理的觸發。更多的詳細內容可以看下面的數據處理流程。
2、Scheduler(調度)
調度程序從Scrapy引擎接受請求並排序列入隊列,並在Scrapy引擎發出請求後返還給他們。
3、Downloader(下載器)
下載器的主要職責是抓取網頁並將網頁內容返還給蜘蛛( Spiders)。
4、Spiders(蜘蛛)
蜘蛛是有Scrapy用戶自己定義用來解析網頁並抓取制定URL返回的內容的類,每個蜘蛛都能處理一個域名或一組域名。換句話說就是用來定義特定網站的抓取和解析規則。
蜘蛛的整個抓取流程(週期)是這樣的:
- 首先獲取第一個URL的初始請求,當請求返回後調取一個回調函數。第一個請求是通過調用start_requests()方法。該方法默認從start_urls中的Url中生成請求,並執行解析來調用回調函數。
- 在回調函數中,你可以解析網頁響應並返回項目對象和請求對象或兩者的迭代。這些請求也將包含一個回調,然後被Scrapy下載,然後有指定的回調處理。
- 在回調函數中,你解析網站的內容,同程使用的是Xpath選擇器(但是你也可以使用BeautifuSoup, lxml或其他任何你喜歡的程序),並生成解析的數據項。
- 最後,從蜘蛛返回的項目通常會進駐到項目管道。
5、Item Pipeline(項目管道)
項目管道的主要責任是負責處理有蜘蛛從網頁中抽取的項目,他的主要任務是清晰、驗證和存儲數據。當頁面被蜘蛛解析後,將被髮送到項目管道,並經過幾個特定的次序處理數據。每個項目管道的組件都是有一個簡單的方法組成的Python類。他們獲取了項目並執行他們的方法,同時他們還需要確定的是是否需要在項目管道中繼續執行下一步或是直接丟棄掉不處理。
項目管道通常執行的過程有:
- 清洗HTML數據
- 驗證解析到的數據(檢查項目是否包含必要的字段)
- 檢查是否是重複數據(如果重複就刪除)
- 將解析到的數據存儲到數據庫中
6、Downloader middlewares(下載器中間件)
下載中間件是位於Scrapy引擎和下載器之間的鉤子框架,主要是處理Scrapy引擎與下載器之間的請求及響應。它提供了一個自定義的代碼的方式來拓展Scrapy的功能。下載中間器是一個處理請求和響應的鉤子框架。他是輕量級的,對Scrapy盡享全局控制的底層的系統。
7、Spider middlewares(蜘蛛中間件)
蜘蛛中間件是介於Scrapy引擎和蜘蛛之間的鉤子框架,主要工作是處理蜘蛛的響應輸入和請求輸出。它提供一個自定義代碼的方式來拓展Scrapy的功能。蛛中間件是一個掛接到Scrapy的蜘蛛處理機制的框架,你可以插入自定義的代碼來處理髮送給蜘蛛的請求和返回蜘蛛獲取的響應內容和項目。
8、Scheduler middlewares(調度中間件)
調度中間件是介於Scrapy引擎和調度之間的中間件,主要工作是處從Scrapy引擎發送到調度的請求和響應。他提供了一個自定義的代碼來拓展Scrapy的功能。
三、數據處理流程Scrapy的整個數據處理流程有Scrapy引擎進行控制,其主要的運行方式爲:
- 引擎打開一個域名,時蜘蛛處理這個域名,並讓蜘蛛獲取第一個爬取的URL。
- 引擎從蜘蛛那獲取第一個需要爬取的URL,然後作爲請求在調度中進行調度。
- 引擎從調度那獲取接下來進行爬取的頁面。
- 調度將下一個爬取的URL返回給引擎,引擎將他們通過下載中間件發送到下載器。
- 當網頁被下載器下載完成以後,響應內容通過下載中間件被髮送到引擎。
- 引擎收到下載器的響應並將它通過蜘蛛中間件發送到蜘蛛進行處理。
- 蜘蛛處理響應並返回爬取到的項目,然後給引擎發送新的請求。
- 引擎將抓取到的項目項目管道,並向調度發送請求。
- 系統重複第二部後面的操作,直到調度中沒有請求,然後斷開引擎與域之間的聯繫。
安裝:
Scrapy是一個快速,高層次的屏幕抓取和web抓取框架,用於抓取web站點並從頁面中提取結構化的數據。Scrapy用途廣泛,可以用於數據挖掘、監測和自動化測試。Scrapy吸引人的地方在於它是一個框架,任何人都可以根據需求方便的修改。它也提供了多種類型爬蟲的基類,如BaseSpider、sitemap爬蟲等,最新版本又提供了web2.0爬蟲的支持。
下面介紹Scrapy在windows下的安裝:
首先下載windows版:Scrapy-0.15.0.2842.win32.exe,直接安裝。
安裝之後不能直接運行scrapy提供的test,會提示錯誤,因爲scrapy基於其他一些python庫,需要把這些庫都安裝才行。
Twisted:Twisted Matrix 是一種用來進行網絡服務和應用程序編程的純 Python 框架,雖然 Twisted Matrix 中有大量鬆散耦合的模塊化組件,但該框架的中心概念還是非阻塞異步服務器這一思想。Twisted的安裝也非常簡單,在這裏直接下載windows平臺下的相應版本即可:http://pypi.python.org/packages/2.7/T/Twisted/,
zope.interface:在這裏下載http://pypi.python.org/pypi/zope.interface/3.8.0#downloads。zope.interface沒有提供windows平臺下的exe版,只提供了windows平臺下的egg包。
ez_setup:下載http://pypi.python.org/pypi/ez_setup,安裝。將egg文件放置在{python安裝目錄}\Scripts目錄下。
打開CMD並切換至scripts目錄,easy_install zope.interface-3.8.0-py2.6-win32.egg安裝。
w3lib:zope.interface問題解決之後還會提示缺少w3lib,下載http://pypi.python.org/pypi/w3lib後安裝即可
libxml2:使用scrapy的html解析功能時,會提示你缺少libxml2,所以我們先把這個也裝上,地址http://xmlsoft.org/sources/win32/python/,下載相應的版本即可。
至此就可以使用Scrapy玩spider了,大家可以根據文檔寫一個簡單的爬蟲試試,實際上使用scrapy做一個簡易的爬蟲甚至只需要幾行代碼就可以了,以後有空再詳細說說使用方法,本文不做更多描述。
入門:
本文參考Scrapy Tutorial裏面的文檔,翻譯出來加上自己的理解,供大家學習。
在本文中,我們將學會如何使用Scrapy建立一個爬蟲程序,並爬取指定網站上的內容,這一切在Scrapy框架內實現將是很簡單輕鬆的事情。
本教程主要內容包括一下四步:
1. 創建一個新的Scrapy Project
2. 定義你需要從網頁中提取的元素Item
3. 實現一個Spider類,通過接口完成爬取URL和提取Item的功能
4. 實現一個Item PipeLine類,完成Item的存儲功能
新建工程
首先,爲我們的爬蟲新建一個工程,首先進入一個目錄(任意一個我們用來保存代碼的目錄),執行:
scrapy startproject Domz
最後的Domz就是項目名稱。這個命令會在當前目錄下創建一個新目錄Domz,結構如下:
dmoz/
scrapy.cfg
dmoz/
__init__.py
items.py
pipelines.py
settings.py
spiders/
__init__.py
scrapy.cfg: 項目配置文件
items.py: 需要提取的數據結構定義文件
pipelines.py: 管道定義,用來對items裏面提取的數據做進一步處理,如保存等
settings.py: 爬蟲配置文件
spiders: 放置spider的目錄
定義Item
在items.py裏面定義我們要抓取的數據:
from scrapy.item import Item, Field
class DmozItem(Item):
title = Field()
link = Field()
desc = Field()
這裏我們需要獲取dmoz頁面上的標題,鏈接,描述,所以定義一個對應的items結構,不像Django裏面models的定義有那麼多種類的Field,這裏只有一種就叫Field(),再複雜就是Field可以接受一個default值。
實現Spider
spider只是一個繼承字scrapy.spider.BaseSpider的Python類,有三個必需的定義的成員
name: 名字,這個spider的標識
start_urls: 一個url列表,spider從這些網頁開始抓取
parse(): 一個方法,當start_urls裏面的網頁抓取下來之後需要調用這個方法解析網頁內容,同時需要返回下一個需要抓取的網頁,或者返回items列表
所以在spiders目錄下新建一個spider,dmoz_spider.py:
class DmozSpider(BaseSpider):
name = "dmoz.org"
start_urls = [
"http://www.dmoz.org/Computers/Programming/Languages/Python/Books/",
"http://www.dmoz.org/Computers/Programming/Languages/Python/Resources/"
]
def parse(self, response):
filename = response.url.split("/")[-2]
open(filename, 'wb').write(response.body)
提取Item
提取數據到Items裏面,主要用到XPath提取網頁數據:
scrapy有提供兩個XPath選擇器,HtmlXPathSelector和XmlXPathSelector,一個用於HTML,一個用於XML,XPath選擇器有三個方法
select(xpath): 返回一個相對於當前選中節點的選擇器列表(一個XPath可能選到多個節點)
extract(): 返回選擇器(列表)對應的節點的字符串(列表)
re(regex): 返回正則表達式匹配的字符串(分組匹配)列表
一種很好的方法是在Shell裏面對XPath進行測試:
scrapy shell http://www.dmoz.org/Computers/Programming/Languages/Python/Books/
現在修改parse()方法看看如何提取數據到items裏面去:
def parse(self, response):
hxs = HtmlXPathSelector(response)
sites = hxs.select('//ul/li')
items = []
for site in sites:
item = DmozItem()
item['title'] = site.select('a/text()').extract()
item['link'] = site.select('a/@href').extract()
item['desc'] = site.select('text()').extract()
items.append(item)
return items
實現PipeLine
PipeLine用來對Spider返回的Item列表進行保存操作,可以寫入到文件、或者數據庫等。
PipeLine只有一個需要實現的方法:process_item,例如我們將Item保存到一個文件中:
def __init__(self):
self.file = open('jingdong.txt', 'wb')
def process_item(self, item, spider):
self.file.write(item['title'] + '\t'+ item['link'] + '\t' + item['desc']+'\n')
到現在,我們就完成了一個基本的爬蟲的實現,可以輸入下面的命令來啓動這個Spider:
scrapy crawl dmoz.org
Scrapy之URL解析與遞歸爬取:
前面介紹了Scrapy如何實現一個最簡單的爬蟲,但是這個Demo裏只是對一個頁面進行了抓取。在實際應用中,爬蟲一個重要功能是”發現新頁面”,然後遞歸的讓爬取操作進行下去。
發現新頁面的方法很簡單,我們首先定義一個爬蟲的入口URL地址,比如Scrapy入門教程中的start_urls,爬蟲首先將這個頁面的內容抓取之後,解析其內容,將所有的鏈接地址提取出來。這個提取的過程是很簡單的,通過一個html解析庫,將這樣的節點內容提取出來,href參數的值就是一個新頁面的URL。獲取這個URL值之後,將其加入到任務隊列中,爬蟲不斷的從隊列中取URL即可。這樣,只需要爲爬蟲定義一個入口的URL,那麼爬蟲就能夠自動的爬取到指定網站的絕大多數頁面。
當然,在具體的實現中,我們還需要對提取的URL做進一步處理:
1. 判斷URL指向網站的域名,如果指向的是外部網站,那麼可以將其丟棄
2. URL去重,可以將所有爬取過的URL存入數據庫中,然後查詢新提取的URL在數據庫中是否存在,如果存在的話,當然就無需再去爬取了。
下面介紹一下如何在Scrapy中完成上述這樣的功能。
我們只需要改寫spider的那個py文件即可,修改parse()方法代碼如下:
from scrapy.selector import HtmlXPathSelector
def parse(self, response):
hxs = HtmlXPathSelector(response)
items = []
newurls = hxs.select('//a/@href').extract()
validurls = []
for url in newurls:
#判斷URL是否合法
if true:
validurls.append(url)
items.extend([self.make_requests_from_url(url).replace(callback=self.parse) for url in validurls])
sites = hxs.select('//ul/li')
items = []
for site in sites:
item = DmozItem()
item['title'] = site.select('a/text()').extract()
item['link'] = site.select('a/@href').extract()
item['desc'] = site.select('text()').extract()
items.append(item)
return items