摘要:是最有名的爬蟲框架之一,可以很方便的進行抓取,并且提供了很強的定制型,這里記錄簡單學習的過程和在實際應用中會遇到的一些常見問題一安裝在安裝之前有一些依賴需要安裝,否則可能會安裝失敗,的選擇器依賴于,還有網絡引擎,下面是下安裝的過程下安裝安裝
scrapy是python最有名的爬蟲框架之一,可以很方便的進行web抓取,并且提供了很強的定制型,這里記錄簡單學習的過程和在實際應用中會遇到的一些常見問題
一、安裝在安裝scrapy之前有一些依賴需要安裝,否則可能會安裝失敗,scrapy的選擇器依賴于lxml,還有Twisted網絡引擎,下面是ubuntu下安裝的過程
1. linux下安裝# 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
2. Mac下安裝http://lxml.de/installation.html
# 安裝xml依賴庫 $ xcode-select —install # 其實相關依賴pip會自動幫我們裝上 $ pip install scrapy
mac下安裝有時候會失敗,建議使用virtualenv安裝在獨立的環境下,可以減少一些問題,因為mac系統自帶python,例如一些依賴庫依賴的一些新的版本,而升級新版本會把舊版本卸載掉,卸載可能會有權限的問題
二、基本使用 1. 初始化scrapy項目我們可以使用命令行初始化一個項目
$ scrapy startproject tutorial
這里可以查看scrapy更多其他的命令
初始化完成后,我們得到下面目錄結構
scrapy.cfg: 項目的配置文件 tutorial/: 該項目的python模塊, 在這里添加代碼 items.py: 項目中的item文件 pipelines.py: 項目中的pipelines文件. settings.py: 項目全局設置文件. spiders/ 爬蟲模塊目錄
我們先看一下scrapy的處理流程
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)(referer: None) 2016-09-13 10:36:45 [scrapy] DEBUG: Crawled (200) (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
2. 通過代碼運行爬蟲每次進入控制臺運行爬蟲還是比較麻煩的,而且不好調試,我們可以通過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()
三、Scrapy類參考:http://doc.scrapy.org/en/latest/topics/practices.html#run-scrapy-from-a-script
如上面的DmozSpider類,爬蟲類繼承自scrapy.Spider,用于構造Request對象給Scheduler
1. 常用屬性與方法屬性
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對象,則會進行增量爬取
2. Request與Response對象每個請求都是一個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四、Selector
上面我們只是爬取了網頁的html文本,對于爬蟲,我們需要明確我們需要爬取的結構化數據,需要對原文本進行解析,解析的方法通常有下面這些
普通文本操作
正則表達式:re
Dom樹操作:BeautifulSoup
XPath選擇器:lxml
scrapy默認支持選擇器的功能,自帶的選擇器構建與lxml之上,并對其進行了改進,使用起來更為簡潔明了
1. XPath選擇器XPpath是標準的XML文檔查詢語言,可以用于查詢XML文檔中的節點和內容,關于XPath語法,可以參見這里
先看一個例子,通過html或xml構造Selector對象,然后通過xpath查詢節點,并解析出節點的內容
from scrapy import Selector html = "goodbuy" sel = Selector(text=html) nodes = sel.xpath("http://span") for node in nodes: print node.extract()
Selector相當于節點,通過xpath去到子節點集合(SelectorList),可以繼續搜索,通過extract方法可以取出節點的值,extract方法也可以作用于SelectorList,對于SelectorList可以通過extract_first取出第一個節點的值
通過text()取出節點的內容
通過@href去除節點屬性值(這里是取出href屬性的值)
直接對節點取值,則是輸出節點的字符串
2. CSS選擇器除了XPath選擇器,scrapy還支持css選擇器
html = """ good buy
關于css選擇器更多的規則,可以見w3c官網
https://www.w3.org/TR/selectors/
五、Item類上面我們只是爬取了網頁的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, 好坑爹,這意味著所有的屬性賦值都得用字符串了,這里有解釋(還是沒太明白)
why-is-scrapys-field-a-dict
修改DmozSpider的parse方法
class DmozSpider(scrapy.Spider): ... def parse(self, response): for sel in response.xpath("http://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六、Pipeline
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兩個方法,在爬蟲啟動和關閉的時候調用
七、Rule爬蟲的通常需要在一個網頁里面爬去其他的鏈接,然后一層一層往下爬,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/pgs+$",)), callback="parse_item"), # 匹配正則表達式,結果加到url列表中,設置請求預處理函數 # Rule(FangLinkExtractor(allow=("http://www.lianjia.com/client/", )), follow=True, process_request="add_cookie") ] def parse_item(self, response): # 這里與之前的parse方法一樣,處理 pass1. Rule對象
Role對象有下面參數
link_extractor:鏈接提取規則
callback:link_extractor提取的鏈接的請求結果的回調
cb_kwargs:附加參數,可以在回調函數中獲取到
follow:表示提取的鏈接請求完成后是否還要應用當前規則(boolean),如果為False則不會對提取出來的網頁進行進一步提取,默認為False
process_links:處理所有的鏈接的回調,用于處理從response提取的links,通常用于過濾(參數為link列表)
process_request:鏈接請求預處理(添加header或cookie等)
2. LinkExtractorLinkExtractor常用的參數有:
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的詳細參數介紹見官網
八、Middleware注意:如果使用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(已經廢棄):為了簡化框架,調度器中間件已經被廢棄,使用另外兩個中間件已經夠用了
1. SpiderMiddleware爬蟲中間件有下面幾個方法
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
2. DownloaderMiddleware參考鏈接: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, }3. 代理服務器
爬蟲最怕的就是封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
十一、常見問題 1. 項目名稱問題更多參數參見這里
在使用的時候遇到過一個問題,在初始化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里面也需要修改)
2. 為每個pipeline配置spider上面我們是在settings.py里面配置pipeline,這里的配置的pipeline會作用于所有的spider,我們可以為每一個spider配置不同的pipeline,設置Spider的custom_settings對象
class LianjiaSpider(CrawlSpider): ... # 自定義配置 custom_settings = { "ITEM_PIPELINES": { "tutorial.pipelines.TestPipeline.TestPipeline": 1, } }3. 獲取提取鏈接的節點信息
通過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.tostring4. 從數據庫中讀取urls
有時候我們已經把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"]5. 如何進行循環爬取
有時候我們需要爬取的一些經常更新的頁面,例如:間隔時間為2s,爬去一個列表前10頁的數據,從第一頁開始爬,爬完成后重新回到第一頁
目前的思路是,通過parse方法迭代返回Request進行增量爬取,由于scrapy默認由緩存機制,需要修改
6. 關于去重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"7. 如何在Pipeline中處理不同的Item
scrapy所有的迭代出來的的Item都會經過所有的Pipeline,如果需要處理不同的Item,只能通過isinstance()方法進行類型判斷,然后分別進行處理,暫時沒有更好的方案
8. url按順序執行我們可以通過Request的priority控制url的請求的執行順序,但由于網絡請求的不確定性,不能保證返回也是按照順序進行的,如果需要進行逐個url請求的話,吧url列表放在meta對象里面,在response的時候迭代返回下一個Request對象到調度器,達到順序執行的目的,暫時沒有更好的方案
十二、總結scrapy雖然是最有名的python爬蟲框架,但是還是有很多不足,例如,item不能多帶帶配置給制定的pipeline,每一個爬取的所有item都會走遍所有的管道,需要在管道里面去判斷不同類型的item,如果在pipelines和items比較多的項目,將會讓項目變得非常臃腫
如有問題歡迎到我的博客留言
十三、參考鏈接官方文檔
中文教程
scrapy五大模塊
最后安利一下自己的博客:http://zhengbomo.github.com
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/38190.html
摘要:最近真是忙的吐血。。。上篇寫的是直接在爬蟲中使用,這樣不是很好,下使用才是正經方法。 最近真是忙的吐血。。。 上篇寫的是直接在爬蟲中使用mongodb,這樣不是很好,scrapy下使用item才是正經方法。在item中定義需要保存的內容,然后在pipeline處理item,爬蟲流程就成了這樣: 抓取 --> 按item規則收集需要數據 -->使用pipeline處理(存儲等) 定義it...
摘要:運行一下我們的代碼,然后刷新下數據庫,可看到數據已經保存到中了查看數據庫內容很清晰,每一項都有保存 抓取論壇、貼吧這種多分頁的信息時,沒接觸scrapy之前,是前確定有多少頁,使用for循環抓取。這方法略顯笨重,使用scrapy則可以直接組合下一頁的鏈接,然后傳給request持續進行抓取,一直到沒有下一頁鏈接為止。 還是以官方教程的網站為例子,先分析下元素: showImg(http...
摘要:時間永遠都過得那么快,一晃從年注冊,到現在已經過去了年那些被我藏在收藏夾吃灰的文章,已經太多了,是時候把他們整理一下了。那是因為收藏夾太亂,橡皮擦給設置私密了,不收拾不好看呀。 ...
摘要:其他筆記還有一些其他的主流云筆記軟件,像印象筆記,為知筆記,等,由于沒有多做嘗試,就不加以評價年最好用的云筆記軟件推薦介紹是一種可以使用普通文本編輯器編寫的標記語言,通過簡單的標記語法,它可以使普通文本內容具有一定的格式。 目錄: [TOC] Git Git介紹: Git是目前世界上最先進的分布式版本控制系統(沒有之一),簡單來說,Git 是一個管理你的「代碼的歷史記錄」的工具。 Gi...
閱讀 932·2021-11-16 11:45
閱讀 2137·2021-10-09 09:44
閱讀 1354·2019-08-30 14:03
閱讀 1138·2019-08-26 18:28
閱讀 3341·2019-08-26 13:50
閱讀 1729·2019-08-23 18:38
閱讀 3460·2019-08-23 18:22
閱讀 3608·2019-08-23 15:27