本章以抓取 http://quotes.toscrape.com/ 為例悯许,講一下如何編寫一個簡單的spider
首先褒傅,我們要在項目目錄下用命令創(chuàng)建一個spider登失,命令scrapy genspider quotes quotes.toscrape.com
静尼,該命令會在spiders目錄下創(chuàng)建一個名為quotes.py
的文件莺褒,其內(nèi)容如下:
# -*- coding: utf-8 -*-
import scrapy
class QuotesSpider(scrapy.Spider):
name = 'quotes'
allowed_domains = ['quotes.toscrape.com']
start_urls = ['http://quotes.toscrape.com/']
def parse(self, response):
pass
scrapy幫我們定義了一個類掩缓,其繼承了scrapy.Spider
,在類中遵岩,幫我們定義了一些參數(shù)和函數(shù)你辣。
-
name
:用于區(qū)別spider,一個項目下不能有相同名字的spider尘执; -
allowed_domain
:可選的域名列表舍哄,其定義了我只能爬取的域名,如果沒有這個參數(shù)誊锭,scrapy將不會限制爬取的域名表悬; -
start_urls
:包含了scrapy爬取的起始地址列表,后續(xù)的爬取URL將會從scrapy中獲取丧靡,后面會看到如果定義了start_requests
函數(shù)蟆沫,將會覆蓋這個行為; -
parse
方法:這個是scrapy的默認回調(diào)函數(shù)温治,scrapy在下載完所爬取的頁面后饭庞,會生成一個Response
對象,然后回調(diào)parse
函數(shù)熬荆,將Response
作為parse
函數(shù)的參數(shù)舟山;該函數(shù)包含解析和處理頁面的邏輯,并返回獲取的數(shù)據(jù)(以item或dict形式返回)卤恳。
設置初始爬取點
除了上面所看到的 start_urls
參數(shù)來設置起始點累盗,還可以通過定義start_requests
函數(shù)類設置爬取的起始點,如下:
# -*- coding: utf-8 -*-
import scrapy
class QuotesSpider(scrapy.Spider):
name = 'quotes'
allowed_domains = ['quotes.toscrape.com']
# start_urls = ['http://quotes.toscrape.com/']
def start_requests(self):
url = "http://quotes.toscrape.com/"
yield scrapy.Request(url, callback = self.parse)
def parse(self, response):
print(response.body)
通過執(zhí)行scrapy crawl quotes --nolog > quotes.html
突琳,可以看到在本地生成了一個html文件若债,里面包含了http://quotes.toscrape.com/
中的內(nèi)容。
其實本今,通過查看scrapy的源碼拆座,在父類scrapy.Scrapy
實現(xiàn)了下面這一段代碼:
for url in self.start_urls:
yield Request(url, dont_filter=True)
因此主巍,如果你想重寫起始點爬取行為的話,可以實現(xiàn)自己的start_requests
方法挪凑,否則孕索,可以直接在start_urls
要爬取的起始地址即可。
Request和Response分別代表了HTTP的請求包和相應包躏碳,下面簡單描述下:
Request
scrapy.http.Request(url[, callback, method='GET', headers, body, cookies, meta, encoding='utf-8', priority=0, dont_filter=False, errback, flags])
url
:請求地址搞旭,必填;
callback
:頁面解析回調(diào)函數(shù)菇绵,默認調(diào)用Spider的parse方法肄渗;
method
:請求的方式,默認為GET
咬最;
headers
:請求頭部字典翎嫡,如果某項的值為None,表示不發(fā)送該頭部信息永乌;
body
:請求正文惑申;
cookies
:Cookies,通常為字典翅雏,也可以是字典的列表圈驼;
meta
:這個比較重要,這個參數(shù)通常用于對象之間傳遞信息望几,比如在Response中會保存相對應的Request對象的meta參數(shù)绩脆;
priority
:請求的優(yōu)先默認值;
dont_filter
:用來表示如果該地址之前請求過橄抹,本次是否過濾該請求靴迫;
errback
:請求異常或出現(xiàn)HTTP錯誤時的回調(diào)函數(shù)害碾。
Scrapy基于Response
提供了一些子類矢劲,如FormRequest
用來處理HTML FORM
。
Response
scrapy.http.Response(url[, status=200, headers=None, body=b'', flags=None, request=None])
url
:請求的地址慌随;
status
:HTTP相應狀態(tài)碼;
headers
:相應頭部躺同;
body
:相應正文阁猜,bytes類型;
request
:對應的Request對象蹋艺。
除了構造方法的參數(shù)剃袍,Response
還需要關注以下參數(shù)/方法:
meta
:來自Request.meta參數(shù);
css(query)
:使用CSS選擇器從Response.body中提取信息捎谨,實際是TextResponse.selector.css(query)
方法的快捷方式民效;
xpath(query)
:使用XPath選擇器從Response中提取信息憔维,實際是TextResponse.selector.xpath(query)
方法的快捷方式;
urljoin(url)
:用于將相對路徑轉化為絕對路徑畏邢。
使用Selector提取數(shù)據(jù)
知道頁面是如何獲取到之后业扒,接下來就是如何從頁面中獲取所需要的信息。Scrapy提供了兩種發(fā)生:CSS選擇器和Xpath選擇器(詳細用法可以參考網(wǎng)上相關資料舒萎,這邊只列舉常用方法)程储。
先來看看Selector:
scrapy.selector.Selector(response=None, text=None, type=None)
response
:可基于Response對象生成Selector對象;
text
:可基于文本生成Selector臂寝,優(yōu)先級 response > text章鲤;
type
:解析類型,html/xml咆贬,通常不用關心败徊。
Selector還提供了一下方法,下面簡要介紹一些常用的方法:
xpath(query)
:基于Xpath選擇器提取數(shù)據(jù)掏缎,返回一個SelectorList元素集嵌;
css(query)
:基于CSS選擇器提取數(shù)據(jù),返回一個SelectorList元素御毅;
extract()
:返回選中元素的Unicode字符串列表根欧;
re(regex)
:返回選中元素符合regex正則表達式的Unicode字符串列表;
再來看看SelectorList提供的方法:
xpath(query)
:基于Xpath選擇器對列表中的每個元素提取數(shù)據(jù)端蛆,所有結果會組成一個SelectList凤粗,并返回;
css(query)
:基于CSS選擇器對列表中的元素提取數(shù)據(jù)今豆,所有結果會組成一個SelectList嫌拣,并返回;
extract()
:對列表中所有元素的調(diào)用extract()
呆躲,所有結果會組成一個SelectList异逐,并返回;
re(regex)
:對列表中所有元素的調(diào)用re(regex)
插掂,所有結果會組成一個SelectList灰瞻,并返回;
extract_first()
:返回第一個元素的Unicode字符串列表辅甥;
re_first(regex)
:返回符合regex正則的第一個元素的Unicode字符串列表酝润;
下面簡單看看CSS選擇器和XPATH選擇器
CSS選擇器
CSS的用法可以看W3C
這邊增加一條額外用法:
選擇器 | 描述 | 例子 |
---|---|---|
element::text | 選擇element元素的文本 | p::text |
element::attr(attr_name) | 選擇element元素屬性為attr_name的值 | a::attr(href) |
示例:
>>> html_body = """
... <div class="quote" itemscope="" itemtype="http://schema.org/CreativeWork">
... <span class="text" itemprop="text">“The world as we have created it is a process of our thinking. It cannot be changed without changing our thinking.”</span>
... <span>by <small class="author" itemprop="author">Albert Einstein</small>
... <a href="/author/Albert-Einstein">(about)</a>
... </span>
... <div class="tags">
... Tags:
... <meta class="keywords" itemprop="keywords" content="change,deep-thoughts,thinking,world">
...
... <a class="tag" href="/tag/change/page/1/">change</a>
...
... <a class="tag" href="/tag/deep-thoughts/page/1/">deep-thoughts</a>
...
... <a class="tag" href="/tag/thinking/page/1/">thinking</a>
...
... <a class="tag" href="/tag/world/page/1/">world</a>
...
... </div>
... </div>
... """
>>> selector = scrapy.Selector(text = html_body)
>>> type(selector)
<class 'scrapy.selector.unified.Selector'>
selector_list = selector.css('div.quote div.tags a.tag::text') #這邊我們要提取的是文本直接a.tag::text也可以
>>> type(selector_list)
<class 'scrapy.selector.unified.SelectorList'>
>>> selector_list.extract_first() #提取第一個tag
'change'
>>> selector_list.extract() #提取所有tag
['change', 'deep-thoughts', 'thinking', 'world']
>>> selector_list.re_first(r'(^w\w+)') #利用正則提取
'world'
XPath選擇器
XPath的用法可以看W3C
這邊增加一條額外用法:
選擇器 | 描述 |
---|---|
text() | 選擇文本 |
//還是用上面的html_body
>>> selector_list = selector.xpath('//div[@class="quote"]/div[@class="tags"]/a[@class="tag"]/text()')
>>> selector_list.extract()
['change', 'deep-thoughts', 'thinking', 'world']
>>> selector_list.re_first(r'(^w\w+)')
'world'
>>> selector_list.extract_first()
'change'
爬取網(wǎng)站
通過結合Response、Request和Selector璃弄,就可以寫出簡單的爬蟲要销。下面是以 http://quotes.toscrape.com/
為例,爬取quote/author/tags并返回夏块。
#-*- coding: utf-8 -*-
#quotes.py
import scrapy
class QuotesSpider(scrapy.Spider):
name = 'quotes'
allowed_domains = ['quotes.toscrape.com']
start_urls = ['http://quotes.toscrape.com/']
# def start_requests(self):
# url = "http://quotes.toscrape.com/"
# yield scrapy.Request(url, callback = self.parse)
def parse(self, response):
quote_selector_list = response.css('body > div > div:nth-child(2) > div.col-md-8 div.quote')
for quote_selector in quote_selector_list:
quote = quote_selector.css('span.text::text').extract_first()
author = quote_selector.css('span small.author::text').extract_first()
tags = quote_selector.css('div.tags a.tag::text').extract()
yield {'quote':quote, 'author':author, 'tags':tags}
#爬取下一頁的鏈接
next_page_url = response.css('ul.pager li.next a::attr(href)').extract_first()
if next_page_url:
next_page_url = response.urljoin(next_page_url)
yield scrapy.Request(next_page_url, callback = self.parse)
http://quotes.toscrape.com/
開啟了Robots疏咐,所以我們要在爬蟲的配置文件中settings.py
將ROBOTSTXT_OBEY = True
改為ROBOTSTXT_OBEY = False
纤掸。
運行命令:scrapy crawl quotes --nolog -o result.json
,最終可以在目錄下看到生成的文件result.json
浑塞。
從上面的代碼看到在第一步yield
我們所需的數(shù)據(jù)之后借跪,去爬取了下一頁的鏈接,如果獲取到下一頁的鏈接缩举,會再yield
一個Request
對象垦梆,Request
對象的callback
還是parse()
方法,因此會一直爬取跌倒知道該網(wǎng)站沒有下一頁為止仅孩。
總結
這一篇博客描述了一個spider文件是如何運行以及如何爬取數(shù)據(jù)托猩。下一篇博客將會講如何使用Scrapy.item
來封裝爬取的數(shù)據(jù)。