scrapy爬取豆瓣電影,存儲在MongoDB
本節分享用的Scrapy爬取豆瓣電影Top250的實戰。
本節要實現的內容有:
- 爬取豆瓣電影Top250頁面的,全部字段
- 將抓取到的結果存儲到MongoDB。
實驗環境:
- PyCharm
- Python3.6
- Scrapy
- PyMongo
- MongoDB
創建項目
在你的工作目錄的文件夾下打開命令提示符窗口,輸入:
scrapy startproject dbmoive
創建爬蟲
cd dbmoive
scarpy genspider douban movie.douban.com/top250
如果正確創建,得到的目錄如下所示
- scrapy.cfg文件中主要包含的是項目的相關設置。
- dbmoive文件夾下是用於編寫爬蟲的目錄。
- items.py:定義我們所要爬取的信息的相關屬性。
- middlewares.py:爬蟲中間件,這裏可以用過自定義相關的方法,用來處理爬蟲的響應和請求。
- pipelines.py:當數據被爬蟲爬取下來後,它會被髮送到item pipelines中,每個item pipelines組件(有時稱爲“項目管道”)是一個實現簡單方法的Python類。他們收到一個項目並對其執行操作,還決定該項目是否應該繼續通過管道或被丟棄並且不再被處理。
- settings.py:項目的設置文件。
- douban.py: 項目中,爬蟲的主要邏輯代碼
禁止ROBOTSTXT_OBEY
接下來你需要打開settings.py文件,將ROBOTSTXT_OBEY修改爲False。
ROBOTSTXT_OBEY = False
它默認爲True,就是要遵守robots.txt 的規則,那麼 robots.txt 是個什麼東西呢?
通俗來說, robots.txt 是遵循 Robot 協議的一個文件,它保存在網站的服務器中,它的作用是,告訴搜索引擎爬蟲,本網站哪些目錄下的網頁 不希望 你進行爬取收錄。在Scrapy啓動後,會在第一時間訪問網站的 robots.txt 文件,然後決定該網站的爬取範圍。
當然,我們並不是在做搜索引擎,而且在某些情況下我們想要獲取的內容恰恰是被 robots.txt 所禁止訪問的。所以,某些時候,我們就要將此配置項設置爲 False ,拒絕遵守 Robot協議 !
嘗試最初的爬取
接下來我們什麼代碼也不修改,執行爬取,運行如下命令:
scrapy crawl douban
你會發現爬取結果會出現這樣的一個錯誤:
500 Internal Server Error
訪問知乎得到的狀態碼是500,這說明爬取並沒有成功,其實這是因爲我們沒有加入請求頭,知乎識別User-Agent發現不是瀏覽器,就返回錯誤的響應了。
所以接下來的一步我們需要加入請求headers信息,你可以在Request的參數里加,也可以在spider裏面的custom_settings裏面加,當然最簡單的方法莫過於在全局settings裏面加了。
我們打開settings.py文件,取消DEFAULT_REQUEST_HEADERS的註釋,加入如下的內容:
所以在這裏設置爲False。當然可能本次爬取不一定會被它限制,但是我們一般來說會首先選擇禁止它。
所以接下來的一步我們需要加入請求headers信息,你可以在Request的參數里加,也可以在spider裏面的custom_settings裏面加,當然最簡單的方法莫過於在全局settings裏面加了。
我們打開settings.py文件,取消DEFAULT_REQUEST_HEADERS的註釋,加入如下的內容:
DEFAULT_REQUEST_HEADERS = {
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
'Accept-Language': 'en',
'Accept-Encoding' : 'gzip, deflate, br',
'Cache-Control' : 'max-age=0',
'Connection' : 'keep-alive',
'Host' : 'movie.douban.com',
'Upgrade-Insecure-Requests' : '1',
'User-Agent' : 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36',
}
這個是爲你的請求添加請求頭,如果你沒有設置headers的話,它就會使用這個請求頭請求,添加了User-Agent信息,所以這樣我們的爬蟲就可以僞裝瀏覽器了。
接下來重新運行爬蟲。
scrapy crawl zhihu
這時你就會發現得到的返回狀態碼就正常了。
解決了這個問題,我們接下來就可以分析頁面邏輯來正式實現爬蟲了。
頁面分析
使用選取工具選取整個電影的信息,可以發現,所有的信息都是放在單獨的一個li
標籤中的,而且在li下還有一個class爲item的div包裹着所有的信息。
定義item
根據前面的分析,我們需要抓取一共十個字段的信息,現在在items.py文件中定義item
class DoubanItem(Item):
# 排名
ranking = Field()
# 篇名
title = Field()
# 導演和演員
director = Field()
# 一句話描述 有的爲空
desc = Field()
# 評分
rating_num = Field()
# 評價人數
people_count = Field()
# 上映時間
date = Field()
# 上映國家
country = Field()
# 類別
category = Field()
parse方法
寫完上面的代碼,其實只是抓取一頁的罷了,爲了抓取完整的top250榜單,我們需要讓爬蟲跳轉到下一頁再進行循環抓取,因爲每個頁面的結構是一樣的,所以不用擔心會抓取不到。
這裏不單獨講解XPath和CSS選擇器的使用方法,多看一點資料,自己總結一下。
不知道各位有沒有獲取XPath和CSS好用一點的方法,歡迎分享給我。
def parse(self, response):
item = DoubanItem()
movies = response.xpath('//div[@class="item"]')
for movie in movies:
# 名次
item['ranking'] = movie.xpath('div[@class="pic"]/em/text()').extract()[0]
# 片名 提取多個片名
titles = movie.xpath('div[@class="info"]/div[1]/a/span/text()').extract()
item['title'] = titles
# 獲取導演信息和演員信息
info_director = movie.xpath('div[2]/div[2]/p[1]/text()[1]').extract()[0].replace(" ", "").replace("\n", "")
item['director'] = info_director
# 上映日期
date = movie.xpath('div[2]/div[2]/p[1]/text()[2]').extract()[0].replace(" ", "").replace("\n", "").split("/")[0]
# 製片國家
country = movie.xpath('div[2]/div[2]/p[1]/text()[2]').extract()[0].replace(" ", "").replace("\n", "").split("/")[1]
# 影片類型
category = movie.xpath('div[2]/div[2]/p[1]/text()[2]').extract()[0].replace(" ", "").replace("\n", "").split("/")[2]
item['date'] = date
item['country'] = country
item['category'] = category
desc = movie.xpath('div[@class="info"]/div[@class="bd"]/p[@class="quote"]/span/text()').extract()
if len(desc) != 0: # 判斷info的值是否爲空,不進行這一步有的電影信息並沒有會報錯或數據不全
item['desc'] = desc
else:
item['desc'] = ' '
# item['desc'] = movie.xpath('div[@class="info"]/div[@class="bd"]/p[@class="quote"]/span[@class="inq"]/text()').extract()[0]
item['rating_num'] = movie.xpath('div[@class="info"]/div[@class="bd"]/div[@class="star"]/span[@class="rating_num"]/text()').extract()[0]
item['people_count'] = movie.xpath('div[@class="info"]/div[@class="bd"]/div[@class="star"]/span[4]/text()').extract()[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, callback=self.parse, dont_filter=True)
那麼到這裏,代碼就寫完了。
然後我們來運行一下這個爬蟲,scrapy框架是通過命令來啓動爬蟲的,
在項目根目錄下打開命令提示符,輸入:
scrapy crawl douban
如果沒有出錯,你會在終端看到一行行滾動的信息。
存儲在Mongo
從官方文檔中拷貝如下代碼到pipeline.py中,只需要修改collection_name,其餘基本不用修改。在存儲MongoDB之前,你需要將正確安裝MongoDB,並且啓動MongoDB
。
class MongoPipeline(object):
collection_name = 'douban'
def __init__(self, mongo_uri, mongo_db):
self.mongo_uri = mongo_uri
self.mongo_db = mongo_db
@classmethod
def from_crawler(cls, crawler):
return cls(
mongo_uri=crawler.settings.get('MONGO_URI'),
mongo_db=crawler.settings.get('MONGO_DATABASE')
)
def open_spider(self, spider):
self.client = pymongo.MongoClient(self.mongo_uri)
self.db = self.client[self.mongo_db]
def close_spider(self, spider):
self.client.close()
def process_item(self, item, spider):
self.db[self.collection_name].insert_one(dict(item))
return item
另外記得開啓一下Item Pileline。setting.py
ITEM_PIPELINES = {
'dbmovie.pipelines.MongoPipeline': 400,
}
實驗結果
項目的完整代碼,可以看這裏scrapy爬取豆瓣電影Top250,存儲在MongoDB中
如果不想存儲在MongoDB中,可以使用scrapy支出的命令導出其他格式的文件
scrapy crawl douban -o data.json
scrapy crawl douban -o data.csv
scrapy crawl douban -o data.xml