scrapy学习笔记(二)进阶使用
?
From:https://www.imooc.com/article/21839
scrapy提升篇之配置:https://segmentfault.com/a/1190000009321902
?
?
前面我寫了一篇《scrapy快速入門》,簡單介紹了一點scrapy的知識。這次就來介紹一些scrapy的進階知識,做一些真正有用的爬蟲來。
scrapy組件介紹
首先先來介紹一下scrapy的體系結構和組件。
- scrapy引擎。顧名思義,它負責調用爬蟲爬取頁面并將數據交給管道來處理。
- 調度程序。調度程序從引擎獲取請求,然后將這些請求交給相應的處理者來處理。
- 下載器。下載器負責獲取web頁面,然后將它們交給引擎來處理。
- 爬蟲。這是我們要編寫的部分,爬蟲負責從頁面抽取要處理的數據,然后交由管道來處理。
- 項目管道。爬蟲抽取出來的數據,就要交給管道來處理,我們可以編寫自己的管道來清洗、驗證和持久化數據。
- 下載器中間件。下載器中間件在下載器和scrapy引擎之間工作,我們可以利用下載器中間件在將頁面交給爬蟲之前做一些事情。scrapy內置了一些下載器中間件,這些中間件將在后面介紹。
- 爬蟲中間件。爬蟲中間件可以讓我們控制爬蟲的回調等等。在后面會介紹一些內置的爬蟲中間件。
下面是scrapy官網的結構圖,可以幫助我們理解scrapy的體系。
?
項目(Item)
在以前的爬蟲中我們都是直接返回一個字典,其實這并不是最佳實踐。scrapy提供了一個Item基類,我們可以通過繼承這個類定義自己的結構化數據,比到處傳遞字典更好。下面是官方文檔的例子。
import scrapyclass Product(scrapy.Item):name = scrapy.Field()price = scrapy.Field()stock = scrapy.Field()last_updated = scrapy.Field(serializer=str)這些項目類一般都定義在scrapy項目的items.py文件中。定義好之后,在爬蟲中我們就不應該在反掌字典了,而是初始化并返回我們自定義的Item對象。
請求和響應對象
scrapy通過請求和響應對象來處理網頁請求,這部分的文檔可以參考https://doc.scrapy.org/en/latest/topics/request-response.html。請求和響應還有一些子類,可以幫助我們完成更具體的工作。例如Request的子類FormRequest就可以幫助我們模擬用戶登錄。
有時候需要模擬用戶登錄,這時候可以使用FormRequest.from_response方法。這時候爬蟲功能稍有變化,parse函數用來發送用戶名和密碼,抽取數據的操作放在回調函數中進行。
import scrapyclass LoginSpider(scrapy.Spider):name = 'example.com'start_urls = ['http://www.example.com/users/login.php']def parse(self, response):return scrapy.FormRequest.from_response(response,formdata={'username': 'john', 'password': 'secret'},callback=self.after_login)def after_login(self, response):# 檢查是否登錄成功if "authentication failed" in response.body:self.logger.error("Login failed")return# 在這里繼續爬取數據管道(pipeline)
管道用來處理爬蟲抽取到的數據,我們可以通過管道對數據進行驗證和持久化等操作。管道其實就是帶有process_item(self, item, spider)函數的一個普通類。下面是scrapy官方文檔的例子,這個例子驗證獲取到的數據是否存在價格字段,并丟棄沒有價格字段的無效數據。這里還引用了scrapy預定義的DropItem異常,這個異常必須在管道中拋出,表示管道應該丟棄這個數據。如果想了解scrapy異常,可以查看官方文檔。
from scrapy.exceptions import DropItemclass PricePipeline(object):vat_factor = 1.15def process_item(self, item, spider):if item['price']:if item['price_excludes_vat']:item['price'] = item['price'] * self.vat_factorreturn itemelse:raise DropItem("Missing price in %s" % item)管道不是一定義好就能用的,還需要在配置文件settings.py中激活。
ITEM_PIPELINES = {'myproject.pipelines.PricePipeline': 300,'myproject.pipelines.JsonWriterPipeline': 800, }將數據保存到MongoDB的管道
管道除了驗證數據,還可以將數據保存到數據庫中。這時候僅僅一個process_item(self, item, spider)函數就不夠了。所以操作數據庫的管道還應該包含幾個函數用于建立和關閉數據庫連接。
下面的例子也是scrapy官方文檔的例子,演示了持久化數據管道的用法。這個管道是從類方法from_crawler(cls, crawler)中初始化出來的,該方法實際上讀取了scrapy的配置文件。這和直接將數據庫連接寫在代碼中相比,是更加通用的方式。初始化和關閉數據庫連接的操作都在對應的方法中執行。
import pymongoclass MongoPipeline(object):collection_name = 'scrapy_items'def __init__(self, mongo_uri, mongo_db):self.mongo_uri = mongo_uriself.mongo_db = mongo_db@classmethoddef from_crawler(cls, crawler):return cls(mongo_uri=crawler.settings.get('MONGO_URI'),mongo_db=crawler.settings.get('MONGO_DATABASE', 'items'))def open_spider(self, spider):self.client = pymongo.MongoClient(self.mongo_uri)self.db = self.client[self.mongo_db]def close_spider(self, spider):self.client.close()def process_item(self, item, spider):self.db[self.collection_name].insert_one(dict(item))return item使用文件和圖片管道
文件和圖片管道處理過程
除了自己編寫管道之外,scrapy還預定義了幾個管道,可以幫助我們方便的保存文件和圖片。這些管道有以下特點:
- 可以避免重復下載最近的文件。
- 指定文件保存位置(文件系統或者亞馬遜S3)
對于圖片管道來說還有額外功能:
- 將圖片轉換成常見格式(JPG)和模式(RGB)
- 生成圖片縮略圖
- 只下載大于某長寬的圖片
使用文件管道的過程如下:
使用圖片管道的過程是相似的,不過要操作的屬性是image_urls和images。
如果你不想使用這幾個屬性,其實屬性名也是可以修改的,需要修改下面四個屬性。
FILES_URLS_FIELD = 'field_name_for_your_files_urls' FILES_RESULT_FIELD = 'field_name_for_your_processed_files' IMAGES_URLS_FIELD = 'field_name_for_your_images_urls' IMAGES_RESULT_FIELD = 'field_name_for_your_processed_images'管道配置
要啟用文件管道和圖片管道,同樣需要激活,當然如果同時激活這兩個管道也是可行的。
ITEM_PIPELINES = {'scrapy.pipelines.images.ImagesPipeline': 1} # 或者 ITEM_PIPELINES = {'scrapy.pipelines.files.FilesPipeline': 1}文件和圖片保存位置需要分別指定。
FILES_STORE = '/path/to/valid/dir' IMAGES_STORE = '/path/to/valid/dir'文件和圖片管道可以避免下載最近的文件,對應的文件過期時間也可以配置,單位是天。
# 120 days of delay for files expiration FILES_EXPIRES = 120# 30 days of delay for images expiration IMAGES_EXPIRES = 30圖片管道可以在保存圖片的時候同時生成縮略圖,縮略圖配置是一個字典,鍵是縮略圖的名字,值是縮略圖長和寬。
IMAGES_THUMBS = {'small': (50, 50),'big': (270, 270), }最后圖片會保存成下面這樣,圖片的文件名是圖片路徑的SHA1哈希值。
/圖片保存路徑/full/完整圖片.jpg /圖片保存路徑/thumbs/small/小圖片.jpg /圖片保存路徑/thumbs/big/中圖片.jpg如果不想使用SHA1哈希值作為文件名,可以繼承ImagesPipeline基類并重寫file_path函數,這里是另外一位簡書作者的爬蟲項目,他重寫了這個函數。我們可以作為參考。
如果要過濾小圖片,啟用下面的配置。默認情況下對圖片尺寸沒有約束,所以所有圖片都會下載。
IMAGES_MIN_HEIGHT = 110 IMAGES_MIN_WIDTH = 110默認情況下文件和圖片管道不支持重定向,遇到需要重定向的鏈接意味著下載失敗,不過我們也可以啟用重定向。
MEDIA_ALLOW_REDIRECTS = True下載器中間件
下載器中間件可以在scrapy引擎和爬蟲之間操縱請求和響應對象。要啟用下載器中間件,啟用下面的配置。這是一個字典,字典的鍵是要啟用的中間件,值會用來比較中間件之間的順序。
DOWNLOADER_MIDDLEWARES = {'myproject.middlewares.CustomDownloaderMiddleware': 543, }如果希望禁用某些內置的中間件,可以將值設置為None。
DOWNLOADER_MIDDLEWARES = {'myproject.middlewares.CustomDownloaderMiddleware': 543,'scrapy.downloadermiddlewares.useragent.UserAgentMiddleware': None, }編寫自己的下載器中間件
自定義下載器中間件應該繼承scrapy.downloadermiddlewares.DownloaderMiddleware類,該類有如下幾個方法,用于操縱請求和響應,我們只要重寫這幾個方法即可。這幾個方法的作用請參考官方文檔,它們比較復雜,所以我就不說了。
- process_request(request, spider)
- process_response(request, response, spider)
- process_exception(request, exception, spider)
內置的下載器中間件
scrapy內置了14個下載器中間件,我簡單介紹一下其中的幾個。詳情參考文檔。
CookiesMiddleware
用于在爬蟲發起請求和獲取響應的時候保持Cookie。
DefaultHeadersMiddleware
用于設置請求的默認請求頭。
該配置位于DEFAULT_REQUEST_HEADERS,默認值如下。
{'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8','Accept-Language': 'en', }HttpProxyMiddleware
設置使用的網絡代理。
UserAgentMiddleware
設置使用的用戶代理。
爬蟲中間件
與下載器中間件類似,啟用爬蟲中間件需要一個字典來配置。
SPIDER_MIDDLEWARES = {'myproject.middlewares.CustomSpiderMiddleware': 543, }想要關閉某個中間件的時候傳遞None值。
SPIDER_MIDDLEWARES = {'myproject.middlewares.CustomSpiderMiddleware': 543,'scrapy.spidermiddlewares.offsite.OffsiteMiddleware': None, }自定義爬蟲中間件
編寫自己的爬蟲中間件需要繼承scrapy.spidermiddlewares.SpiderMiddleware基類,并重寫以下幾個方法。
- process_spider_input(response, spider)
- process_spider_output(response, result, spider)
- process_spider_exception(response, exception, spider)
- process_start_requests(start_requests, spider)
內置的爬蟲中間件
scrapy內置了5個爬蟲中間件,這里我僅介紹一兩個。
DepthMiddleware
該中間件記錄了爬蟲爬取請求地址的深度。
我們可以使用DEPTH_LIMIT來指定爬蟲爬取的深度。
UrlLengthMiddleware
該中間件會過濾掉超出最大允許長度的URL,爬蟲不會訪問這些超長URL。
最大長度通過URLLENGTH_LIMIT配置來指定,默認值是2083。
URLLENGTH_LIMIT = 2083內建服務
scrapy內置了幾個服務,可以讓我們使用scrapy更加方便。
日志
爬蟲類定義了log函數,我們可以方便的在爬蟲類中記錄日志。
import scrapyclass MySpider(scrapy.Spider):name = 'myspider'start_urls = ['https://scrapinghub.com']def parse(self, response):self.logger.info('Parse function called on %s', response.url)日志相關的配置,點擊可以跳轉到官方文檔查看詳細信息。
- LOG_FILE
- LOG_ENABLED
- LOG_ENCODING
- LOG_LEVEL
- LOG_FORMAT
- LOG_DATEFORMAT
- LOG_STDOUT
- LOG_SHORT_NAMES
發送電子郵件
有時候我們可能希望爬到一定數量的數據就發送電子郵件進行提醒。scrapy也內置了這個功能。我們可以通過構造函數參數來創建郵件發送器。
from scrapy.mail import MailSender mailer = MailSender(這里是構造函數參數)也可以從配置文件實例化。
mailer = MailSender.from_settings(settings)然后調用send方法就可以發送郵件了。
mailer.send(to=["someone@example.com"], subject="Some subject", body="Some body", cc=["another@example.com"])電子郵件相關配置參考官方文檔。
web服務
這個功能本來是寫在官方文檔內建服務條目下的,但是實際上這個功能已經變成了一個單獨的項目,需要額外安裝。
pip install scrapy-jsonrpc然后在擴展中包含這個功能。
EXTENSIONS = {'scrapy_jsonrpc.webservice.WebService': 500, }還需要在配置中啟用該功能。
JSONRPC_ENABLED = True然后在爬蟲運行的時候訪問http://localhost:6080/crawler即可查看爬蟲運行情況了。
該項目的其他配置查看其官方文檔。
優化爬蟲
爬蟲項目可以通過修改一些配置進行優化。
增大并發數
并發數可以通過下面的配置進行設置。具體的并發數需要根據服務器的CPU等設置來進行更改。一般來說服務器CPU使用在80%-90%之間利用率比較高。我們可以從并發數100開始反復進行測試。
CONCURRENT_REQUESTS = 100增大線程池
scrapy通過一個線程池來進行DNS查詢,增大這個線程池一般也可以提高scrapy性能。
REACTOR_THREADPOOL_MAXSIZE = 20降低日志級別
默認情況下scrapy使用debug級別來打印日志,通過降低日志級別,我們可以減少日志打印,從而提高程序運行速度。
LOG_LEVEL = 'INFO'禁用Cookie
如果不是必須的,我們可以通過禁用Cookie來提高性能。如果需要登錄用戶才能爬取數據,不要禁用Cookie。
COOKIES_ENABLED = False關閉重試
頻繁重試可能導致目標服務器響應緩慢,我們自己訪問不了別人也訪問不了。所以可以考慮關閉重試。
RETRY_ENABLED = False減少下載超時
如果網絡連接比較快的話,我們可以減少下載超時,讓爬蟲卡住的請求中跳出來,一般可以提高爬蟲效率。
DOWNLOAD_TIMEOUT = 15關閉重定向
如果不是必要的話,我們可以關閉重定向來提高爬蟲性能。
REDIRECT_ENABLED = False自動調整爬蟲負載
scrapy有一個擴展可以自動調節服務器負載,它通過一個算法來確定最佳的爬蟲延時等設置。它的文檔在這里。
相關配置如下,點擊鏈接可以跳轉到對應文檔。
- AUTOTHROTTLE_ENABLED
- AUTOTHROTTLE_START_DELAY
- AUTOTHROTTLE_MAX_DELAY
- AUTOTHROTTLE_TARGET_CONCURRENCY
- AUTOTHROTTLE_DEBUG
- CONCURRENT_REQUESTS_PER_DOMAIN
- CONCURRENT_REQUESTS_PER_IP
- DOWNLOAD_DELAY
部署爬蟲
官方文檔介紹了兩種部署爬蟲的方式,可以將爬蟲部署到服務器上遠程執行。第一種是通過Scrapyd開源項目來部署,也是這里要介紹的方式。第二種是通過scrapy公司提供的商業收費版服務Scrapy Cloud部署,推薦有財力的公司考慮。
服務器端
首先服務器需要安裝scrapyd包,如果是Linux系統還可以考慮使用對應的包管理器來安裝。
pip install scrapyd apt-get install scrapyd然后運行scrapyd服務,如果使用系統包管理器安裝,那么可能已經配置好了systemd文件。
scrapyd # 或者 systemctl enable scrapydscrapyd附帶了一個簡單的web界面可以幫助我們查看爬蟲運行情況,默認情況下訪問http://localhost:6800/來查看這個界面。
scrapyd的配置文件可以是~/.scrapyd.conf或者/etc/scrapyd/scrapyd.conf。下面是一個簡單配置,綁定所有端口,這樣一來從任意位置都可以訪問web界面。
[scrapyd] bind_address = 0.0.0.0scrapyd的功能可以查看其API文檔。
客戶端
客戶端如果要上傳爬蟲,可以通過服務器API的端點addversion.json來實現,或者安裝一個簡便工具scrapyd-client。
首先安裝客戶端工具。
pip install scrapyd-client這個客戶端目前好像有bug,在windows下運行scrapy-deploy命令不會直接執行,而是彈出一個文件關聯對話框。如果你遇到這種情況,可以找到Python安裝路徑下的腳本路徑(例如C:\Program Files\Python36\Scripts),然后編寫一個scrapyd-deploy.bat批處理文件,內容如下。這樣就可以正常運行了。
@"c:\program files\python36\python.exe" "c:\program files\python36\Scripts\scrapyd-deploy" %*然后切換到項目路徑,編輯項目全局配置文件scrapy.cfg,添加部署路徑。
[deploy] url = http://192.168.64.136:6800/ project = quotesbot然后直接運行scrapy-deploy命令,就可以看到項目已經成功部署到服務器上了。
?
運行爬蟲需要使用scrapyd的API,例如使用curl,可以用下面的命令。
curl http://192.168.64.136:6800/schedule.json -d project=quotesbot -d spider=toscrape-css或者使用Jetbrains 系列IDE 2017.3的基于編輯器的HTTP客戶端。
然后點擊Jobs就可以看到爬蟲已經開始運行了。如果要查看狀態,點擊右邊的log即可。
以上就是scrapy的進階介紹了,利用這些功能,我們可以編寫更加實用的爬蟲,并將它們部署到服務器上。
?
?
?
總結
以上是生活随笔為你收集整理的scrapy学习笔记(二)进阶使用的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 简明 Python 教程学习笔记_2_函
- 下一篇: OFD文件结构--带签名