序言
第1章 Scrapy介紹
第2章 理解HTML和XPath
第3章 爬蟲基礎
第4章 從Scrapy到移動應用
第5章 快速構建爬蟲
第6章 Scrapinghub部署
第7章 配置和管理
第8章 Scrapy編程
第9章 使用Pipeline
第10章 理解Scrapy的性能
第11章(完) Scrapyd分布式抓取和實時分析
我們已經學過了用Scrapy寫一個抓取網絡信息的簡單爬蟲是多么容易。通過進行設置,Scrapy還有許多用途和功能。對于許多軟件框架,用設置調節系統的運行,很讓人頭痛。對于Scrapy,設置是最基礎的知識,除了調節和配置,它還可以擴展框架的功能。這里只是補充官方Scrapy文檔,讓你可以盡快對設置有所了解,并找到能對你有用的東西。在做出修改時,還請查閱文檔。
使用Scrapy設置
在Scrapy的設置中,你可以按照五個等級進行設置。第一級是默認設置,你不必進行修改,但是scrapy/settings/default_settings.py文件還是值得一讀的。默認設置可以在命令級進行優化。一般來講,除非你要插入自定義命令,否則不必修改。更經常的,我們只是修改自己項目的settings.py文件。這些設置只對當前項目管用。這么做很方便,因為當我們把項目部署到云主機時,可以連帶設置文件一起打包,并且因為它是文件,可以用文字編輯器進行編輯。下一級是每個爬蟲的設置。通過在爬蟲中使用custom_settings屬性,我們可以自定義每個爬蟲的設置。例如,這可以讓我們打開或關閉某個特定蜘蛛的Pipelines。最后,要做最后的修改時,我們可以在命令行中使用-s參數。我們做過這樣的設置,例如-s CLOSESPIDER_PAGECOUNT=3,這可以限制爬蟲的抓取范圍。在這一級,我們可以設置API、密碼等等。不要在settings.py文件中保存這些設置,因為不想讓它們在公共倉庫中失效。
這一章,我們會學習一些非常重要且常用的設置。在任意項目中輸入以下命令,可以了解設置都有多少類型:
$ scrapy settings --get CONCURRENT_REQUESTS
16
你得到的是默認值。修改這個項目的settings.py文件的CONCURRENT_REQUESTS的值,比如,14。上面命令行的結果也會變為14,別忘了將設置改回去。在命令行中設置參數的話:
$ scrapy settings --get CONCURRENT_REQUESTS -s CONCURRENT_REQUESTS=19
19
這個結果暗示scrapy crawl和scrapy settings都是命令。每個命令都使用這樣的方法加載設置。再舉一個例子:
$ scrapy shell -s CONCURRENT_REQUESTS=19
>>> settings.getint('CONCURRENT_REQUESTS')
19
當你想確認設置文件中的值時,你就可以才用以上幾種方法。下面詳細學習Scrapy的設置。
基本設置
Scrapy的設置太多,將其分類很有必要。我們從下圖的基本設置開始,它可以讓你明白重要的系統特性,你可能會頻繁使用。
分析
通過這些設置,可以調節Scrapy的性能、調試信息的日志、統計、遠程登錄設備。
日志
Scrapy有不同的日志等級:DEBUG(最低),INFO,WARNING,ERROR,和CRITICAL(最高)。除此之外,還有一個SILENT級,沒有日志輸出。Scrapy的有用擴展之一是Log Stats,它可以打印出每分鐘抓取的文件數和頁數。LOGSTATS_INTERVAL設置日志頻率,默認值是60秒。這個間隔偏長。我習慣于將其設置為5秒,因為許多運行都很短。LOG_FILE設置將日志寫入文件。除非進行設定,輸出會一直持續到發生標準錯誤,將LOG_ENABLED設定為False,就不會這樣了。最后,通過設定LOG_STDOUT為True,你可以讓Scrapy在日志中記錄所有的輸出(比如print)。
統計
STATS_DUMP是默認開啟的,當爬蟲運行完畢時,它把統計收集器(Stats Collector)中的值轉移到日志。設定DOWNLOADER_STATS,可以決定是否記錄統計信息。通過DEPTH_STATS,可以設定是否記錄網站抓取深度的信息。若要記錄更詳細的深度信息,將DEPTH_STATS_VERBOSE設定為True。STATSMAILER_RCPTS是一個當爬蟲結束時,發送email的列表。你不用經常設置它,但有時調試時會用到它。
遠程登錄
Scrapy包括一個內建的遠程登錄控制臺,你可以在上面用Python控制Scrapy。TELNETCONSOLE_ENABLED是默認開啟的,TELNETCONSOLE_PORT決定連接端口。在發生沖突時,可以對其修改。
案例1——使用遠程登錄
有時,你想查看Scrapy運行時的內部狀態。讓我們來看看如何用遠程登錄來做:
筆記:本章代碼位于ch07。這個例子位于ch07/properties文件夾中。
$ pwd
/root/book/ch07/properties
$ ls
properties scrapy.cfg
Start a crawl as follows:
$ scrapy crawl fast
...
[scrapy] DEBUG: Telnet console listening on 127.0.0.1:6023:6023
這段信息是說遠程登錄被激活,監聽端口是6023。然后在另一臺電腦,使用遠程登錄的命令連接:
$ telnet localhost 6023
>>>
現在,這臺終端會給你一個在Scrapy中的Python控制臺。你可以查看某些組件,例如用engine變量查看引擎,可以用est()進行快速查看:
>>> est()
Execution engine status
time()-engine.start_time : 5.73892092705
engine.has_capacity() : False
len(engine.downloader.active) : 8
...
len(engine.slot.inprogress) : 10
...
len(engine.scraper.slot.active) : 2
我們在第10章中會繼續學習里面的參數。接著輸入以下命令:
>>> import time
>>> time.sleep(1) # Don't do this!
你會注意到,另一臺電腦有一個短暫停。你還可以進行暫停、繼續、停止爬蟲。使用遠程機器時,使用遠程登錄的功能非常有用:
>>> engine.pause()
>>> engine.unpause()
>>> engine.stop()
Connection closed by foreign host.
性能
第10章會詳細介紹這些設置,這里只是一個概括。性能設定可以讓你根據具體的工作調節爬蟲的性能。CONCURRENT_REQUESTS設置了并發請求的最大數。這是為了當你抓取很多不同的網站(域名/IPs)時,保護你的服務器性能。不是這樣的話,你會發現CONCURRENT_REQUESTS_PER_DOMAIN和CONCURRENT_REQUESTS_PER_IP更多是限制性的。這兩項分別通過限制每一個域名或IP地址的并發請求數,保護遠程服務器。如果CONCURRENT_REQUESTS_PER_IP是非零的,CONCURRENT_REQUESTS_PER_DOMAIN則被忽略。這些設置不是按照每秒。如果CONCURRENT_REQUESTS = 16,請求平均消耗四分之一秒,最大極限則為每秒16/0.25 = 64次請求。CONCURRENT_ITEMS設定每次請求并發處理的最大文件數。你可能會覺得這個設置沒什么用,因為每個頁面通常只有一個抓取項。它的默認值是100。如果降低到,例如10或1,你可能會覺得性能提升了,取決于每次請求抓取多少項和pipelines的復雜度。你還會注意到,當這個值是關于每次請求的,如果CONCURRENT_REQUESTS = 16,CONCURRENT_ITEMS = 100意味每秒有1600個文件同時要寫入數據庫。我一般把這個值設的比較小。
對于下載,DOWNLOADS_TIMEOUT決定了取消請求前,下載器的等待時間。默認是180秒,這個時間太長,并發請求是16時,每秒的下載數是5頁。我建議設為10秒。默認情況下,各個下載間的間隔是0,以提高抓取速度。你可以設置DOWNLOADS_DELAY改變下載速度。有的網站會測量請求頻率以判定是否是機器人行為。設定DOWNLOADS_DELAY的同時,還會有±50%的隨機延遲。你可以設定RANDOMIZE_DOWNLOAD_DELAY為False。
最后,若要使用更快的DNS查找,可以設定DNSCACHE_ENABLED打開內存DNS緩存。
提早結束抓取
Scrapy的CloseSpider擴展可以在條件達成時,自動結束抓取。你可以用CLOSESPIDER_TIMEOUT(in seconds), CLOSESPIDER_ITEMCOUNT, CLOSESPIDER_PAGECOUNT,和CLOSESPIDER_ERRORCOUNT分別設置在一段時間、抓取一定數量的文件、發出一定數量請求、發生一定數量錯誤時,提前關閉爬蟲。你會在運行爬蟲時頻繁地做出這類設置:
$ scrapy crawl fast -s CLOSESPIDER_ITEMCOUNT=10
$ scrapy crawl fast -s CLOSESPIDER_PAGECOUNT=10
$ scrapy crawl fast -s CLOSESPIDER_TIMEOUT=10
HTTP緩存和脫機工作
Scrapy的HttpCacheMiddleware中間件(默認關閉)提供了一個低級的HTTP請求響應緩存。如果打開的話,緩存會存儲每次請求和對應的響應。通過設定HTTPCACHE_POLICY為scrapy.contrib.httpcache.RFC2616Policy,我們可以使用一個更為復雜的、按照RFC2616遵循網站提示的緩存策略。打開這項功能,設定HTTPCACHE_ENABLED為True,HTTPCACHE_DIR指向一個磁盤路徑(使用相對路徑的話,會存在當前文件夾內)。
你可以為緩存文件指定數據庫后端,通過設定HTTPCACHE_STORAGE為scrapy.contrib.httpcache.DbmCacheStorage,還可以選擇調整HTTPCACHE_DBM_MODULE。(默認為anydbm)還有其它微調緩存的設置,但按照默認設置就可以了。
案例2——用緩存離線工作
運行以下代碼:
$ scrapy crawl fast -s LOG_LEVEL=INFO -s CLOSESPIDER_ITEMCOUNT=5000
一分鐘之后才結束。如果你無法聯網,就無法進行任何抓取。用下面的代碼再次進行抓取:
$ scrapy crawl fast -s LOG_LEVEL=INFO -s CLOSESPIDER_ITEMCOUNT=5000 -s HTTPCACHE_ENABLED=1
...
INFO: Enabled downloader middlewares:...*HttpCacheMiddleware*
你會看到啟用了HttpCacheMiddleware,如果你查看當前目錄,會發現一個隱藏文件夾,如下所示:
$ tree .scrapy | head
.scrapy
└── httpcache
└── easy
├── 00
│ ├── 002054968919f13763a7292c1907caf06d5a4810
│ │ ├── meta
│ │ ├── pickled_meta
│ │ ├── request_body
│ │ ├── request_headers
│ │ ├── response_body
...
當你再次運行不能聯網的爬蟲時,抓取稍少的文件,你會發現運行變快了:
$ scrapy crawl fast -s LOG_LEVEL=INFO -s CLOSESPIDER_ITEMCOUNT=4500 -s
HTTPCACHE_ENABLED=1
抓取稍少的文件,是因為使用CLOSESPIDER_ITEMCOUNT結束爬蟲時,爬蟲實際上會多抓取幾頁,我們不想抓取不在緩存中的內容。清理緩存的話,只需刪除緩存目錄:
$ rm -rf .scrapy
抓取方式
Scrapy允許你設置從哪一頁開始爬。設置DEPTH_LIMIT,可以設置最大深度,0代表沒有限制。根據深度,通過DEPTH_PRIORITY,可以給請求設置優先級。將其設為正值,可以讓你實現廣度優先抓取,并在LIFO和FIFO間切換:
DEPTH_PRIORITY = 1
SCHEDULER_DISK_QUEUE = 'scrapy.squeue.PickleFifoDiskQueue'
SCHEDULER_MEMORY_QUEUE = 'scrapy.squeue.FifoMemoryQueue'
這個功能十分有用,例如,當你抓取一個新聞網站,先抓取離首頁近的最近的新聞,然后再是其它頁面。默認的Scrapy方式是順著第一條新聞抓取到最深,然后再進行下一條。廣度優先可以先抓取層級最高的新聞,再往深抓取,當設定DEPTH_LIMIT為3時,就可以讓你快速查看最近的新聞。
有的網站在根目錄中用一個網絡標準文件robots.txt規定了爬蟲的規則。當設定ROBOTSTXT_OBEY為True時,Scrapy會參考這個文件。設定為True之后,記得調試的時候碰到意外的錯誤時,可能是這個原因。
CookiesMiddleware負責所有cookie相關的操作,開啟session跟蹤的話,可以實現登錄。如果你想進行秘密抓取,可以設置COOKIES_ENABLED為False。使cookies無效減少了帶寬,一定程度上可以加快抓取。相似的,REFERER_ENABLED默認是True,可使RefererMiddleware生效,用它填充Referer headers。你可以用DEFAULT_REQUEST_HEADERS自定義headers。你會發現當有些奇怪的網站要求特定的請求頭時,這個特別有用。最后,自動生成的settings.py文件建議我們設定USER_AGENT。默認也可以,但我們應該修改它,以便網站所有者可以聯系我們。
Feeds
Feeds可以讓你導出用Scrapy抓取的數據到本地或到服務器。存儲路徑取決于FEED_URI.FEED_URI,其中可能包括參數。例如scrapy crawl fast -o "%(name)s_%(time)s.jl,可以自動將時間和名字填入到輸出文件。如果你需要你個自定義參數,例如%(foo)s, feed輸出器希望在爬蟲中提供一個叫做foo的屬性。數據的存儲,例如S3、FTP或本地,也是在URI中定義。例如,FEED_URI='s3://mybucket/file.json'可以使用你的Amazon證書(AWS_ACCESS_KEY_ID和AWS_SECRET_ACCESS_KEY),將你的文件存儲到Amazon S3。存儲的格式,JSON、JSON Lines、CSV和XML,取決于FEED_FORMAT。如果沒有指定的話,Scrapy會根據FEED_URI的后綴猜測。你可以選擇輸出為空,通過設定FEED_STORE_EMPTY為True。你還可以選擇輸出指定字段,通過設定FEED_EXPORT_FIELDS。這對.csv文件特別有用,可以固定header的列數。最后FEED_URI_PARAMS用于定義一個函數,對傳遞給FEED_URI的參數進行后處理。
下載媒體文件
Scrapy可以用Image Pipeline下載媒體文件,它還可以將圖片轉換成不同的格式、生成面包屑路徑、或根據圖片大小進行過濾。
IMAGES_STORE設置了圖片存儲的路徑(選用相對路徑的話,會存儲在項目的根目錄)。每個圖片的URL存在各自的image_URL字段(它可以被IMAGES_URL_FIELD設置覆蓋),下載下來的圖片的文件名會存在一個新的image字段(它可以被IMAGES_RESULT_FIELD設置覆蓋)。你可以通過IMAGES_MIN_WIDTH和IMAGES_MIN_HEIGHT篩選出小圖片。IMAGES_EXPIRES可以決定圖片在緩存中存儲的天數。IMAGES_THUMBS可以設置一個或多個縮略圖,還可以設置縮略圖的大小。例如,你可以讓Scrapy生成一個圖標大小的縮略圖或為每個圖片生成一個中等的縮略圖。
其它媒體文件
你可以使用Files Pipelines下載其它媒體文件。與圖片相同FILES_STORE決定了存儲地址,FILES_EXPIRES決定存儲時間。FILES_URL_FIELD和FILES_
RESULT_FIELD的作用與之前圖片的相似。文件和圖片的pipelines可以同時工作。
案例3——下載圖片
為了使用圖片功能,我們必須安裝圖片包,命令是pip install image。我們的開發機已經安裝好了。要啟動Image Pipeline,你需要編輯settings.py加入一些設置。首先在ITEM_PIPELINES添加scrapy.pipelines.images.ImagesPipeline。然后,將IMAGES_STORE設為相對路徑"images",通過設置IMAGES_THUMBS,添加縮略圖的描述,如下所示:
ITEM_PIPELINES = {
...
'scrapy.pipelines.images.ImagesPipeline': 1,
}
IMAGES_STORE = 'images'
IMAGES_THUMBS = { 'small': (30, 30) }
我們已經為Item安排了image_URL字段,然后如下運行:
$ scrapy crawl fast -s CLOSESPIDER_ITEMCOUNT=90
...
DEBUG: Scraped from <200 http://http://web:9312/.../index_00003.html/
property_000001.html>{
'image_URL': [u'http://web:9312/images/i02.jpg'],
'images': [{'checksum': 'c5b29f4b223218e5b5beece79fe31510',
'path': 'full/705a3112e67...a1f.jpg',
'url': 'http://web:9312/images/i02.jpg'}],
...
$ tree images
images
├── full
│ ├── 0abf072604df23b3be3ac51c9509999fa92ea311.jpg
│ ├── 1520131b5cc5f656bc683ddf5eab9b63e12c45b2.jpg
...
└── thumbs
└── small
├── 0abf072604df23b3be3ac51c9509999fa92ea311.jpg
├── 1520131b5cc5f656bc683ddf5eab9b63e12c45b2.jpg
...
我們看到圖片成功下載下來,病生成了縮略圖。Images文件夾中存儲了jpg文件。縮略圖的路徑可以很容易推測出來。刪掉圖片,可以使用命令rm -rf images。
亞馬遜網絡服務
Scrapy內建支持亞馬遜服務。你可以將AWS的access key存儲到AWS_ACCESS_KEY_ID,將secret key存到AWS_SECRET_ACCESS_KEY。這兩個設置默認都是空的。使用方法如下:
- 當你用開頭是s3://(注意不是http://)下載URL時
- 當你用media pipelines在s3://路徑存儲文件或縮略圖時
- 當你在s3://目錄存儲輸出文件時,不要在settings.py中存儲這些設置,以免有一天這個文件要公開。
使用代理和爬蟲
Scrapy的HttpProxyMiddleware組件可以讓你使用代理,它包括http_proxy、https_proxy和no_proxy環境變量。代理功能默認是開啟的。
案例4——使用代理和Crawlera的智慧代理
DynDNS提供了一個免費檢查你的IP地址的服務。使用Scrapy shell,我們向checkip.dyndns.org發送一個請求,檢查響應確定當前的IP 地址:
$ scrapy shell http://checkip.dyndns.org
>>> response.body
'<html><head><title>Current IP Check</title></head><body>Current IP
Address: xxx.xxx.xxx.xxx</body></html>\r\n'
>>> exit()
要使用代理請求,退出shell,然后使用export命令設置一個新代理。你可以通過搜索HMA的公共代理列表(http://proxylist.hidemyass.com/)測試一個免費代理。例如,假設我們選擇一個代理IP是10.10.1.1,端口是80(替換成你的),如下運行:
$ # First check if you already use a proxy
$ env | grep http_proxy
$ # We should have nothing. Now let's set a proxy
$ export http_proxy=http://10.10.1.1:80
再次運行Scrapy shell,你可以看到這次請求使用了不同的IP。代理很慢,有時還會失敗,這時可以選擇另一個IP。要關閉代理,可以退出Scrapy shell,并使用unset http_proxy。
Crawlera是Scrapinghub的一個服務。除了使用一個大的IP池,它還能調整延遲并退出壞的請求,讓連接變得快速穩定。這是爬蟲工程師夢寐以求的產品。使用它,只需設置http_proxy的環境變量為:
$ export http_proxy=myusername:mypassword@proxy.crawlera.com:8010
除了HTTP代理,還可以通過它給Scrapy設計的中間件使用Crawlera。
更多的設置
接下來看一些Scrapy不常用的設置和Scrapy的擴展設置,后者在后面的章節會詳細介紹。
和項目相關的設定
這個小標題下,介紹和具體項目相關的設置,例如BOT_NAME、SPIDER_MODULES等等。最好在文檔中查看一下,因為它們在某些具體情況下可以提高效率。但是通常來講,Scrapy的startproject和genspider命令的默認設置已經是合理的了,所以就不必另行設置了。和郵件相關的設置,例如MAIL_FROM,可以讓你配置MailSender類,它被用來發送統計數據(還可以查看STATSMAILER_RCPTS)和內存使用(還可以查看MEMUSAGE_NOTIFY_MAIL)。還有兩個環境變量SCRAPY_SETTINGS_MODULE和SCRAPY_PROJECT,它們可以讓你微調Scrapy項目的整合,例如,整合一個Django項目。scrapy.cfg還可以讓你修改設置模塊的名字。
擴展Scrapy設置
這些設定允許你擴展和修改Scrapy的幾乎每個方面。最重要的就是ITEM_PIPELINES。它允許你在項目中使用Item Processing Pipelines。我們會在第9章中看到更多的例子。除了pipelines,還可以用多種方式擴展Scrapy,第8章總結了一些方式。COMMANDS_MODULE允許我們設置自定義命令。例如,假設我們添加了一個properties/hi.py文件:
from scrapy.commands import ScrapyCommand
class Command(ScrapyCommand):
default_settings = {'LOG_ENABLED': False}
def run(self, args, opts):
print("hello")
一旦我們在settings.py加入了COMMANDS_MODULE='properties.hi',就可以在Scrapy的help中運行hi查看。在命令行的default_settings中定義的設置會與項目的設置合并,但是與settings.py文件的優先級比起來,它的優先級偏低。
Scrapy使用-_BASE字典(例如,FEED_EXPORTERS_BASE)來存儲不同擴展框架的默認值,然后我們可以在settings.py文件和命令行中設置non-_BASE版本進行切換(例如,FEED_EXPORTERS)。
最后,Scrapy使用設置,例如DOWNLOADER或SCHEDULER,保管系統基本組件的包和類的名。我們可以繼承默認的下載器(scrapy.core.downloader.Downloader),加載一些方法,在DOWNLOADER設置中自定義我們的類。這可以讓開發者試驗新特性、簡化自動檢測,但是只推薦專業人士這么做。
微調下載
RETRY_, REDIRECT_和METAREFRESH_*設置分別配置了Retry、Redirect、Meta-Refresh中間件。例如,REDIRECT_PRIORITY_設為2,意味著每次有重定向時,都會在沒有重定向請求之后,預約一個新的請求。REDIRECT_MAX_TIMES設為20意味著,在20次重定向之后,下載器不會再進行重定向,并返回現有值。當你抓取一些有問題的網站時,知道這些設置是很有用的,但是默認設置在大多數情況下就能應付了。HTTPERROR_ALLOWED_CODES和URLLENGTH_LIMIT也類似。
自動限定擴展設置
AUTOTHROTTLE_*設置可以自動限定擴展。看起來有用,但在實際中,我發現很難用它進行調節。它使用下載延遲,并根據加載和指向服務器,調節下載器的延遲。如果你不能確定DOWNLOAD_DELAY(默認是0)的值,這個模塊會派上用場。
內存使用擴展設置
MEMUSAGE_*設置可以配置內存使用擴展。當超出內存上限時,它會關閉爬蟲。在共享環境中這會很有用,因為抓取過程要盡量小心。更多時候,你會將MEMUSAGE_LIMIT_MB設為0,將自動關閉爬蟲的功能取消,只接收警告email。這個擴展只在類Unix平臺有。
MEMDEBUG_ENABLED和MEMDEBUG_NOTIFY可以配置內存調試擴展,可以在爬蟲關閉時實時打印出參考的個數。閱讀用trackref調試內存泄漏的文檔,更重要的,我建議抓取過程最好簡短、分批次,并匹配服務器的能力。我認為,每批次最好一千個網頁、不超過幾分鐘。
登錄和調試
最后,還有一些登錄和調試的設置。LOG_ENCODING,LOG_DATEFORMAT和LOG_FORMAT可以讓你微調登錄的方式,當你使用登錄管理,比如Splunk、Logstash和Kibana時,你會覺得它很好用。DUPEFILTER_DEBUG和COOKIES_DEBUG可以幫助你調試相對復雜的狀況,比如,當你的請求數比預期少,或丟失session時。
總結
通過閱讀本章,你一定會贊嘆比起以前手寫的爬蟲,Scrapy的功能更具深度和廣度。如果你想微調或擴展Scrapy的功能,可以有大量的方法,見下面幾章。
序言
第1章 Scrapy介紹
第2章 理解HTML和XPath
第3章 爬蟲基礎
第4章 從Scrapy到移動應用
第5章 快速構建爬蟲
第6章 Scrapinghub部署
第7章 配置和管理
第8章 Scrapy編程
第9章 使用Pipeline
第10章 理解Scrapy的性能
第11章(完) Scrapyd分布式抓取和實時分析