scrapy是python最有名的爬蟲框架之一,能夠很方便的進行web抓取,而且提供了很強的定製型,這裏記錄簡單學習的過程和在實際應用中會遇到的一些常見問題css
在安裝scrapy以前有一些依賴須要安裝,不然可能會安裝失敗,scrapy的選擇器依賴於lxml
,還有Twisted
網絡引擎,下面是ubuntu下安裝的過程html
# 1. 安裝xml依賴庫 $ sudo apt-get install libxml2 libxml2-dev $ sudo apt-get install libxslt1-dev $ sudo apt-get install python-libxml2 # 2. 安裝lxml $ sudo pip install lxml # 3. 安裝Twisted(版本能夠換成最新的),用pip也能夠,若是失敗的話下載源碼安裝,以下 $ wget https://pypi.python.org/packages/6b/23/8dbe86fc83215015e221fbd861a545c6ec5c9e9cd7514af114d1f64084ab/Twisted-16.4.1.tar.bz2#md5=c6d09bdd681f538369659111f079c29d $ tar xjf Twisted-16.4.1.tar.bz2 $ cd Twisted-16.4.1 $ sudo python setup.py install # 3. 安裝scrapy $ sudo pip install scrapy
# 安裝xml依賴庫 $ xcode-select —install # 其實相關依賴pip會自動幫咱們裝上 $ pip install scrapy
mac下安裝有時候會失敗,建議使用virtualenv
安裝在獨立的環境下,能夠減小一些問題,由於mac系統自帶python,例如一些依賴庫依賴的一些新的版本,而升級新版本會把舊版本卸載掉,卸載可能會有權限的問題python
咱們可使用命令行初始化一個項目linux
$ scrapy startproject tutorial
這裏能夠查看scrapy更多其餘的命令git
初始化完成後,咱們獲得下面目錄結構github
scrapy.cfg: 項目的配置文件 tutorial/: 該項目的python模塊, 在這裏添加代碼 items.py: 項目中的item文件 pipelines.py: 項目中的pipelines文件. settings.py: 項目全局設置文件. spiders/ 爬蟲模塊目錄
咱們先看一下scrapy的處理流程web
scrapy由下面幾個部分組成正則表達式
spiders
:爬蟲模塊,負責配置須要爬取的數據和爬取規則,以及解析結構化數據數據庫
items
:定義咱們須要的結構化數據,使用至關於dict
pipelines
:管道模塊,處理spider模塊分析好的結構化數據,如保存入庫等
middlewares
:中間件,至關於鉤子,能夠對爬取先後作預處理,如修改請求header,url過濾等
咱們先來看一個例子,在spiders
目錄下新建一個模塊DmozSpider.py
import scrapy class DmozSpider(scrapy.Spider): # 必須定義 name = "dmoz" # 初始urls start_urls = [ "http://www.dmoz.org/Computers/Programming/Languages/Python/Books/", "http://www.dmoz.org/Computers/Programming/Languages/Python/Resources/" ] # 默認response處理函數 def parse(self, response): # 把結果寫到文件中 filename = response.url.split("/")[-2] with open(filename, 'wb') as f: f.write(response.body)
打開終端進入根目錄,執行下面命令
$ scrapy crawl dmoz
爬蟲開始爬取start_urls定義的url,並輸出到文件中,最後輸出爬去報告,會輸出爬取得統計結果
2016-09-13 10:36:43 [scrapy] INFO: Spider opened 2016-09-13 10:36:43 [scrapy] INFO: Crawled 0 pages (at 0 pages/min), scraped 0 items (at 0 items/min) 2016-09-13 10:36:43 [scrapy] DEBUG: Telnet console listening on 127.0.0.1:6023 2016-09-13 10:36:44 [scrapy] DEBUG: Crawled (200) <GET http://www.dmoz.org/Computers/Programming/Languages/Python/Resources/> (referer: None) 2016-09-13 10:36:45 [scrapy] DEBUG: Crawled (200) <GET http://www.dmoz.org/Computers/Programming/Languages/Python/Books/> (referer: None) 2016-09-13 10:36:45 [scrapy] INFO: Closing spider (finished) 2016-09-13 10:36:45 [scrapy] INFO: Dumping Scrapy stats: {'downloader/request_bytes': 548, 'downloader/request_count': 2, 'downloader/request_method_count/GET': 2, 'downloader/response_bytes': 16179, 'downloader/response_count': 2, 'downloader/response_status_count/200': 2, 'finish_reason': 'finished', 'finish_time': datetime.datetime(2016, 9, 13, 2, 36, 45, 585113), 'log_count/DEBUG': 3, 'log_count/INFO': 7, 'response_received_count': 2, 'scheduler/dequeued': 2, 'scheduler/dequeued/memory': 2, 'scheduler/enqueued': 2, 'scheduler/enqueued/memory': 2, 'start_time': datetime.datetime(2016, 9, 13, 2, 36, 43, 935790)} 2016-09-13 10:36:45 [scrapy] INFO: Spider closed (finished)
這裏咱們完成了簡單的爬取和保存的操做,會在根目錄生成兩個文件Resources
和Books
每次進入控制檯運行爬蟲仍是比較麻煩的,並且很差調試,咱們能夠經過CrawlerProcess
經過代碼運行爬蟲,新建一個模塊run.py
from scrapy.crawler import CrawlerProcess from scrapy.utils.project import get_project_settings from spiders.DmozSpider import DmozSpider # 獲取settings.py模塊的設置 settings = get_project_settings() process = CrawlerProcess(settings=settings) # 能夠添加多個spider # process.crawl(Spider1) # process.crawl(Spider2) process.crawl(DmozSpider) # 啓動爬蟲,會阻塞,直到爬取完成 process.start()
參考:http://doc.scrapy.org/en/latest/topics/practices.html#run-scrapy-from-a-script
如上面的DmozSpider
類,爬蟲類繼承自scrapy.Spider
,用於構造Request
對象給Scheduler
屬性
name
:爬蟲的名字,必須惟一(若是在控制檯使用的話,必須配置)
start_urls
:爬蟲初始爬取的連接列表
parse
:response結果處理函數
custom_settings
:自定義配置,覆蓋settings.py
中的默認配置
方法
start_requests
:啓動爬蟲的時候調用,默認是調用make_requests_from_url
方法爬取start_urls
的連接,能夠在這個方法裏面定製,若是重寫了該方法,start_urls默認將不會被使用,能夠在這個方法裏面定製一些自定義的url,如登陸,從數據庫讀取url等,本方法返回Request對象
make_requests_from_url
:默認由start_requests
調用,能夠配置Request對象,返回Request對象
parse
:response到達spider的時候默認調用,若是在Request對象配置了callback函數,則不會調用,parse方法能夠迭代返回Item
或Request
對象,若是返回Request對象,則會進行增量爬取
每一個請求都是一個Request對象,Request對象定義了請求的相關信息(url
, method
, headers
, body
, cookie
, priority
)和回調的相關信息(meta
, callback
, dont_filter
, errback
),一般由spider迭代返回
其中meta
至關於附加變量,能夠在請求完成後經過response.meta
訪問
請求完成後,會經過Response
對象發送給spider處理,經常使用屬性有(url
, status
, headers
, body
, request
, meta
, )
詳細介紹參考官網
https://doc.scrapy.org/en/latest/topics/request-response.html#request-objects
https://doc.scrapy.org/en/latest/topics/request-response.html#response-objects
看下面這個例子
from scrapy import Spider from scrapy import Request class TestSpider(Spider): name = 'test' start_urls = [ "http://www.qq.com/", ] def login_parse(self, response): ''' 若是登陸成功,手動構造請求Request迭代返回 ''' print response for i in range(0, 10): yield Request('http://www.example.com/list/1?page={0}'.format(i)) def start_requests(self): ''' 覆蓋默認的方法(忽略start_urls),返回登陸請求頁,制定處理函數爲login_parse ''' return Request('http://www.example.com/login', method="POST" body='username=bomo&pwd=123456', callback=self.login_parse) def parse(self, response): ''' 默認請求處理函數 ''' print response
上面咱們只是爬取了網頁的html文本,對於爬蟲,咱們須要明確咱們須要爬取的結構化數據,須要對原文本進行解析,解析的方法一般有下面這些
普通文本操做
正則表達式:re
Dom樹操做:BeautifulSoup
XPath選擇器:lxml
scrapy默認支持選擇器的功能,自帶的選擇器構建與lxml之上,並對其進行了改進,使用起來更爲簡潔明瞭
XPpath是標準的XML文檔查詢語言,能夠用於查詢XML文檔中的節點和內容,關於XPath語法,能夠參見這裏
先看一個例子,經過html或xml構造Selector對象,而後經過xpath查詢節點,並解析出節點的內容
from scrapy import Selector html = '<html><body><span>good</span><span>buy</span></body></html>' sel = Selector(text=html) nodes = sel.xpath('//span') for node in nodes: print node.extract()
Selector至關於節點,經過xpath去到子節點集合(SelectorList),能夠繼續搜索,經過extract
方法能夠取出節點的值,extract
方法也能夠做用於SelectorList,對於SelectorList能夠經過extract_first
取出第一個節點的值
經過text()
取出節點的內容
經過@href
去除節點屬性值(這裏是取出href
屬性的值)
直接對節點取值,則是輸出節點的字符串
除了XPath選擇器,scrapy還支持css選擇器
html = """ <html> <body> <span>good</span> <span>buy</span> <ul> <li class="video_part_lists">aa<li> <li class="video_part_lists">bb<li> <li class="audio_part_lists">cc<li> <li class="video_part_lists"> <a href="/">主頁</a> <li> </ul> </body> </html> """ sel = Selector(text=html) # 選擇class爲video_part_lists的li節點 lis = sel.css('li.video_part_lists') for li in lis: # 選擇a節點的屬性 print li.css('a::attr(href)').extract()
關於css選擇器更多的規則,能夠見w3c官網
上面咱們只是爬取了網頁的html文本,對於爬蟲,咱們須要明確咱們須要爬取的結構化數據,咱們定義一個item存儲分類信息,scrapy的item繼承自scrapy.Item
from scrapy import Item, Field class DmozItem(Item): title = Field() link = Field() desc = Field()
scrapy.Item
的用法與python中的字典用法基本同樣,只是作了一些安全限制,屬性定義使用Field,這裏只是進行了聲明,而不是真正的屬性,使用的時候經過鍵值對操做,不支持屬性訪問
what, 好坑爹,這意味着全部的屬性賦值都得用字符串了,這裏有解釋(仍是沒太明白)
修改DmozSpider的parse方法
class DmozSpider(scrapy.Spider): ... def parse(self, response): for sel in response.xpath('//ul/li'): dmoz_item = DmozItem() dmoz_item['title'] = sel.xpath('a/text()').extract() dmoz_item['link'] = sel.xpath('a/@href').extract() dmoz_item['desc'] = sel.xpath('text()').extract() print dmoz_item
spider負責爬蟲的配置,item負責聲明結構化數據,而對於數據的處理,在scrapy中使用管道的方式進行處理,只要註冊過的管道均可以處理item數據(處理,過濾,保存)
下面看看管道的聲明方式,這裏定義一個預處理管道PretreatmentPipeline.py
,若是item的title爲None,則設置爲空字符串
class PretreatmentPipeline(object): def process_item(self, item, spider): if item['title']: # 不讓title爲空 item['title'] = '' return item
再定義一個過濾重複數據的管道DuplicatesPipeline.py
,當link重複,則丟棄
from scrapy.exceptions import DropItem class DuplicatesPipeline(object): def __init__(self): self.links = set() def process_item(self, item, spider): if item['link'] in self.links: # 跑出DropItem表示丟掉數據 raise DropItem("Duplicate item found: %s" % item) else: self.links.add(item['link']) return item
最後能夠定義一個保存數據的管道,能夠把數據保存到數據庫中
from scrapy.exceptions import DropItem from Database import Database class DatabasePipeline(object): def __init__(self): self.db = Database def process_item(self, item, spider): if self.db.item_exists(item['id']): self.db.update_item(item) else: self.db.insert_item(item)
定義好管道以後咱們須要配置到爬蟲上,咱們在settings.py
模塊中配置,後面的數字表示管道的順序
ITEM_PIPELINES = { 'pipelines.DuplicatesPipeline.DuplicatesPipeline': 1, 'pipelines.PretreatmentPipeline.PretreatmentPipeline': 2, }
咱們也能夠爲spider配置單獨的pipeline
class TestSpider(Spider): # 自定義配置 custom_settings = { # item處理管道 'ITEM_PIPELINES': { 'tutorial.pipelines.FangDetailPipeline.FangDetailPipeline': 1, }, } ...
除了process_item
方法外,pipeline還有open_spider
和spider_closed
兩個方法,在爬蟲啓動和關閉的時候調用
爬蟲的一般須要在一個網頁裏面爬去其餘的連接,而後一層一層往下爬,scrapy提供了LinkExtractor類用於對網頁連接的提取,使用LinkExtractor須要使用CrawlSpider
爬蟲類中,CrawlSpider
與Spider
相比主要是多了rules
,能夠添加一些規則,先看下面這個例子,爬取鏈家網的連接
from scrapy.spiders import CrawlSpider, Rule from scrapy.linkextractors import LinkExtractor class LianjiaSpider(CrawlSpider): name = "lianjia" allowed_domains = ["lianjia.com"] start_urls = [ "http://bj.lianjia.com/ershoufang/" ] rules = [ # 匹配正則表達式,處理下一頁 Rule(LinkExtractor(allow=(r'http://bj.lianjia.com/ershoufang/pg\s+$',)), callback='parse_item'), # 匹配正則表達式,結果加到url列表中,設置請求預處理函數 # Rule(FangLinkExtractor(allow=('http://www.lianjia.com/client/', )), follow=True, process_request='add_cookie') ] def parse_item(self, response): # 這裏與以前的parse方法同樣,處理 pass
Role對象有下面參數
link_extractor
:連接提取規則
callback
:link_extractor提取的連接的請求結果的回調
cb_kwargs
:附加參數,能夠在回調函數中獲取到
follow
:表示提取的連接請求完成後是否還要應用當前規則(boolean),若是爲False
則不會對提取出來的網頁進行進一步提取,默認爲False
process_links
:處理全部的連接的回調,用於處理從response提取的links,一般用於過濾(參數爲link列表)
process_request
:連接請求預處理(添加header或cookie等)
LinkExtractor經常使用的參數有:
allow
:提取知足正則表達式的連接
deny
:排除正則表達式匹配的連接(優先級高於allow
)
allow_domains
:容許的域名(能夠是str
或list
)
deny_domains
:排除的域名(能夠是str
或list
)
restrict_xpaths
:提取知足XPath選擇條件的連接(能夠是str
或list
)
restrict_css
:提取知足css選擇條件的連接(能夠是str
或list
)
tags
:提取指定標籤下的連接,默認從a
和area
中提取(能夠是str
或list
)
attrs
:提取知足擁有屬性的連接,默認爲href
(類型爲list
)
unique
:連接是否去重(類型爲boolean
)
process_value
:值處理函數(優先級大於allow
)
關於LinkExtractor的詳細參數介紹見官網
注意:若是使用rules規則,請不要覆蓋或重寫
CrawlSpider
的parse
方法,不然規則會失效,可使用parse_start_urls
方法
從最開始的流程圖能夠看到,爬去一個資源連接的流程,首先咱們配置spider相關的爬取信息,在啓動爬取實例後,scrapy_engine
從Spider取出Request
(通過SpiderMiddleware
),而後丟給Scheduler(通過SchedulerMiddleware
),Scheduler接着把請求丟給Downloader(通過DownloadMiddlware
),Downloader把請求結果丟還給Spider,而後Spider把分析好的結構化數據丟給Pipeline,Pipeline進行分析保存或丟棄,這裏面有4個角色
scrapy有下面三種middlewares
SpiderMiddleware
:一般用於配置爬蟲相關的屬性,引用連接設置,Url長度限制,成功狀態碼設置,爬取深度設置,爬去優先級設置等
DownloadMiddlware
:一般用於處理下載以前的預處理,如請求Header(Cookie,User-Agent),登陸驗證處理,重定向處理,代理服務器處理,超時處理,重試處理等
SchedulerMiddleware
(已經廢棄):爲了簡化框架,調度器中間件已經被廢棄,使用另外兩個中間件已經夠用了
爬蟲中間件有下面幾個方法
process_spider_input
:當response經過spider的時候被調用,返回None(繼續給其餘中間件處理)或拋出異常(不會給其餘中間件處理,當成異常處理)
process_spider_output
:當spider有item或Request輸出的時候調動
process_spider_exception
:處理出現異常時調用
process_start_requests
:spider當開始請求Request的時候調用
下面是scrapy自帶的一些中間件(在scrapy.spidermiddlewares
命名空間下)
UrlLengthMiddleware
RefererMiddleware
OffsiteMiddleware
HttpErrorMiddleware
DepthMiddleware
咱們本身實現一個SpiderMiddleware
TODO
參考連接:http://doc.scrapy.org/en/latest/topics/spider-middleware.html
下載中間件有下面幾個方法
process_request
:請求經過下載器的時候調用
process_response
:請求完成後調用
process_exception
:請求發生異常時調用
from_crawler
:從crawler構造的時候調用
from_settings
:從settings構造的時候調用
``
更多詳細的參數解釋見這裏
在爬取網頁的時候,使用不一樣的User-Agent
能夠提升請求的隨機性,定義一個隨機設置User-Agent的中間件RandomUserAgentMiddleware
import random class RandomUserAgentMiddleware(object): """Randomly rotate user agents based on a list of predefined ones""" def __init__(self, agents): self.agents = agents # 從crawler構造,USER_AGENTS定義在crawler的配置的設置中 @classmethod def from_crawler(cls, crawler): return cls(crawler.settings.getlist('USER_AGENTS')) # 從settings構造,USER_AGENTS定義在settings.py中 @classmethod def from_settings(cls, settings): return cls(settings.getlist('USER_AGENTS')) def process_request(self, request, spider): # 設置隨機的User-Agent request.headers.setdefault('User-Agent', random.choice(self.agents))
在settings.py
設置USER_AGENTS參數
USER_AGENTS = [ "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; AcooBrowser; .NET CLR 1.1.4322; .NET CLR 2.0.50727)", "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0; Acoo Browser; SLCC1; .NET CLR 2.0.50727; Media Center PC 5.0; .NET CLR 3.0.04506)", "Mozilla/4.0 (compatible; MSIE 7.0; AOL 9.5; AOLBuild 4337.35; Windows NT 5.1; .NET CLR 1.1.4322; .NET CLR 2.0.50727)", "Mozilla/5.0 (Windows; U; MSIE 9.0; Windows NT 9.0; en-US)", "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Win64; x64; Trident/5.0; .NET CLR 3.5.30729; .NET CLR 3.0.30729; .NET CLR 2.0.50727; Media Center PC 6.0)", "Mozilla/5.0 (compatible; MSIE 8.0; Windows NT 6.0; Trident/4.0; WOW64; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; .NET CLR 1.0.3705; .NET CLR 1.1.4322)", "Mozilla/4.0 (compatible; MSIE 7.0b; Windows NT 5.2; .NET CLR 1.1.4322; .NET CLR 2.0.50727; InfoPath.2; .NET CLR 3.0.04506.30)", "Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN) AppleWebKit/523.15 (KHTML, like Gecko, Safari/419.3) Arora/0.3 (Change: 287 c9dfb30)", "Mozilla/5.0 (X11; U; Linux; en-US) AppleWebKit/527+ (KHTML, like Gecko, Safari/419.3) Arora/0.6", "Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1.2pre) Gecko/20070215 K-Ninja/2.1.1", "Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN; rv:1.9) Gecko/20080705 Firefox/3.0 Kapiko/3.0", "Mozilla/5.0 (X11; Linux i686; U;) Gecko/20070322 Kazehakase/0.4.5", "Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.8) Gecko Fedora/1.9.0.8-1.fc10 Kazehakase/0.5.6", "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/535.11 (KHTML, like Gecko) Chrome/17.0.963.56 Safari/535.11", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_3) AppleWebKit/535.20 (KHTML, like Gecko) Chrome/19.0.1036.7 Safari/535.20", "Opera/9.80 (Macintosh; Intel Mac OS X 10.6.8; U; fr) Presto/2.9.168 Version/11.52", ]
配置爬蟲中間件的方式與pipeline相似,第二個參數表示優先級
# 配置爬蟲中間件 SPIDER_MIDDLEWARES = { 'myproject.middlewares.CustomSpiderMiddleware': 543, # 若是想禁用默認的中間件的話,能夠設置其優先級爲None 'scrapy.spidermiddlewares.offsite.OffsiteMiddleware': None, } # 配置下載中間件 DOWNLOADER_MIDDLEWARES = { 'myproject.middlewares.RandomUserAgentMiddleware': 543, 'scrapy.contrib.downloadermiddleware.useragent.UserAgentMiddleware': None, }
爬蟲最怕的就是封ip,這時候就須要代理服務器來爬取,scrapy設置代理服務器很是簡單,只須要在請求前設置Request
對象的meta
屬性,添加proxy
值便可,一般咱們能夠經過中間件來作
class ProxyMiddleware(object): def process_request(self, request, spider): proxy = 'https://178.33.6.236:3128' # 代理服務器 request.meta['proxy'] = proxy
scrapy默認已經自帶了緩存的功能,一般咱們只須要配置便可,打開settings.py
# 打開緩存 HTTPCACHE_ENABLED = True # 設置緩存過時時間(單位:秒) #HTTPCACHE_EXPIRATION_SECS = 0 # 緩存路徑(默認爲:.scrapy/httpcache) HTTPCACHE_DIR = 'httpcache' # 忽略的狀態碼 HTTPCACHE_IGNORE_HTTP_CODES = [] # 緩存模式(文件緩存) HTTPCACHE_STORAGE = 'scrapy.extensions.httpcache.FilesystemCacheStorage'
更多參數參見這裏
scrapy網絡請求是基於Twisted,而Twisted默認支持多線程,並且scrapy默認也是經過多線程請求的,而且支持多核CPU的併發,一般只須要配置一些參數便可
# 默認Item併發數:100 CONCURRENT_ITEMS = 100 # 默認Request併發數:16 CONCURRENT_REQUESTS = 16 # 默認每一個域名的併發數:8 CONCURRENT_REQUESTS_PER_DOMAIN = 8 # 每一個IP的最大併發數:0表示忽略 CONCURRENT_REQUESTS_PER_IP = 0
更多參數參見這裏
在使用的時候遇到過一個問題,在初始化scrapy startproject tutorial
的時候,若是使用了一些特殊的名字,如:test
, fang
等單詞的話,經過get_project_settings
方法獲取配置的時候會出錯,改爲tutorial
或一些複雜的名字的時候不會
ImportError: No module named tutorial.settings
這是一個bug,在github上有提到:https://github.com/scrapy/scrapy/issues/428,但貌似沒有徹底修復,修改一下名字就行了(固然scrapy.cfg
和settings.py
裏面也須要修改)
上面咱們是在settings.py裏面配置pipeline,這裏的配置的pipeline會做用於全部的spider,咱們能夠爲每個spider配置不一樣的pipeline,設置Spider
的custom_settings
對象
class LianjiaSpider(CrawlSpider): ... # 自定義配置 custom_settings = { 'ITEM_PIPELINES': { 'tutorial.pipelines.TestPipeline.TestPipeline': 1, } }
經過LinkExtractor提取的scrapy.Link
默認不帶節點信息,有時候咱們須要節點的其餘attribute屬性,scrapy.Link
有個text
屬性保存從節點提取的text
值,咱們能夠經過修改lxmlhtml._collect_string_content
變量爲etree.tostring
,這樣能夠在提取節點值就變味渲染節點scrapy.Link.text
,而後根據scrapy.Link.text
屬性拿到節點的html,最後提取出咱們須要的值
from lxml import etree import scrapy.linkextractors.lxmlhtml scrapy.linkextractors.lxmlhtml._collect_string_content = etree.tostring
有時候咱們已經把urls下載到數據庫了,而不是在start_urls裏配置,這時候能夠重載spider的start_requests
方法
def start_requests(self): for u in self.db.session.query(User.link): yield Request(u.link)
咱們還能夠在Request添加元數據,而後在response中訪問
def start_requests(self): for u in self.db.session.query(User): yield Request(u.link, meta={'name': u.name}) def parse(self, response): print response.url, response.meta['name']
有時候咱們須要爬取的一些常常更新的頁面,例如:間隔時間爲2s,爬去一個列表前10頁的數據,從第一頁開始爬,爬完成後從新回到第一頁
目前的思路是,經過parse方法迭代返回Request進行增量爬取,因爲scrapy默認由緩存機制,須要修改
scrapy默認有本身的去重機制,默認使用scrapy.dupefilters.RFPDupeFilter
類進行去重,主要邏輯以下
if include_headers: include_headers = tuple(to_bytes(h.lower()) for h in sorted(include_headers)) cache = _fingerprint_cache.setdefault(request, {}) if include_headers not in cache: fp = hashlib.sha1() fp.update(to_bytes(request.method)) fp.update(to_bytes(canonicalize_url(request.url))) fp.update(request.body or b'') if include_headers: for hdr in include_headers: if hdr in request.headers: fp.update(hdr) for v in request.headers.getlist(hdr): fp.update(v) cache[include_headers] = fp.hexdigest() return cache[include_headers]
默認的去重指紋是sha1(method + url + body + header),這種方式並不能過濾不少,例若有一些請求會加上時間戳的,基本每次都會不一樣,這時候咱們須要自定義過濾規則
from scrapy.dupefilter import RFPDupeFilter class CustomURLFilter(RFPDupeFilter): """ 只根據url去重""" def __init__(self, path=None): self.urls_seen = set() RFPDupeFilter.__init__(self, path) def request_seen(self, request): if request.url in self.urls_seen: return True else: self.urls_seen.add(request.url)
配置setting
DUPEFILTER_CLASS = 'tutorial.custom_filters.CustomURLFilter'
scrapy全部的迭代出來的的Item都會通過全部的Pipeline,若是須要處理不一樣的Item,只能經過isinstance()
方法進行類型判斷,而後分別進行處理,暫時沒有更好的方案
咱們能夠經過Request的priority控制url的請求的執行順序,但因爲網絡請求的不肯定性,不能保證返回也是按照順序進行的,若是須要進行逐個url請求的話,吧url列表放在meta對象裏面,在response的時候迭代返回下一個Request對象到調度器,達到順序執行的目的,暫時沒有更好的方案
scrapy雖然是最有名的python爬蟲框架,可是仍是有不少不足,例如,item不能單獨配置給制定的pipeline,每個爬取的全部item都會走遍全部的管道,須要在管道里面去判斷不一樣類型的item,若是在pipelines和items比較多的項目,將會讓項目變得很是臃腫
若有問題歡迎到個人博客留言
最後安利一下本身的博客:http://zhengbomo.github.com