![]() |
VOOZH | about |
在上一篇中,我們實現了一個基本網絡爬蟲,它可以從StackOverflow上下載最新的問題,並將它們存儲在MongoDB資料庫中。在本文中,我們將對其擴展,使它能夠爬取每個網頁底部的分頁連結,並從每一頁中下載問題(包含問題標題和URL)。
在你開始任何爬取工作之前,檢查目標網站的使用條款並遵守robots.txt文件。同時,做爬取練習時遵守道德,不要在短時間內向某個網站發起大量請求。像對待自己的網站一樣對待任何你將爬取的網站。
開始有兩種可能的方法來接著從上次我們停下的地方繼續進行。
第一個方法是,擴展我們現有的網絡爬蟲,通過利用一個xpath表達式從」parse_item」方法裡的響應中提取每個下一頁連結,並通過回調同一個parse_item方法產生一個請求對象。利用這種方法,爬蟲會自動生成針對我們指定的連結的新請求,你可以在Scrapy文檔這裡找到更多有關該方法的信息。
另一個更簡單的方法是,使用一個不同類型的爬蟲—CrawlSpider(連結)。這是基本Spider的一個擴展版本,它剛好滿足我們的要求。
CrawlSpider我們將使用與上一篇教程中相同的爬蟲項目,所以如果你需要的話可以從repo上獲取這些代碼。
在「stack」目錄中,首先由crawl模板生成爬蟲樣板。
$ scrapy genspider stack_crawler stackoverflow.com -t crawl├── scrapy.cfg└── stack├── __init__.py├── items.py├── pipelines.py├── settings.py└── spiders├── __init__.py├── stack_crawler.py└── stack_spider.pystack_crawler.py文件內容如下:
fromscrapy.contrib.linkextractorsimportLinkExtractorfromscrapy.contrib.spidersimportCrawlSpider, Rulefromstack.itemsimportStackItemStackCrawlerSpider(CrawlSpider):
allowed_domains=['stackoverflow.com']start_urls=['http://www.stackoverflow.com/']Rule(LinkExtractor(allow=r'Items/'), callback='parse_item',
follow=True),, response):
i=StackItem#i['domain_id'] = response.xpath('//input[@id="sid"]/@value').extract#i['name'] = response.xpath('//div[@id="name"]').extract#i['description'] = response.xpath('//div[@id="description"]').extractreturni我們只需要對這個樣板做一些更新。
首先,添加問題的第一個頁面連結到start_urls列表:
|
1
2
3
|
start_urls=['http://stackoverflow.com/questions?pagesize=50&sort=newest'] |
接下來,我們需要添加一個正則表達式到「rules」屬性中,以此告訴爬蟲在哪裡可以找到下一個頁面連結:
rules=[Rule(LinkExtractor(allow=r'questions?page=[0-9]&sort=newest'),callback='parse_item', follow=True)]現在爬蟲能根據那些連結自動請求新的頁面,並將響應傳遞給「parse_item」方法,以此來提取問題和對應的標題。
如果你仔細查看的話,可以發現這個正則表達式限制了它只能爬取前9個網頁,因為在這個demo中,我們不想爬取所有的176234個網頁。
現在我們只需編寫如何使用xpath解析網頁,這一點我們已經在上一篇教程中實現過了,所以直接複製過來。
questions:
item=StackItemitem['url']=question.xpath('a[@class="question-hyperlink"]/@href').extract[0]item['title']=question.xpath('a[@class="question-hyperlink"]/text').extract[0]yielditem這就是為爬蟲提供的解析代碼,但是現在先不要啟動它。
我們需要通過在settings.py文件中設定一個下載延遲來善待StackOverflow(和任何其他網站)。
|
1
|
DOWNLOAD_DELAY=5 |
這告訴爬蟲需要在每兩個發出的新請求之間等待5秒鐘。你也很有必要做這樣的限制,因為如果你不這麼做的話,StackOverflow將會限制你的訪問流量,如果你繼續不加限制地爬取該網站,那麼你的IP將會被禁止。所有,友好點—要像對待自己的網站一樣對待任何你爬取的網站。
現在只剩下一件事要考慮—存儲數據。
MongoDB上次我們僅僅下載了50個問題,但是因為這次我們要爬取更多的數據,所有我們希望避免向資料庫中添加重複的問題。為了實現這一點,我們可以使用一個MongoDB的 upsert方法,它意味著如果一個問題已經存在資料庫中,我們將更新它的標題;否則我們將新問題插入資料庫中。
修改我們前面定義的MongoDBPipeline:
data:
raiseDropItem("Missing data!")self.collection.update({'url': item['url']},dict(item),
upsert=True)log.msg("Question added to MongoDB database!"level=log.DEBUG, spider=spider)returnitem為簡單起見,我們沒有優化查詢,也沒有處理索引值,因為這不是一個生產環境。
測試啟動爬蟲!
|
1
|
$ scrapy crawl questions |
現在你可以坐下來,看著你的資料庫漸漸充滿數據。
結論你可以從Github庫下載整個原始碼,也可以在下面評論或提問。