注解
本教程所使用的樣例站 Google Directory 已經(jīng)被 Google 關(guān)閉了。不過教程中的概念任然適用。 如果您打算使用一個新的網(wǎng)站來更新本教程,您的貢獻(xiàn)是再歡迎不過了。 詳細(xì)信息請參考 Contributing to Scrapy。
本文檔介紹了如何適用 Firebug(一個 Firefox 的插件)來使得爬取更為簡單,有趣。更多有意思的 Firefox 插件請參考對爬取有幫助的實(shí)用 Firefox 插件。使用 Firefox 插件檢查頁面需要有些注意事項(xiàng):在瀏覽器中檢查 DOM 的注意事項(xiàng)。
在本樣例中將展現(xiàn)如何使用 Firebug 從 Google Directory 來爬取數(shù)據(jù)。Google Directory 包含了入門教程里所使用的 Open Directory Project 中一樣的數(shù)據(jù),不過有著不同的結(jié)構(gòu)。
Firebug 提供了非常實(shí)用的檢查元素功能。該功能允許您將鼠標(biāo)懸浮在不同的頁面元素上, 顯示相應(yīng)元素的 HTML 代碼。否則,您只能十分痛苦的在 HTML 的 body 中手動搜索標(biāo)簽。
在下列截圖中,您將看到 檢查元素 的執(zhí)行效果。
http://wiki.jikexueyuan.com/project/scrapy/images/1.png" alt="" />
首先我們能看到目錄根據(jù)種類進(jìn)行分類的同時(shí),還劃分了子類。
不過,看起來子類還有更多的子類,而不僅僅是頁面顯示的這些,所以我們接著查找:
http://wiki.jikexueyuan.com/project/scrapy/images/2.png" alt="" />
正如路徑的概念那樣,子類包含了其他子類的鏈接,同時(shí)也鏈接到實(shí)際的網(wǎng)站中。
查看路徑的 URL,我們可以看到 URL 的通用模式(pattern):
http://directory.google.com/Category/Subcategory/Another_Subcategory
了解到這個消息,我們可以構(gòu)建一個跟進(jìn)的鏈接的正則表達(dá)式:
directory\.google\.com/[A-Z][a-zA-Z_/]+$
因此,根據(jù)這個表達(dá)式,我們創(chuàng)建第一個爬取規(guī)則:
Rule(LinkExtractor(allow='directory.google.com/[A-Z][a-zA-Z_/]+$', ),
'parse_category',
follow=True,
),
Rule 對象指導(dǎo)基于 CrawlSpider 的 spider 如何跟進(jìn)目錄鏈接。 parse_category 是 spider 的方法,用于從頁面中處理也提取數(shù)據(jù)。
spider 的代碼如下:
from scrapy.contrib.linkextractors import LinkExtractor
from scrapy.contrib.spiders import CrawlSpider, Rule
class GoogleDirectorySpider(CrawlSpider):
name = 'directory.google.com'
allowed_domains = ['directory.google.com']
start_urls = ['http://directory.google.com/']
rules = (
Rule(LinkExtractor(allow='directory\.google\.com/[A-Z][a-zA-Z_/]+$'),
'parse_category', follow=True,
),
)
def parse_category(self, response):
# write the category page data extraction code here
pass
現(xiàn)在我們來編寫提取數(shù)據(jù)的代碼。
在 Firebug 的幫助下,我們將查看一些包含網(wǎng)站鏈接的網(wǎng)頁(以 http://directory.google.com/Top/Arts/Awards/為例),找到使用 Selectors 提取鏈接的方法。我們也將使用 Scrapy shell 來測試得到的 XPath 表達(dá)式,確保表達(dá)式工作符合預(yù)期。
http://wiki.jikexueyuan.com/project/scrapy/images/3.png" alt="" />
正如您所看到的那樣,頁面的標(biāo)記并不是十分明顯: 元素并不包含 id,class 或任何可以區(qū)分的屬性。所以我們將使用等級槽(rank bar)作為指示點(diǎn)來選擇提取的數(shù)據(jù),創(chuàng)建 XPath。
使用 Firebug,我們可以看到每個鏈接都在 td 標(biāo)簽中。該標(biāo)簽存在于同時(shí)(在另一個 td)包含鏈接的等級槽(ranking bar)的 tr 中。
所以我們選擇等級槽(ranking bar),接著找到其父節(jié)點(diǎn)(tr),最后是(包含我們要爬取數(shù)據(jù)的)鏈接的 td 。
對應(yīng)的 XPath:
//td[descendant::a[contains(@href, "#pagerank")]]/following-sibling::td//a
使用 Scrapy 終端來測試這些復(fù)雜的 XPath 表達(dá)式,確保其工作符合預(yù)期。
簡單來說,該表達(dá)式會查找等級槽的 td 元素,接著選擇所有 td 元素,該元素?fù)碛凶訉O a 元素,且 a 元素的屬性 href 包含字符串 #pagerank。
當(dāng)然,這不是唯一的 XPath,也許也不是選擇數(shù)據(jù)的最簡單的那個。 其他的方法也可能是,例如,選擇灰色的鏈接的 font 標(biāo)簽。
最終,我們編寫 parse_category()方法:
def parse_category(self, response):
# The path to website links in directory page
links = response.xpath('//td[descendant::a[contains(@href, "#pagerank")]]/following-sibling::td/font')
for link in links:
item = DirectoryItem()
item['name'] = link.xpath('a/text()').extract()
item['url'] = link.xpath('a/@href').extract()
item['description'] = link.xpath('font[2]/text()').extract()
yield item
注意,您可能會遇到有些在 Firebug 找到,但是在原始 HTML 中找不到的元素, 例如典型的 <tbody>元素, 或者 Firebug 檢查活動 DOM(live DOM)所看到的元素,但元素由 javascript 動態(tài)生成,并不在 HTML 源碼中。 (原文語句亂了,上面為意譯- -: or tags which Therefer in page HTML sources may on Firebug inspects the live DOM )。