scrapy爬蟲框架教程(二)-- 爬取豆瓣電影TOP250


scrapy爬蟲框架教程(二)-- 爬取豆瓣電影TOP250

前言

經過上一篇教程我們已經大致了解了Scrapy的基本情況,並寫了一個簡單的小demo。這次我會以爬取豆瓣電影TOP250為例進一步為大家講解一個完整爬蟲的流程。

工具和環境

  1. 語言:python 2.7
  2. IDE: Pycharm
  3. 瀏覽器:Chrome
  4. 爬蟲框架:Scrapy 1.2.1

教程正文

觀察頁面結構

首先我們打開豆瓣電影TOP250的頁面


通過觀察頁面決定讓我們的爬蟲獲取每一部電影的排名、電影名稱、評分和評分的人數。

聲明Item

什么是Items呢?官方文檔Items定義如下:

Items

爬取的主要目標就是從非結構性的數據源提取結構性數據,例如網頁。 Scrapy spider可以以python的dict來返回提取的數據.雖然dict很方便,並且用起來也熟悉,但是其缺少結構性,容易打錯字段的名字或者返回不一致的數據,尤其在具有多個spider的大項目中。

為了定義常用的輸出數據,Scrapy提供了 Item 類。 Item 對象是種簡單的容器,保存了爬取到得數據。 其提供了 類似於詞典(dictionary-like) 的API以及用於聲明可用字段的簡單語法。

許多Scrapy組件使用了Item提供的額外信息: exporter根據Item聲明的字段來導出數據、 序列化可以通過Item字段的元數據(metadata)來定義、 trackref 追蹤Item實例來幫助尋找內存泄露 (see 使用 trackref 調試內存泄露) 等等。

Item使用簡單的class定義語法以及Field對象來聲明。我們打開scrapyspider目錄下的items.py文件寫入下列代碼聲明Item:

import scrapy class DoubanMovieItem(scrapy.Item): # 排名 ranking = scrapy.Field() # 電影名稱 movie_name = scrapy.Field() # 評分 score = scrapy.Field() # 評論人數 score_num = scrapy.Field() 

爬蟲程序

在scrapyspider/spiders目錄下創建douban_spider.py文件,並寫入初步的代碼:

from scrapy.spiders import Spider from scrapyspider.items import DoubanMovieItem class DoubanMovieTop250Spider(Spider): name = 'douban_movie_top250' start_urls = ['https://movie.douban.com/top250'] def parse(self, response): item = DoubanMovieItem() 

這個一個基本的scrapy的spider的model,首先我們要導入Scrapy.spiders中的Spider類,以及scrapyspider.items中我們剛剛定義好的DoubanMovieItem。
接着創建我們自己的爬蟲類DoubanMovieTop250Spider並繼承Spider類,scrapy.spiders中有很多不同的爬蟲類可供我們繼承,一般情況下使用Spider類就可以滿足要求。(其他爬蟲類的使用可以去參考官方文檔)。

Spider

class scrapy.spider.Spider

Spider是最簡單的spider。每個其他的spider必須繼承自該類(包括Scrapy自帶的其他spider以及您自己編寫的spider)。 Spider並沒有提供什么特殊的功能。 其僅僅請求給定的 start_urls/start_requests ,並根據返回的結果(resulting responses)調用spider的 parse 方法。

name 定義spider名字的字符串(string)。spider的名字定義了Scrapy如何定位(並初始化)spider,所以其必須是唯一的。 不過您可以生成多個相同的spider實例(instance),這沒有任何限制。 name是spider最重要的屬性,而且是必須的。

如果該spider爬取單個網站(single domain),一個常見的做法是以該網站(domain)(加或不加 后綴 )來命名spider。 例如,如果spider爬取 mywebsite.com ,該spider通常會被命名為 mywebsite 。

allowed_domains 可選。包含了spider允許爬取的域名(domain)列表(list)。 當 OffsiteMiddleware 啟用時, 域名不在列表中的URL不會被跟進。

start_urls URL列表。當沒有制定特定的URL時,spider將從該列表中開始進行爬取。 因此,第一個被獲取到的頁面的URL將是該列表之一。 后續的URL將會從獲取到的數據中提取。

start_requests() 該方法必須返回一個可迭代對象(iterable)。該對象包含了spider用於爬取的第一個Request。

當spider啟動爬取並且未制定URL時,該方法被調用。 當指定了URL時,make_requests_from_url() 將被調用來創建Request對象。 該方法僅僅會被Scrapy調用一次,因此您可以將其實現為生成器。

該方法的默認實現是使用 start_urls 的url生成Request。

如果您想要修改最初爬取某個網站的Request對象,您可以重寫(override)該方法。 例如,如果您需要在啟動時以POST登錄某個網站,你可以這么寫:

def start_requests(self): return [scrapy.FormRequest("http://www.example.com/login", formdata={'user': 'john', 'pass': 'secret'}, callback=self.logged_in)] def logged_in(self, response): # here you would extract links to follow and return Requests for # each of them, with another callback pass 

make_requests_from_url(url) 該方法接受一個URL並返回用於爬取的 Request 對象。 該方法在初始化request時被 start_requests() 調用,也被用於轉化url為request。

默認未被復寫(overridden)的情況下,該方法返回的Request對象中, parse() 作為回調函數,dont_filter參數也被設置為開啟。 (詳情參見 Request).

parse(response) 當response沒有指定回調函數時,該方法是Scrapy處理下載的response的默認方法。

parse 負責處理response並返回處理的數據以及(/或)跟進的URL。 Spider 對其他的Request的回調函數也有相同的要求。

該方法及其他的Request回調函數必須返回一個包含 Request 及(或) Item 的可迭代的對象。

參數: response (Response) – 用於分析的response

log(message[, level, component]) 使用 scrapy.log.msg() 方法記錄(log)message。 log中自動帶上該spider的 name 屬性。 更多數據請參見 Logging 。

closed(reason) 當spider關閉時,該函數被調用。 該方法提供了一個替代調用signals.connect()來監聽 spider_closed 信號的快捷方式。

提取網頁信息

我們使用xpath語法來提取我們所需的信息。 不熟悉xpath語法的可以在W3School網站學習一下,很快就能上手。
首先我們在chrome瀏覽器里進入豆瓣電影TOP250頁面並按F12打開開發者工具。


點擊工具欄左上角的類鼠標符號圖標或者Ctrl + Shift + c在頁面中點擊我們想要的元素即可在工具欄中看到它在網頁HTML源碼中所處的位置。
一般抓取時會以先抓大再抓小的原則來抓取。通過觀察我們看到該頁面所有影片的信息都位於一個class屬性為grid_view的ol標簽內的li標簽內。

<ol class="grid_view"> <li> <div class="item"> <div class="pic"> <em class="">1</em> <a href="https://movie.douban.com/subject/1292052/"> <img alt="肖申克的救贖" src="https://img3.doubanio.com/view/movie_poster_cover/ipst/public/p480747492.jpg" class=""> </a> </div> <div class="info"> <div class="hd"> <a href="https://movie.douban.com/subject/1292052/" class=""> <span class="title">肖申克的救贖</span> <span class="title">&nbsp;/&nbsp;The Shawshank Redemption</span> <span class="other">&nbsp;/&nbsp;月黑高飛(港) / 刺激1995(台)</span> </a> <span class="playable">[可播放]</span> </div> <div class="bd"> <p class=""> 導演: 弗蘭克·德拉邦特 Frank Darabont&nbsp;&nbsp;&nbsp;主演: 蒂姆·羅賓斯 Tim Robbins /...<br> 1994&nbsp;/&nbsp;美國&nbsp;/&nbsp;犯罪 劇情 </p> <div class="star"> <span class="rating5-t"></span> <span class="rating_num" property="v:average">9.6</span> <span property="v:best" content="10.0"></span> <span>766719人評價</span> </div> <p class="quote"> <span class="inq">希望讓人自由。</span> </p> </div> </div> </div> </li> ... ... ... </ol> 

因此我們根據以上原則對所需信息進行抓取

from scrapy.spiders import Spider from scrapyspider.items import DoubanMovieItem class DoubanMovieTop250Spider(Spider): name = 'douban_movie_top250' start_urls = ['https://movie.douban.com/top250'] def parse(self, response): item = DoubanMovieItem() movies = response.xpath('//ol[@class="grid_view"]/li') for movie in movies: item['ranking'] = movie.xpath( './/div[@class="pic"]/em/text()').extract()[0] item['movie_name'] = movie.xpath( './/div[@class="hd"]/a/span[1]/text()').extract()[0] item['score'] = movie.xpath( './/div[@class="star"]/span[@class="rating_num"]/text()' ).extract()[0] item['score_num'] = movie.xpath( './/div[@class="star"]/span/text()').re(ur'(\d+)人評價')[0] yield item 

對於Scrapy提取頁面信息的內容詳情可以參照官方文檔的相應章節

運行爬蟲

在項目文件夾內打開cmd運行下列命令:

scrapy crawl douban_movie_top250 -o douban.csv

注意此處的douban_movie_top250即為我們剛剛寫的爬蟲的name, 而-o douban.csv是scrapy提供的將item輸出為csv格式的快捷方式

試着運行一下爬蟲怎么什么也沒輸出呢?!!!


辛辛苦苦到了這里難道要失敗了嗎?!!!
不要急我們看下一控制台輸出的信息,原來是403錯誤了。這是因為豆瓣對爬蟲設了一個小小的門檻,我們只需要更改一下發送請求時的請求頭user-agent即可。

from scrapy.spiders import Spider from scrapyspider.items import DoubanMovieItem class DoubanMovieTop250Spider(Spider): name = 'douban_movie_top250' headers = { 'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.143 Safari/537.36', } def start_requests(self): url = 'https://movie.douban.com/top250' yield Request(url, headers=self.headers) def parse(self, response): item = DoubanMovieItem() movies = response.xpath('//ol[@class="grid_view"]/li') for movie in movies: item['ranking'] = movie.xpath( './/div[@class="pic"]/em/text()').extract()[0] item['movie_name'] = movie.xpath( './/div[@class="hd"]/a/span[1]/text()').extract()[0] item['score'] = movie.xpath( './/div[@class="star"]/span[@class="rating_num"]/text()' ).extract()[0] item['score_num'] = movie.xpath( './/div[@class="star"]/span/text()').re(ur'(\d+)人評價')[0] yield item 

更改后的代碼是不是覺得有些地方不太一樣了?start_urls怎么不見了?start_requests函數又是干什么的?還記得剛才對Spider類的介紹嗎?先回過頭復習一下上面關於start_urls和start_requests函數的介紹。簡單的說就是使用start_requests函數我們對初始URL的處理就有了更多的權利,比如這次給初始URL增加請求頭user_agent。

再次運行爬蟲,我們想要的信息都被下載到douban.scv文件夾里了。直接用WPS打開即可查看信息。


自動翻頁

先別急着高興,你難道沒有發現一個問題嗎?這樣的話我們還是只能爬到當前頁的25個電影的內容。怎么樣才能把剩下的也一起爬下來呢?
實現自動翻頁一般有兩種方法:

  1. 在頁面中找到下一頁的地址;
  2. 自己根據URL的變化規律構造所有頁面地址。

一般情況下我們使用第一種方法,第二種方法適用於頁面的下一頁地址為JS加載的情況。今天我們只說第一種方法。
首先利用Chrome瀏覽器的開發者工具找到下一頁的地址

然后在解析該頁面時獲取下一頁的地址並將地址交給調度器(Scheduler)

from scrapy import Request from scrapy.spiders import Spider from scrapyspider.items import DoubanMovieItem class DoubanMovieTop250Spider(Spider): name = 'douban_movie_top250' headers = { 'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.143 Safari/537.36', } def start_requests(self): url = 'https://movie.douban.com/top250' yield Request(url, headers=self.headers) def parse(self, response): item = DoubanMovieItem() movies = response.xpath('//ol[@class="grid_view"]/li') for movie in movies: item['ranking'] = movie.xpath( './/div[@class="pic"]/em/text()').extract()[0] item['movie_name'] = movie.xpath( './/div[@class="hd"]/a/span[1]/text()').extract()[0] item['score'] = movie.xpath( './/div[@class="star"]/span[@class="rating_num"]/text()' ).extract()[0] item['score_num'] = movie.xpath( './/div[@class="star"]/span/text()').re(ur'(\d+)人評價')[0] yield item next_url = response.xpath('//span[@class="next"]/a/@href').extract() if next_url: next_url = 'https://movie.douban.com/top250' + next_url[0] yield Request(next_url, headers=self.headers) 

最后再運行一下爬蟲,打開douban.csv。是不是發現所有的影片信息都獲取到了,250個一個不多一個不少。


最后,利用WPS的篩選功能你可以篩選任意符合你要求的影片。(Ps:外來的和尚有時候不一定好念經。記得要用WPS打開這個CVS文件,用EXCEL打開會因為有中文而顯示不正常。)

結尾

從寫這個Scrapy爬蟲框架教程以來,我越來越覺得自己學會的東西再輸出出去沒有想象的那么簡單,往往寫了幾個小時的教程最后發現還是沒有想表達的東西表達完美。如果有什么說的不好的地方歡迎大家指正。聞道有先后,術業有專攻。大家互相學習: )


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM