Scrapy 框架的 Shell 工具使用
今天我們來(lái)介紹和實(shí)戰(zhàn) Scrapy 框架的命令行模式,它和 Django 框架的 shell 模式一樣,用于我們前期調(diào)試工程代碼,非常方便,掌握好 Scrapy 的 shell 模式會(huì)使得我們開(kāi)發(fā)爬蟲(chóng)更為順暢。
1. Scrapy Shell 介紹
Scrapy Shell 是一個(gè)交互終端,類(lèi)似于 Python 交互式模式,它使我們可以在未啟動(dòng) Scrapy 爬蟲(chóng)的情況下調(diào)試爬蟲(chóng)代碼。
在 Scrapy 的交互模式下,我們可以直接獲取網(wǎng)頁(yè)的 Response 結(jié)果,然后使用 XPath 或 CSS 表達(dá)式來(lái)獲取網(wǎng)頁(yè)元素,并以此測(cè)試我們獲取網(wǎng)頁(yè)數(shù)據(jù)的 Xpath 或者 CSS 表達(dá)式,確保后續(xù)執(zhí)行時(shí)能正確得到數(shù)據(jù)。我們來(lái)看看如何進(jìn)入 shell 模式,參考如下的視頻:
在 Scrapy 框架中內(nèi)置了 Selector 選擇器,這個(gè)選擇器是屬于 parsel 模塊的,而 parsel 模塊是由 Scrapy 團(tuán)隊(duì)為解析網(wǎng)頁(yè)而開(kāi)發(fā)的,并且獨(dú)立出來(lái)形成了一個(gè)第三方模塊。
這樣我們可以在自己的爬蟲(chóng)程序中使用 parsel 模塊而不必基于 Scrapy 框架 。我們從源碼中來(lái)看看這個(gè)選擇器類(lèi)的定義??梢钥吹?Selector 類(lèi)有我們熟悉的 xpath()
、css()
、re()
以及 extract()
等方法,這些是我們解析網(wǎng)頁(yè)的基礎(chǔ)。
我們來(lái)思考一個(gè)問(wèn)題,然后去源碼中找到答案:
(scrapy-test) [root@server ~]# scrapy shell https://gz.lianjia.com/ershoufang/ --nolog
...
>>> type(response)
<class 'scrapy.http.response.html.HtmlResponse'>
我們?cè)?Scrapy Shell 中可以看到 response 是 HtmlResponse 的一個(gè)實(shí)例,它是怎么會(huì)有 Selector 的方法的?我們?cè)谇懊娴?Scrapy 初步的實(shí)例中看到過(guò) response.xpath()
這樣的用法,源碼里面是怎么做的呢?
這個(gè)問(wèn)題比較簡(jiǎn)單,我們翻看一下源碼就可以找到答案了。首先查看 HtmlResponse
類(lèi)定義:
# 源碼位置:scrapy/http/response/html.py
from scrapy.http.response.text import TextResponse
class HtmlResponse(TextResponse):
pass
這個(gè)夠不夠簡(jiǎn)單?繼續(xù)追看 TextResponse
的定義:
# 源碼位置:scrapy/http/response/text.py
# ...
class TextResponse(Response):
# ...
@property
def selector(self):
from scrapy.selector import Selector
if self._cached_selector is None:
self._cached_selector = Selector(self)
return self._cached_selector
# ...
def xpath(self, query, **kwargs):
return self.selector.xpath(query, **kwargs)
def css(self, query):
return self.selector.css(query)
# ...
是不是一下子就明白了?response.xpath()
正是調(diào)用的 scrapy.selector
下的Selector
,繼續(xù)看這個(gè) Selector
的定義:
# 源碼位置:scrapy/selector/unified.py
from parsel import Selector as _ParselSelector
class Selector(_ParselSelector, object_ref):
# ...
是不是最后又到了 parsel 下的 Selector?所以使用 response.xpath()
等價(jià)于使用 parsel 模塊 下的 Selector 類(lèi)中的 xpath 方法去定位網(wǎng)頁(yè)元素。在給大家留一個(gè)更進(jìn)一步的問(wèn)題:
在前面爬取互動(dòng)出版網(wǎng)的 Scrapy 框架實(shí)例中,我們還用到了這樣的表達(dá)式:
book.xpath().extract()[0]
和book.xpath().extract_first()
,這樣的代碼執(zhí)行過(guò)程又是怎樣的呢(追蹤到 parsel 這一層即可)?
這個(gè)問(wèn)題追蹤的代碼會(huì)比上面多一點(diǎn),但也不復(fù)雜,這個(gè)問(wèn)題會(huì)在下一節(jié)的 Reponse 類(lèi)分析中給出相應(yīng)的回答。
2. Scrapy Shell 實(shí)戰(zhàn)
上面介紹了一些 Scrapy Shell 和 Response 的基礎(chǔ)知識(shí),我們現(xiàn)在就來(lái)在 Scrapy Shell 中實(shí)戰(zhàn) Selector 選擇器。本次測(cè)試的網(wǎng)站為廣州鏈家,測(cè)試頁(yè)面為二手房頁(yè)面:
我已經(jīng)在上面標(biāo)出了想要爬取的網(wǎng)頁(yè)信息,后面也主要測(cè)試這些數(shù)據(jù)的 xpath 表達(dá)式 或者 css 表達(dá)式。首先使用 scrapy shell 目標(biāo)網(wǎng)址
命令進(jìn)行想要的命令行,此時(shí) Scrapy 框架已經(jīng)為我們將目標(biāo)網(wǎng)站的網(wǎng)頁(yè)數(shù)據(jù)爬取了下來(lái):
(scrapy-test) [root@server ~]# scrapy shell https://gz.lianjia.com/ershoufang/
...
>>> response
<200 https://gz.lianjia.com/ershoufang/>
我們看到響應(yīng)的網(wǎng)頁(yè)數(shù)據(jù)已經(jīng)有了,接下來(lái)我們就可以開(kāi)始進(jìn)行網(wǎng)頁(yè)分析來(lái)抓取圖片中標(biāo)記的數(shù)據(jù)了。首先是標(biāo)題信息:
根據(jù)上面的網(wǎng)頁(yè)結(jié)構(gòu),可以很快得到標(biāo)題的 xpath 路徑表達(dá)式:
標(biāo)題://ul[@class="sellListContent"]/li/div/div[@class="title"]/a/text()
在 Scrapy Shell 中我們實(shí)戰(zhàn)一把:
>>> response.xpath('//ul[@class="sellListContent"]/li/div/div[@class="title"]/a/text()')
[<Selector xpath='//ul[@class="sellListContent"]/li/div/div[@class="title"]/a/text()' data='地鐵口 總價(jià)低 精裝實(shí)用小兩房'>, <Selector xpath='//ul[@class="sellListContent"]/li/div/div[@class="title"]/a/text()' data='剛需精裝小三房/三房?jī)蓮d一廚一衛(wèi)/廣州東綠湖國(guó)際城'>, <Selector xpath='//ul[@class="sellListContent"]/li/div/div[@class="title"]/a/text()' data='周門(mén)社區(qū) 綠雅苑六樓 精裝三房'>, <Selector xpath='//ul[@class="sellListContent"]/li/div/div[@class="title"]/a/text()' data='金碧領(lǐng)秀國(guó)際 精裝修一房 中樓層采光好'>, <Selector xpath='//ul[@class="sellListContent"]/li/div/div[@class="title"]/a/text()' data='戶型方正 采光好 通風(fēng)透氣 小區(qū)安靜'>, <Selector xpath='//ul[@class="sellListContent"]/li/div/div[@class="title"]/a/text()' data='毛紡小區(qū) 南向兩房 方正實(shí)用 采光好'>, <Selector xpath='//ul[@class="sellListContent"]/li/div/div[@class="title"]/a/text()' data='南奧疊層復(fù)式 前后樓距開(kāi)闊 南北對(duì)流通風(fēng)好'>, <Selector xpath='//ul[@class="sellListContent"]/li/div/div[@class="title"]/a/text()' data='丹桂園 實(shí)用三房精裝修 南向戶型 擰包入住'>, <Selector xpath='//ul[@class="sellListContent"]/li/div/div[@class="title"]/a/text()' data='周門(mén)小區(qū)大院管理 近地鐵總價(jià)低全明正規(guī)一房一廳'>, <Selector xpath='//ul[@class="sellListContent"]/li/div/div[@class="title"]/a/text()' data='云鶴北街 精裝低樓層 南向兩房'>, <Selector xpath='//ul[@class="sellListContent"]/li/div/div[@class="title"]/a/text()' data='中海譽(yù)城東南向兩房,住家安靜,無(wú)抵押交易快'>, <Selector xpath='//ul[@class="sellListContent"]/li/div/div[@class="title"]/a/text()' data='精裝兩房,步梯中層,總價(jià)低,交通方便,配套齊全'>, <Selector xpath='//ul[@class="sellListContent"]/li/div/div[@class="title"]/a/text()' data='中層南向四房 格局方正 樓層適中'>, <Selector xpath='//ul[@class="sellListContent"]/li/div/div[@class="title"]/a/text()' data='精裝修 戶型好 中空一房 采光保養(yǎng)很好'>, <Selector xpath='//ul[@class="sellListContent"]/li/div/div[@class="title"]/a/text()' data='業(yè)主急售,價(jià)格優(yōu)質(zhì)看房方便有密碼'>, <Selector xpath='//ul[@class="sellListContent"]/li/div/div[@class="title"]/a/text()' data='匯僑新城北區(qū) 精裝三房 看花園 戶型靚'>, <Selector xpath='//ul[@class="sellListContent"]/li/div/div[@class="title"]/a/text()' data='小區(qū)中間,安靜,前無(wú)遮擋,視野寬闊,望別墅花園'>, <Selector xpath='//ul[@class="sellListContent"]/li/div/div[@class="title"]/a/text()' data='小區(qū)側(cè)邊位 通風(fēng)采光好 小區(qū)管理 裝修保養(yǎng)好'>, <Selector xpath='//ul[@class="sellListContent"]/li/div/div[@class="title"]/a/text()' data='萬(wàn)科三房 南北對(duì)流 中高層采光好'>, <Selector xpath='//ul[@class="sellListContent"]/li/div/div[@class="title"]/a/text()' data='南北對(duì)流,采光充足,配套設(shè)施完善,交通便利'>, <Selector xpath='//ul[@class="sellListContent"]/li/div/div[@class="title"]/a/text()' data='美力倚睛居3房南有精裝修拎包入住'>, <Selector xpath='//ul[@class="sellListContent"]/li/div/div[@class="title"]/a/text()' data='美心翠擁華庭二期 3室2廳 228萬(wàn)'>, <Selector xpath='//ul[@class="sellListContent"]/li/div/div[@class="title"]/a/text()' data='天河公園門(mén)口 交通便利 配套成熟'>, <Selector xpath='//ul[@class="sellListContent"]/li/div/div[@class="title"]/a/text()' data='保利香檳花園 高層視野好 保養(yǎng)很好 居家感好'>, <Selector xpath='//ul[@class="sellListContent"]/li/div/div[@class="title"]/a/text()' data='恒安大廈兩房,有公交,交通方便,價(jià)格方面可談'>, <Selector xpath='//ul[@class="sellListContent"]/li/div/div[@class="title"]/a/text()' data='此房是商品房,低層,南北對(duì)流,全明屋'>, <Selector xpath='//ul[@class="sellListContent"]/li/div/div[@class="title"]/a/text()' data='水蔭直街 原裝電梯戶型 方正三房格局'>, <Selector xpath='//ul[@class="sellListContent"]/li/div/div[@class="title"]/a/text()' data='嘉誠(chéng)國(guó)際公寓 可明火 正規(guī)一房一廳'>, <Selector xpath='//ul[@class="sellListContent"]/li/div/div[@class="title"]/a/text()' data='近地鐵兩房 均價(jià)低 業(yè)主自住 裝修保養(yǎng)好'>, <Selector xpath='//ul[@class="sellListContent"]/li/div/div[@class="title"]/a/text()' data='宏城匯 三房 南向 采光好 戶型方正 交通便利'>]
上面結(jié)果返回的是 SelectorList
類(lèi)型:
>>> data_list = response.xpath('//ul[@class="sellListContent"]/li/div/div[@class="title"]/a/text()')
>>> type(data_list)
<class 'scrapy.selector.unified.SelectorList'>
最后我們通過(guò)提取 Selector 的 root 屬性,只得到相應(yīng)的文本信息:
>>> data = [d.root for d in data_list]
>>> data
['地鐵口 總價(jià)低 精裝實(shí)用小兩房', '剛需精裝小三房/三房?jī)蓮d一廚一衛(wèi)/廣州東綠湖國(guó)際城', '周門(mén)社區(qū) 綠雅苑六樓 精裝三房', '金碧領(lǐng)秀國(guó)際 精裝修一房 中樓層采光好', '戶型方正 采光好 通風(fēng)透氣 小區(qū)安靜', '毛紡小區(qū) 南向兩房 方正實(shí)用 采光好', '南奧疊層復(fù)式 前后樓距開(kāi)闊 南北對(duì)流通風(fēng)好', '丹桂園 實(shí)用三房精裝修 南向戶型 擰包入住', '周門(mén)小區(qū)大院管理 近地鐵總價(jià)低全明正規(guī)一房一廳', '云鶴北街 精裝低樓層 南向兩房', '中海譽(yù)城東南向兩房,住家安靜,無(wú)抵押交易快', '精裝兩房,步梯中層,總價(jià)低,交通方便,配套齊全', '中層南向四房 格局方正 樓層適中', '精裝修 戶型好 中空一房 采光保養(yǎng)很好', '業(yè)主急售,價(jià)格優(yōu)質(zhì)看房方便有密碼', '匯僑新城北區(qū) 精裝三房 看花園 戶型靚', '小區(qū)中間,安靜,前無(wú)遮擋,視野寬闊,望別墅花園', '小區(qū)側(cè)邊位 通風(fēng)采光好 小區(qū)管理 裝修保養(yǎng)好', '萬(wàn)科三房 南北對(duì)流 中高層采光好', '南北對(duì)流,采光充足,配套設(shè)施完善,交通便利', '美力倚睛居3房南有精裝修拎包入住', '美心翠擁華庭二期 3室2廳 228萬(wàn)', '天河公園門(mén)口 交通便利 配套成熟', '保利香檳花園 高層視野好 保養(yǎng)很好 居家感好', '恒安大廈兩房,有公交,交通方便,價(jià)格方面可談', '此房是商品房,低層,南北對(duì)流,全明屋', '水蔭直街 原裝電梯戶型 方正三房格局', '嘉誠(chéng)國(guó)際公寓 可明火 正規(guī)一房一廳', '近地鐵兩房 均價(jià)低 業(yè)主自住 裝修保養(yǎng)好', '宏城匯 三房 南向 采光好 戶型方正 交通便利']
是不是非常簡(jiǎn)單就爬到了數(shù)據(jù)?另外,我們還可以使用 extract()[0]
或者 extract_first()
這樣的方式來(lái)提取結(jié)果列表中的第一個(gè)文本數(shù)據(jù):
>>> data = response.xpath('//ul[@class="sellListContent"]/li/div/div[@class="title"]/a/text()').extract()[0]
>>> data2 = response.xpath('//ul[@class="sellListContent"]/li/div/div[@class="title"]/a/text()').extract_first()
>>> data == data2
True
>>> data
'地鐵口 總價(jià)低 精裝實(shí)用小兩房'
接下來(lái),我們依次找出獲取二手房位置、房屋價(jià)格、房屋信息的 xpath 路徑表達(dá)式:
房屋位置:
//ul[@class="sellListContent"]/li/div/div[@class="food"]/div[@class="positionInfo"]/a[]/text()
房屋信息:
//ul[@class="sellListContent"]/li/div/div[@class="address"]/div[@class="houseInfo"]/text()
房屋價(jià)格:
//ul[@class="sellListContent"]/li/div/div[@class="priceInfo"]/div[@class="totalPrice"]/span/text()
有了這些之后,我們就可以依次提取出二手房的【標(biāo)題介紹】、【房屋位置】、【房屋信息】以及【房屋價(jià)格】這些信息。此外對(duì)于提取的【房屋信息】字段還要進(jìn)一步處理,分割成【房屋結(jié)構(gòu)】、【房屋大小】以及【朝向】等信息。這些信息將在 Spider 模塊中進(jìn)行提取,也就是我們前面互動(dòng)出版網(wǎng)爬蟲(chóng)的 ChinaPubCrawler.py
文件中的 ChinaPubCrawler
類(lèi)來(lái)解析。
最后我們?cè)诮榻B下 scrapy shell
命令的參數(shù):
(scrapy-test) [root@server ~]# scrapy shell --help
Usage
=====
scrapy shell [url|file]
Interactive console for scraping the given url or file. Use ./file.html syntax
or full path for local file.
Options
=======
--help, -h show this help message and exit
-c CODE evaluate the code in the shell, print the result and
exit
--spider=SPIDER use this spider
--no-redirect do not handle HTTP 3xx status codes and print response
as-is
Global Options
--------------
--logfile=FILE log file. if omitted stderr will be used
--loglevel=LEVEL, -L LEVEL
log level (default: DEBUG)
--nolog disable logging completely
--profile=FILE write python cProfile stats to FILE
--pidfile=FILE write process ID to FILE
--set=NAME=VALUE, -s NAME=VALUE
set/override setting (may be repeated)
--pdb enable pdb on failure
比較常用的有 --no-redirect
和 -s
選項(xiàng):
--no-redirect
: 指的是不處理重定向,直接按照原始響應(yīng)返回即可;-s
:替換settings.py
中的配置。常用的有設(shè)置USER_AGENT
等。
3. 小結(jié)
本小節(jié)中我們簡(jiǎn)單介紹了 Scrapy 的 Shell 模式以及 Response 類(lèi)的一些原理。緊接著我們開(kāi)始了 Scrapy Shell 實(shí)戰(zhàn),以廣州鏈家網(wǎng)為例,測(cè)試在 Shell 模式下使用 Selector 選擇器來(lái)解析網(wǎng)頁(yè)數(shù)據(jù),提取房屋標(biāo)題、位置、價(jià)格等數(shù)據(jù)。下面一節(jié)我們會(huì)深入分析 Scrapy 框架中的 Request 和 Response ,包括解答今天的一個(gè)源碼追蹤作業(yè)。