原文發表於:http://blog.javachen.com/2014/05/24/using-scrapy-to-cralw-data.html
Scrapy是Python開發的一個快速,高層次的螢幕抓取和web抓取框架,用於抓取web站點並從頁面中提取結構化的資料。Scrapy用途廣泛,可以用於資料探勘、監測和自動化測試。
- 官方主頁: http://www.scrapy.org/
- 中文文件:Scrapy 0.22 文件
- GitHub專案主頁:https://github.com/scrapy/scrapy
Scrapy 使用了 Twisted 非同步網路庫來處理網路通訊。整體架構大致如下(注:圖片來自網際網路):
Scrapy主要包括了以下元件:
- 引擎,用來處理整個系統的資料流處理,觸發事務。
- 排程器,用來接受引擎發過來的請求,壓入佇列中,並在引擎再次請求的時候返回。
- 下載器,用於下載網頁內容,並將網頁內容返回給蜘蛛。
- 蜘蛛,蜘蛛是主要幹活的,用它來制訂特定域名或網頁的解析規則。
- 專案管道,負責處理有蜘蛛從網頁中抽取的專案,他的主要任務是清晰、驗證和儲存資料。當頁面被蜘蛛解析後,將被髮送到專案管道,並經過幾個特定的次序處理資料。
- 下載器中介軟體,位於Scrapy引擎和下載器之間的鉤子框架,主要是處理Scrapy引擎與下載器之間的請求及響應。
- 蜘蛛中介軟體,介於Scrapy引擎和蜘蛛之間的鉤子框架,主要工作是處理蜘蛛的響應輸入和請求輸出。
- 排程中介軟體,介於Scrapy引擎和排程之間的中介軟體,從Scrapy引擎傳送到排程的請求和響應。
使用Scrapy可以很方便的完成網上資料的採集工作,它為我們完成了大量的工作,而不需要自己費大力氣去開發。
1. 安裝
安裝 python
Scrapy 目前最新版本為0.22.2,該版本需要 python 2.7,故需要先安裝 python 2.7。這裡我使用 centos 伺服器來做測試,因為系統自帶了 python ,需要先檢查 python 版本。
檢視python版本:
bash
$ python -V Python 2.6.6
升級版本到2.7:
bash
$ Python 2.7.6: $ wget http://python.org/ftp/python/2.7.6/Python-2.7.6.tar.xz $ tar xf Python-2.7.6.tar.xz $ cd Python-2.7.6 $ ./configure --prefix=/usr/local --enable-unicode=ucs4 --enable-shared LDFLAGS="-Wl,-rpath /usr/local/lib" $ make && make altinstall
建立軟連線,使系統預設的 python指向 python2.7
bash
$ mv /usr/bin/python /usr/bin/python2.6.6 $ ln -s /usr/local/bin/python2.7 /usr/bin/python
再次檢視python版本:
bash
$ python -V Python 2.7.6
安裝
這裡使用 wget 的方式來安裝 setuptools :
bash
$ wget https://bootstrap.pypa.io/ez_setup.py -O - | python
安裝 zope.interface
bash
$ easy_install zope.interface
安裝 twisted
Scrapy 使用了 Twisted 非同步網路庫來處理網路通訊,故需要安裝 twisted。
安裝 twisted 前,需要先安裝 gcc:
bash
$ yum install gcc -y
然後,再通過 easy_install 安裝 twisted:
bash
$ easy_install twisted
如果出現下面錯誤:
bash
$ easy_install twisted Searching for twisted Reading https://pypi.python.org/simple/twisted/ Best match: Twisted 14.0.0 Downloading https://pypi.python.org/packages/source/T/Twisted/Twisted-14.0.0.tar.bz2#md5=9625c094e0a18da77faa4627b98c9815 Processing Twisted-14.0.0.tar.bz2 Writing /tmp/easy_install-kYHKjn/Twisted-14.0.0/setup.cfg Running Twisted-14.0.0/setup.py -q bdist_egg --dist-dir /tmp/easy_install-kYHKjn/Twisted-14.0.0/egg-dist-tmp-vu1n6Y twisted/runner/portmap.c:10:20: error: Python.h: No such file or directory twisted/runner/portmap.c:14: error: expected ‘=’, ‘,’, ‘;’, ‘asm’ or ‘__attribute__’ before ‘*’ token twisted/runner/portmap.c:31: error: expected ‘=’, ‘,’, ‘;’, ‘asm’ or ‘__attribute__’ before ‘*’ token twisted/runner/portmap.c:45: error: expected ‘=’, ‘,’, ‘;’, ‘asm’ or ‘__attribute__’ before ‘PortmapMethods’ twisted/runner/portmap.c: In function ‘initportmap’: twisted/runner/portmap.c:55: warning: implicit declaration of function ‘Py_InitModule’ twisted/runner/portmap.c:55: error: ‘PortmapMethods’ undeclared (first use in this function) twisted/runner/portmap.c:55: error: (Each undeclared identifier is reported only once twisted/runner/portmap.c:55: error: for each function it appears in.)
請安裝 python-devel 然後再次執行:
bash
$ yum install python-devel -y $ easy_install twisted
如果出現下面異常:
error: Not a recognized archive type: /tmp/easy_install-tVwC5O/Twisted-14.0.0.tar.bz2
請手動下載然後安裝,下載地址在這裡
bash
$ wget https://pypi.python.org/packages/source/T/Twisted/Twisted-14.0.0.tar.bz2#md5=9625c094e0a18da77faa4627b98c9815 $ tar -vxjf Twisted-14.0.0.tar.bz2 $ cd Twisted-14.0.0 $ python setup.py install
安裝 pyOpenSSL
先安裝一些依賴:
bash
$ yum install libffi libffi-devel openssl-devel -y
然後,再通過 easy_install 安裝 pyOpenSSL:
bash
$ easy_install pyOpenSSL
安裝 Scrapy
先安裝一些依賴:
bash
$ yum install libxml2 libxslt libxslt-devel -y
最後再來安裝 Scrapy :
bash
$ easy_install scrapy
2. 使用 Scrapy
在安裝成功之後,你可以瞭解一些 Scrapy 的基本概念和使用方法,並學習 Scrapy 專案的例子 dirbot 。
Dirbot 專案位於 https://github.com/scrapy/dirbot,該專案包含一個 README 檔案,它詳細描述了專案的內容。如果你熟悉 git,你可以 checkout 它的原始碼。或者你可以通過點選 Downloads 下載 tarball 或 zip 格式的檔案。
下面以該例子來描述如何使用 Scrapy 建立一個爬蟲專案。
新建工程
在抓取之前,你需要新建一個 Scrapy 工程。進入一個你想用來儲存程式碼的目錄,然後執行:
bash
$ scrapy startproject tutorial
這個命令會在當前目錄下建立一個新目錄 tutorial,它的結構如下:
.
├── scrapy.cfg
└── tutorial
├── __init__.py
├── items.py
├── pipelines.py
├── settings.py
└── spiders
└── __init__.py
這些檔案主要是:
- scrapy.cfg: 專案配置檔案
- tutorial/: 專案python模組, 呆會程式碼將從這裡匯入
- tutorial/items.py: 專案items檔案
- tutorial/pipelines.py: 專案管道檔案
- tutorial/settings.py: 專案配置檔案
- tutorial/spiders: 放置spider的目錄
定義Item
Items是將要裝載抓取的資料的容器,它工作方式像 python 裡面的字典,但它提供更多的保護,比如對未定義的欄位填充以防止拼寫錯誤。
它通過建立一個 scrapy.item.Item
類來宣告,定義它的屬性為 scrpy.item.Field
物件,就像是一個物件關係對映(ORM).
我們通過將需要的item模型化,來控制從 dmoz.org 獲得的站點資料,比如我們要獲得站點的名字,url 和網站描述,我們定義這三種屬性的域。要做到這點,我們編輯在 tutorial 目錄下的 items.py 檔案,我們的 Item 類將會是這樣
python
from scrapy.item import Item, Field class DmozItem(Item): title = Field() link = Field() desc = Field()
剛開始看起來可能會有些困惑,但是定義這些 item 能讓你用其他 Scrapy 元件的時候知道你的 items 到底是什麼。
編寫爬蟲(Spider)
Spider 是使用者編寫的類,用於從一個域(或域組)中抓取資訊。們定義了用於下載的URL的初步列表,如何跟蹤連結,以及如何來解析這些網頁的內容用於提取items。
要建立一個 Spider,你可以為 scrapy.spider.BaseSpider
建立一個子類,並確定三個主要的、強制的屬性:
-
name
:爬蟲的識別名,它必須是唯一的,在不同的爬蟲中你必須定義不同的名字. -
start_urls
:爬蟲開始爬的一個 URL 列表。爬蟲從這裡開始抓取資料,所以,第一次下載的資料將會從這些 URLS 開始。其他子 URL 將會從這些起始 URL 中繼承性生成。 -
parse()
:爬蟲的方法,呼叫時候傳入從每一個 URL 傳回的 Response 物件作為引數,response 將會是 parse 方法的唯一的一個引數,
這個方法負責解析返回的資料、匹配抓取的資料(解析為 item )並跟蹤更多的 URL。
在 tutorial/spiders 目錄下建立 DmozSpider.py
python
from scrapy.spider import BaseSpider class DmozSpider(BaseSpider): name = "dmoz" allowed_domains = ["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)
執行專案
bash
$ scrapy crawl dmoz
該命令從 dmoz.org 域啟動爬蟲,第三個引數為 DmozSpider.py 中的 name 屬性值。
xpath選擇器
Scrapy 使用一種叫做 XPath selectors 的機制,它基於 XPath 表示式。如果你想了解更多selectors和其他機制你可以查閱資料。
這是一些XPath表示式的例子和他們的含義:
-
/html/head/title
: 選擇HTML文件<head>
元素下面的<title>
標籤。 -
/html/head/title/text()
: 選擇前面提到的<title>
元素下面的文字內容 -
//td
: 選擇所有<td>
元素 -
//div[@class="mine"]
: 選擇所有包含class="mine"
屬性的div 標籤元素
這只是幾個使用 XPath 的簡單例子,但是實際上 XPath 非常強大。如果你想了解更多 XPATH 的內容,我們向你推薦這個 XPath 教程
為了方便使用 XPaths,Scrapy 提供 Selector 類, 有三種方法
-
xpath()
:返回selectors列表, 每一個select表示一個xpath參數列達式選擇的節點. -
extract()
:返回一個unicode字串,該字串為XPath選擇器返回的資料 -
re()
: 返回unicode字串列表,字串作為引數由正規表示式提取出來 css()
提取資料
我們可以通過如下命令選擇每個在網站中的 <li>
元素:
python
sel.xpath('//ul/li')
然後是網站描述:
python
sel.xpath('//ul/li/text()').extract()
網站標題:
python
sel.xpath('//ul/li/a/text()').extract()
網站連結:
python
sel.xpath('//ul/li/a/@href').extract()
如前所述,每個 xpath()
呼叫返回一個 selectors 列表,所以我們可以結合 xpath()
去挖掘更深的節點。我們將會用到這些特性,所以:
python
sites = sel.xpath('//ul/li') for site in sites: title = site.xpath('a/text()').extract() link = site.xpath('a/@href').extract() desc = site.xpath('text()').extract() print title, link, desc
使用Item
scrapy.item.Item
的呼叫介面類似於 python 的 dict ,Item 包含多個 scrapy.item.Field
。這跟 django 的 Model 與
Item 通常是在 Spider 的 parse 方法裡使用,它用來儲存解析到的資料。
最後修改爬蟲類,使用 Item 來儲存資料,程式碼如下:
python
from scrapy.spider import Spider from scrapy.selector import Selector from dirbot.items import Website class DmozSpider(Spider): name = "dmoz" allowed_domains = ["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): """ The lines below is a spider contract. For more info see: http://doc.scrapy.org/en/latest/topics/contracts.html @url http://www.dmoz.org/Computers/Programming/Languages/Python/Resources/ @scrapes name """ sel = Selector(response) sites = sel.xpath('//ul[@class="directory-url"]/li') items = [] for site in sites: item = Website() item['name'] = site.xpath('a/text()').extract() item['url'] = site.xpath('a/@href').extract() item['description'] = site.xpath('text()').re('-\s([^\n]*?)\\n') items.append(item) return items
現在,可以再次執行該專案檢視執行結果:
bash
$ scrapy crawl dmoz
使用Item Pipeline
在 settings.py 中設定 ITEM_PIPELINES
,其預設為[]
,與 django 的 MIDDLEWARE_CLASSES
等相似。
從 Spider 的 parse 返回的 Item 資料將依次被 ITEM_PIPELINES
列表中的 Pipeline 類處理。
一個 Item Pipeline 類必須實現以下方法:
-
process_item(item, spider)
為每個 item pipeline 元件呼叫,並且需要返回一個scrapy.item.Item
例項物件或者丟擲一個scrapy.exceptions.DropItem
異常。當丟擲異常後該 item 將不會被之後的 pipeline 處理。引數:-
item (Item object)
– 由 parse 方法返回的 Item 物件 -
spider (BaseSpider object)
– 抓取到這個 Item 物件對應的爬蟲物件
-
也可額外的實現以下兩個方法:
-
open_spider(spider)
當爬蟲開啟之後被呼叫。引數:spider (BaseSpider object)
– 已經執行的爬蟲 -
close_spider(spider)
當爬蟲關閉之後被呼叫。引數:spider (BaseSpider object)
– 已經關閉的爬蟲
儲存抓取的資料
儲存資訊的最簡單的方法是通過 Feed exports,命令如下:
bash
$ scrapy crawl dmoz -o items.json -t json
除了 json 格式之外,還支援 JSON lines、CSV、XML格式,你也可以通過介面擴充套件一些格式。
對於小專案用這種方法也足夠了。如果是比較複雜的資料的話可能就需要編寫一個 Item Pipeline 進行處理了。
所有抓取的 items 將以 JSON 格式被儲存在新生成的 items.json 檔案中
總結
上面描述瞭如何建立一個爬蟲專案的過程,你可以參照上面過程聯絡一遍。作為學習的例子,你還可以參考這篇文章:scrapy 中文教程(爬cnbeta例項) 。
這篇文章中的爬蟲類程式碼如下:
python
from scrapy.contrib.spiders import CrawlSpider, Rule from scrapy.contrib.linkextractors.sgml import SgmlLinkExtractor from scrapy.selector import Selector from cnbeta.items import CnbetaItem class CBSpider(CrawlSpider): name = 'cnbeta' allowed_domains = ['cnbeta.com'] start_urls = ['http://www.cnbeta.com'] rules = ( Rule(SgmlLinkExtractor(allow=('/articles/.*\.htm', )), callback='parse_page', follow=True), ) def parse_page(self, response): item = CnbetaItem() sel = Selector(response) item['title'] = sel.xpath('//title/text()').extract() item['url'] = response.url return item
需要說明的是:
- 該爬蟲類繼承的是
CrawlSpider
類,並且定義規則,rules指定了含有/articles/.*\.htm
的連結都會被匹配。 - 該類並沒有實現parse方法,並且規則中定義了回撥函式
parse_page
,你可以參考更多資料瞭解 CrawlSpider 的用法
3. 學習資料
接觸 Scrapy,是因為想爬取一些知乎的資料,最開始的時候搜尋了一些相關的資料和別人的實現方式。
Github 上已經有人或多或少的實現了對知乎資料的爬取,我搜尋到的有以下幾個倉庫:
- https://github.com/KeithYue/Zhihu_Spider 實現先通過使用者名稱和密碼登陸再爬取資料,程式碼見 zhihu_spider.py。
- https://github.com/immzz/zhihu-scrapy 使用 selenium 下載和執行 javascript 程式碼。
- https://github.com/tangerinewhite32/zhihu-stat-py
- https://github.com/Zcc/zhihu 主要是爬指定話題的topanswers,還有使用者個人資料,新增了登入程式碼。
- https://github.com/pelick/VerticleSearchEngine 基於爬取的學術資源,提供搜尋、推薦、視覺化、分享四塊。使用了 Scrapy、MongoDB、Apache Lucene/Solr、Apache Tika等技術。
- https://github.com/geekan/scrapy-examples scrapy的一些例子,包括獲取豆瓣資料、linkedin、騰訊招聘資料等例子。
- https://github.com/owengbs/deeplearning 實現分頁獲取話題。
- https://github.com/gnemoug/distribute_crawler 使用scrapy、redis、mongodb、graphite實現的一個分散式網路爬蟲,底層儲存mongodb叢集,分散式使用redis實現,爬蟲狀態顯示使用graphite實現
- https://github.com/weizetao/spider-roach 一個分散式定向抓取叢集的簡單實現。
其他資料:
- http://www.52ml.net/tags/Scrapy 收集了很多關於 Scrapy 的文章,推薦閱讀
- 用Python Requests抓取知乎使用者資訊
- 使用scrapy框架爬取自己的博文
- Scrapy 深入一點點
- 使用python,scrapy寫(定製)爬蟲的經驗,資料,雜。
- Scrapy 輕鬆定製網路爬蟲
- 在scrapy中怎麼讓Spider自動去抓取豆瓣小組頁面
scrapy 和 javascript 互動例子:
還有一些待整理的知識點:
- 如何先登陸再爬資料
- 如何使用規則做過濾
- 如何遞迴爬取資料
- scrapy的引數設定和優化
- 如何實現分散式爬取
4. 總結
以上就是最近幾天學習 Scrapy 的一個筆記和知識整理,參考了一些網上的文章才寫成此文,對此表示感謝,也希望這篇文章能夠對你有所幫助。如果你有什麼想法,歡迎留言;如果喜歡此文,請幫忙分享,謝謝!