如何編寫一個Spider

本章以抓取 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ù)你辣。

  1. name:用于區(qū)別spider,一個項目下不能有相同名字的spider尘执;
  2. allowed_domain:可選的域名列表舍哄,其定義了我只能爬取的域名,如果沒有這個參數(shù)誊锭,scrapy將不會限制爬取的域名表悬;
  3. start_urls:包含了scrapy爬取的起始地址列表,后續(xù)的爬取URL將會從scrapy中獲取丧靡,后面會看到如果定義了start_requests函數(shù)蟆沫,將會覆蓋這個行為;
  4. 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.pyROBOTSTXT_OBEY = True改為ROBOTSTXT_OBEY = False纤掸。
運行命令:scrapy crawl quotes --nolog -o result.json,最終可以在目錄下看到生成的文件result.json浑塞。

爬取結果.png

從上面的代碼看到在第一步yield我們所需的數(shù)據(jù)之后借跪,去爬取了下一頁的鏈接,如果獲取到下一頁的鏈接缩举,會再yield一個Request對象垦梆,Request對象的callback還是parse()方法,因此會一直爬取跌倒知道該網(wǎng)站沒有下一頁為止仅孩。

總結

這一篇博客描述了一個spider文件是如何運行以及如何爬取數(shù)據(jù)托猩。下一篇博客將會講如何使用Scrapy.item來封裝爬取的數(shù)據(jù)。

?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末辽慕,一起剝皮案震驚了整個濱河市京腥,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌溅蛉,老刑警劉巖公浪,帶你破解...
    沈念sama閱讀 222,183評論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異船侧,居然都是意外死亡欠气,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,850評論 3 399
  • 文/潘曉璐 我一進店門镜撩,熙熙樓的掌柜王于貴愁眉苦臉地迎上來预柒,“玉大人,你說我怎么就攤上這事袁梗∫搜欤” “怎么了?”我有些...
    開封第一講書人閱讀 168,766評論 0 361
  • 文/不壞的土叔 我叫張陵遮怜,是天一觀的道長淋袖。 經(jīng)常有香客問我,道長锯梁,這世上最難降的妖魔是什么即碗? 我笑而不...
    開封第一講書人閱讀 59,854評論 1 299
  • 正文 為了忘掉前任,我火速辦了婚禮涝桅,結果婚禮上拜姿,老公的妹妹穿的比我還像新娘。我一直安慰自己冯遂,他們只是感情好,可當我...
    茶點故事閱讀 68,871評論 6 398
  • 文/花漫 我一把揭開白布谒获。 她就那樣靜靜地躺著蛤肌,像睡著了一般壁却。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上裸准,一...
    開封第一講書人閱讀 52,457評論 1 311
  • 那天展东,我揣著相機與錄音,去河邊找鬼炒俱。 笑死盐肃,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的权悟。 我是一名探鬼主播砸王,決...
    沈念sama閱讀 40,999評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼峦阁!你這毒婦竟也來了谦铃?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 39,914評論 0 277
  • 序言:老撾萬榮一對情侶失蹤榔昔,失蹤者是張志新(化名)和其女友劉穎驹闰,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體撒会,經(jīng)...
    沈念sama閱讀 46,465評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡嘹朗,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,543評論 3 342
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了诵肛。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片屹培。...
    茶點故事閱讀 40,675評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖曾掂,靈堂內(nèi)的尸體忽然破棺而出惫谤,到底是詐尸還是另有隱情,我是刑警寧澤珠洗,帶...
    沈念sama閱讀 36,354評論 5 351
  • 正文 年R本政府宣布溜歪,位于F島的核電站,受9級特大地震影響许蓖,放射性物質(zhì)發(fā)生泄漏蝴猪。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 42,029評論 3 335
  • 文/蒙蒙 一膊爪、第九天 我趴在偏房一處隱蔽的房頂上張望自阱。 院中可真熱鬧,春花似錦米酬、人聲如沸沛豌。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,514評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽加派。三九已至叫确,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間芍锦,已是汗流浹背竹勉。 一陣腳步聲響...
    開封第一講書人閱讀 33,616評論 1 274
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留娄琉,地道東北人次乓。 一個月前我還...
    沈念sama閱讀 49,091評論 3 378
  • 正文 我出身青樓,卻偏偏與公主長得像孽水,于是被迫代替她去往敵國和親票腰。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,685評論 2 360

推薦閱讀更多精彩內(nèi)容

  • scrapy學習筆記(有示例版) 我的博客 scrapy學習筆記1.使用scrapy1.1創(chuàng)建工程1.2創(chuàng)建爬蟲模...
    陳思煜閱讀 12,713評論 4 46
  • 1.Scrapy的命令行命令 創(chuàng)建一個Scrapy工程終端輸入: PyCharm 下直接運行 ScrapyScra...
    EnjoyWT閱讀 3,137評論 0 1
  • 江南莫之閱讀 278評論 0 3
  • 南瓜3閱讀 261評論 0 0
  • 12月27日精進匈棘,今日體驗:晚上開會總結了本月業(yè)績丧慈,還有目標,自己有了目標就不會盲目了主卫,加油干逃默!
    京心達田佳閱讀 160評論 0 0