Scrapy一個開源和協做的框架,其最初是爲了頁面抓取 (更確切來講, 網絡抓取 )所設計的,使用它能夠以快速、簡單、可擴展的方式從網站中提取所需的數據。但目前Scrapy的用途十分普遍,可用於如數據挖掘、監測和自動化測試等領域,也能夠應用在獲取API所返回的數據(例如 Amazon Associates Web Services ) 或者通用的網絡爬蟲。html
Scrapy 是基於twisted框架開發而來,twisted是一個流行的事件驅動的python網絡框架。所以Scrapy使用了一種非阻塞(又名異步)的代碼來實現併發。總體架構大體以下python
1 #Windows平臺 2 一、pip3 install wheel #安裝後,便支持經過wheel文件安裝軟件,wheel文件官網:https://www.lfd.uci.edu/~gohlke/pythonlibs 3 3、pip3 install lxml 4 4、pip3 install pyopenssl 5 五、下載並安裝pywin32:https://sourceforge.net/projects/pywin32/files/pywin32/ 6 六、下載twisted的wheel文件:http://www.lfd.uci.edu/~gohlke/pythonlibs/#twisted 7 七、執行pip3 install 下載目錄\Twisted-17.9.0-cp36-cp36m-win_amd64.whl 8 8、pip3 install scrapy 9 10 #Linux平臺 11 一、pip3 install scrapy
1 # 1 幫助命令 2 scrapy -h 3 scrapy <command> -h 4 5 # 2 global命令 6 startproject # 建立項目 7 genspider # 建立爬蟲程序 8 settings # 若是是在項目目錄下,則獲得的是該項目的配置,不然是全局的配置 9 runspider # 運行一個獨立的python文件,沒必要建立項目 10 shell # scrapy shell url 地址,在交互調試,如選擇器規則正確與否 11 fetch # 獨立與線程單純地爬取一個頁面,能夠拿到請求頭 12 view # 下載完畢後直接彈出瀏覽器,以此能夠分辨出那些數據是ajax請求 13 version # scrapy vewsion 查看scrapy版本,scrapy version -v查看scrapy依賴庫的版本 14 15 # 3 項目命令(必須切換到項目文件下才能執行) 16 crawl # 運行爬蟲,必須建立項目,確保配置文件中ROBOTSTXT_OBEY = Fasle 17 check # 檢測項目中有無語法錯誤 18 list # 列出項目中所包含的爬蟲名 19 edit # 編輯器,用來編輯文件使用,通常不用 20 parse # scrapy parse url地址 -- callback 回調函數 # 以此能夠驗證咱們的回調函數是否正確 21 bench # scrapy bentch壓力測試 22 23 #3 官網連接 24 https://docs.scrapy.org/en/latest/topics/commands.html
1 #一、執行全局命令:請確保不在某個項目的目錄下,排除受該項目配置的影響 2 scrapy startproject MyProject 3 4 cd MyProject 5 scrapy genspider baidu www.baidu.com 6 7 scrapy settings --get XXX #若是切換到項目目錄下,看到的則是該項目的配置 8 9 scrapy runspider baidu.py 10 11 scrapy shell https://www.baidu.com 12 response 13 response.status 14 response.body 15 view(response) 16 17 scrapy view https://www.taobao.com #若是頁面顯示內容不全,不全的內容則是ajax請求實現的,以此快速定位問題 18 19 scrapy fetch --nolog --headers https://www.taobao.com 20 21 scrapy version #scrapy的版本 22 23 scrapy version -v #依賴庫的版本 24 25 26 #二、執行項目命令:切到項目目錄下 27 scrapy crawl baidu 28 scrapy check 29 scrapy list 30 scrapy parse http://quotes.toscrape.com/ --callback parse 31 scrapy bench
1 # 在項目目錄下新建:entrypoint.py 2 from scrapy.cmdline import execute 3 4 execute(['scrapy, 'crawl', 'xiaohuar'])
1 import sys, os 2 3 sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='gb18030')
1 # 1 scrapy.spiders.Spider # scrapy.Spider等同於scrapy.spiders.Spider 2 # 2 scrapy.spiders.CrawSpider 3 # 3 scrapy.spiders.XMLFeedSpider 4 # 4 scrapy.spiders.CSVFeedSpider 5 # 5 scrapy.spiders.SitemapSpider
1 from scrapy 2 from scrapy.spiders import Spider, CrawSpider, XMLFeedSpider, CSVFeedSpider, SitemapSpider 3 4 class AmazonSpider(scrapy.Spider): # 自定義類,繼承Spider提供的基類 5 # class scrapy.spiders.Spider 6 # 這是最簡單的spider類,任何其餘的spider類都須要繼承它(包含你本身定義的) 7 # 該類不提供任何特殊的功能,它僅提供了一個默認的start_requests方法,默認從start_urls中讀取url地址發送requests請求,而且默認parse做爲回調函數 8 name = 'amazon' 9 allowed_domains = ['www.amazon.cn'] 10 start_urls = ['http://www.amazon.cn/'] 11 12 # 自定製配置文件,會首先讀取這個配置文件 13 custom_settings = { 14 'BOT_NAME' : 'Egon_Spider_Amazon', 15 'REQUEST_HEADERS' : { 16 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8', 17 'Accept-Language': 'en', 18 } 19 } 20 21 def parse(self, response): 22 pass
1 # 一、name='amazon' 2 定義爬蟲名,scrapy會根據該值定位爬蟲程序 3 因此它必需要有且必須惟一(In Python 2 this must be ASCII only) 4 5 # 二、allowed_domains = ['www.amazon.cn'] 6 定義容許爬取得域名,若是offsiteMiddleware啓動(默認就啓動), 7 那麼不屬於該列表的域名及子域名都不容許爬取 8 若是爬去的網址爲:https://www.example.com/1.html,那麼就添加"example.com"到列表 9 10 # 三、start_urls = ['http://www.amazon.cn'] 11 若是沒有指定url,就從該列表中讀取url來生成第一個請求 12 13 # 四、custom_settings 14 值爲一個字典,定義一些配置信息,在運行爬蟲程序時,這些配置覆蓋項目級別的配置 15 因此custom_settings必須被定義成一個類屬性,因爲settings會在類實例化前被加載 16 17 # 五、settings 18 經過self.settings['配置項的名字']能夠訪問settings.py中的配置,若是定義了custom_settings仍是以本身的爲準 19 20 # 六、logger 21 日誌名默認爲spider的內容 22 self.logger.debug('==>%s' %self.settings['BOT_NAME']) 23 24 # 七、crawler 25 該屬性必須被定義到類方法from_crawler中 26 27 # 八、from_crawler(crealer, *args, **kwargs) 28 You probably won’t need to override this directly because the default implementation acts as a proxy to the __init__() method, calling it with the given arguments args and named arguments kwargs. 29 30 # 九、start_requests() 31 該方法用來發起第一個Requests請求,且必須返回一個可迭代的對象。它在爬蟲程序打開時就被Scrapy調用,Scrapy只調用它一次。 32 默認從start_urls裏取出每一個url來生成Rquest(url, dont_filter=True) 33 若是你想要改變起始爬取得Requests,你就須要覆蓋整個方法,例如你想要起始發送一個POST請求,以下: 34 class MySpider(scrapy.Spider): 35 name = 'myspider' 36 37 def start_requests(self): 38 return [scrapy.FormRequest("http://www.example.com/login", formdata={"user": "join", "pass": "secret"}, callback=self.logged_in)] 39 40 def logged_in(self, response): 41 # here you would extract links to follow and return Requests for each of them, with another callback 42 pass 43 44 # 十、parse(reqponse) 45 這個是默認的回調函數,全部的回調函數必須返回an iteranle of Request and/or dicts or Item objects. 46 47 # 十一、log(message[, level, component]) 48 Wrapper that sends a log message through the Spider's logger, kept for backwards compatibility. 49 For more information see Logging from Spiders 50 51 # 十二、closed(reason) 52 爬蟲程序結束時自動觸發
1 去重規則應該多個爬蟲共享,但凡一個爬蟲爬取了,其餘都不要爬了 2 3 # 方法一: 4 1、新增類屬性 5 visited = set() # 類屬性 6 7 2、回調函數parse方法內 8 def parse(self, response): 9 if rsponse.url in self.visited: 10 return None 11 ... 12 self.visited.add(response.url) 13 14 # 方法一改進:針對url可能過長,因此咱們存放url的hash值 15 def parse(self, response): 16 url = md5(response.request.url) 17 if url in self.visited: 18 return None 19 ... 20 self.visited.add(url) 21 22 23 # 方法二:Scrapy自帶去重功能 24 配置文件: 25 DUPEFILTER_CLASS = 'scrapy.dupefilter.RFPDupeFilter' # 默認的去重規則,組重規則在內存中 26 DUPEFILTER_DEBUG = False 27 JOBDIR = '保存範文記錄的日誌路徑,如:/root/' # 最終路徑爲/root/requests.seen,去重規則放文件中 28 29 scrapy自帶去重規則默認爲RFPDupeFilter,只須要咱們指定 30 Request(..., dont_filter=False), 若是dont_filter=True則告訴scrapy這個rul不參與去重 31 32 33 # 方法三:咱們能夠仿照RFPDupeFilter自定義去重規則 34 from scrapy.dupefilter import RFPDupeFilter # 看源碼,仿照BaseDupeFilter 35 # 步驟1:在項目目錄下自定義去重文件dup.py 36 class UrlFilter(object): 37 def __init__(self): 38 self.visited = set() # 或者放到數據庫 39 40 @classmethod 41 def from_settings(cls, settings): 42 return cls() 43 def request_seen(self, request): 44 if request.url in self.visited: 45 return True 46 self.visited.add(request.url) 47 48 def open(self): # can return deferred 49 pass 50 51 def close(self, reason): # can return a deferred 52 pass 53 54 def log(self, request, spider): # log that a request has been filtered 55 pass 56 57 # 步驟二:配置文件settings.py 58 DUPEFILTER_CLASS = "項目名.dup.UrlFilter" 59 60 # 源碼分析 61 from scrapy.core.scheduler import Scheduler 62 # 見Scheduler下的enqueue_request方法:self.df.request_seen(request)
1 import scrapy 2 3 class MySpider(scrapy.Spider): 4 name = 'example.com' 5 allowed_domains = ['example.com'] 6 start_urls = [ 7 ‘http://www.example.com/1.html', 8 'http://www.example.com/2.html', 9 ] 10 11 def parse(self, response): 12 self.logger.info('A response from %s just arrived!', response.url) 13 14 # 實例二 15 import scrapy 16 17 class MySpider(scrapy.Spider): 18 name = 'example.com' 19 allowed_domains = ['example.com'] 20 start_urls = [ 21 ‘http://www.example.com/1.html', 22 'http://www.example.com/2.html', 23 ] 24 25 def parse(self, response): 26 for h2 in response.xpath('//h2').extract(): 27 yield {'title':h2} 28 29 for url in response.xpath('//a/@href').extract(): 30 yield scrapy.Request(url, callback=self.parse) 31 32 33 # 例三:在start_requests()內直接指定起始爬取得urls,start_urls就沒有用了 34 import scrapy 35 from myproject.items import MyItem 36 37 class MySpider(scrapy.Spider): 38 name = 'example.com' 39 allowed_domains = ['example.com'] 40 41 def start_requests(self): 42 yield scrapy.Request('http://www.example.com/1.html', self.parse) 43 yield scrapy.Request('http://www.example.com/2.html', self.parse) 44 yield scrapy.Request('httl://www.example.com/3.html', self.parse) 45 46 def parse(self, response): 47 for h3 in response.xpath('//h3').extract(): 48 yield MyItem(title=h3) 49 50 for url in response.xpath('//a/@href').extract(): 51 yield scrapy.Request(url, callback=self.parse)
1 # 咱們可能須要在命令行尾爬蟲程序傳遞參數,好比傳遞初始的url 2 # 執行命令行 3 scrapy crawl myspider -a category=electronics 4 5 # 在__init__方法中能夠接收外部傳進來的參數 6 import scrapy 7 8 class MySpider(scrapy.Spider): 9 name = 'myspider' 10 11 def __init__(self, category=None, *args, **kwargs): 12 super(MySpider, self).__init__(*args, **kwargs) 13 self.start_urls = ['http://www.example.com/categories/%s' % category] 14 # 主要接收的參數全都是字符串,若是想要結構化數據,你須要用相似json.loads的方法