原文發表於:http://blog.javachen.com/2014/05/24/using-scrapy-to-cralw-data.htmljavascript
Scrapy是Python開發的一個快速,高層次的屏幕抓取和web抓取框架,用於抓取web站點並從頁面中提取結構化的數據。Scrapy用途普遍,能夠用於數據挖掘、監測和自動化測試。css
Scrapy 使用了 Twisted 異步網絡庫來處理網絡通信。總體架構大體以下(注:圖片來自互聯網):html
Scrapy主要包括瞭如下組件:java
使用Scrapy能夠很方便的完成網上數據的採集工做,它爲咱們完成了大量的工做,而不須要本身費大力氣去開發。python
Scrapy 目前最新版本爲0.22.2,該版本須要 python 2.7,故須要先安裝 python 2.7。這裏我使用 centos 服務器來作測試,由於系統自帶了 python ,須要先檢查 python 版本。git
查看python版本:github
bash$ python -V Python 2.6.6
升級版本到2.7:web
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版本:redis
bash$ python -V Python 2.7.6
這裏使用 wget 的方式來安裝 setuptools :
bash$ wget https://bootstrap.pypa.io/ez_setup.py -O - | python
bash$ easy_install zope.interface
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
先安裝一些依賴:
bash$ yum install libffi libffi-devel openssl-devel -y
而後,再經過 easy_install 安裝 pyOpenSSL:
bash$ easy_install pyOpenSSL
先安裝一些依賴:
bash$ yum install libxml2 libxslt libxslt-devel -y
最後再來安裝 Scrapy :
bash$ easy_install 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
這些文件主要是:
Items是將要裝載抓取的數據的容器,它工做方式像 python 裏面的字典,但它提供更多的保護,好比對未定義的字段填充以防止拼寫錯誤。
它經過建立一個 scrapy.item.Item
類來聲明,定義它的屬性爲 scrpy.item.Field
對象,就像是一個對象關係映射(ORM).
咱們經過將須要的item模型化,來控制從 dmoz.org 得到的站點數據,好比咱們要得到站點的名字,url 和網站描述,咱們定義這三種屬性的域。要作到這點,咱們編輯在 tutorial 目錄下的 items.py 文件,咱們的 Item 類將會是這樣
pythonfrom scrapy.item import Item, Field class DmozItem(Item): title = Field() link = Field() desc = Field()
剛開始看起來可能會有些困惑,可是定義這些 item 能讓你用其餘 Scrapy 組件的時候知道你的 items 究竟是什麼。
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
pythonfrom 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 屬性值。
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>
元素:
pythonsel.xpath('//ul/li')
而後是網站描述:
pythonsel.xpath('//ul/li/text()').extract()
網站標題:
pythonsel.xpath('//ul/li/a/text()').extract()
網站連接:
pythonsel.xpath('//ul/li/a/@href').extract()
如前所述,每一個 xpath()
調用返回一個 selectors 列表,因此咱們能夠結合 xpath()
去挖掘更深的節點。咱們將會用到這些特性,因此:
pythonsites = 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
scrapy.item.Item
的調用接口相似於 python 的 dict ,Item 包含多個 scrapy.item.Field
。這跟 django 的 Model 與
Item 一般是在 Spider 的 parse 方法裏使用,它用來保存解析到的數據。
最後修改爬蟲類,使用 Item 來保存數據,代碼以下:
pythonfrom 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
在 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實例) 。
這篇文章中的爬蟲類代碼以下:
pythonfrom 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_page
,你能夠參考更多資料瞭解 CrawlSpider 的用法接觸 Scrapy,是由於想爬取一些知乎的數據,最開始的時候搜索了一些相關的資料和別人的實現方式。
Github 上已經有人或多或少的實現了對知乎數據的爬取,我搜索到的有如下幾個倉庫:
其餘資料:
scrapy 和 javascript 交互例子:
還有一些待整理的知識點:
以上就是最近幾天學習 Scrapy 的一個筆記和知識整理,參考了一些網上的文章才寫成此文,對此表示感謝,也但願這篇文章可以對你有所幫助。若是你有什麼想法,歡迎留言;若是喜歡此文,請幫忙分享,謝謝!