對於scrapy我們前面已經介紹了簡單的應用,今天我們用一個完整的例子,爬取豆瓣電影TOP250來做一個小的練習,把scrapy階段做一個總結。
1 環境配置
語言:Python 3.6.1
IDE: Pycharm
瀏覽器:firefox
爬蟲框架:Scrapy 1.5.0
操作系統:Windows 10 家庭中文版
2 爬取前分析
2.1 需要保存的數據
首先確定我們要獲取的內容,在items中定義字段,來將非結構化數據生成結構化數據,獲取的內容主要包括:排名、電影名稱、得分、評論人數。
如下圖:
在scrapy中需要定義items.py來配置我們的字段:
import scrapy
class SpItem(scrapy.Item):
"""
定義item字段
"""
# 排名
ranking = scrapy.Field()
# 電影名稱
movie_name = scrapy.Field()
# 評分
score = scrapy.Field()
# 評論人數
people_num = scrapy.Field()
2.2 編寫爬蟲spider
基本的框架如下:
import scrapy
from sp.items import SpItem
class DoubanSpider(scrapy.Spider):
"""
爬取豆瓣電影TOP250類,繼承了scrapy.Spider類
"""
# 定義spider名稱,必須要有而且是唯一值
name = 'douban'
# 初始url,可以使用start_requests(self)也可以直接使用start_urls方式,而且區別是start_requests(self)可以更靈活,添加更多內容,如header等。
start_urls = ['https://movie.douban.com/top250']
def parse(self, response):
"""
解析response中的字段,傳送到items中
"""
# 實例化item,用來添加獲取的內容
item = SpItem()
pass
下面我們就看一看我們需要解析的內容,打開firefox瀏覽器,開發者模式(F12),通過元素選擇箭頭選擇我們要獲取的內容,發現我們要獲取的當前頁面的信息都在一個class爲grid_view的ol標籤中,如下圖:
2.3 使用scrapy shell獲取內容
因爲我們無法一次就能準確獲取我們的內容,所以建議還是先用scrapy shell來獲取內容再執行,執行結果如下:
scrapy shell "https://movie.douban.com/top250"
執行結果如下:
小夥伴們有沒有發現問題,沒錯看到403了,這是什麼原因,難道是因爲沒有添加header,好的那我就加上header試試,scrapy shell 添加header命令運行如下:
scrapy shell -s USER_AGENT="Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:58.0) Gecko/20100101 Firefox/58.0" "https://movie.douban.com/top250"
使用-s參數更多參數可以查看官方文檔(scrapy shell),傳入USER_AGENT頭信息,頭信息我們哪裏獲取呢?見下圖:
讓我們看一下結果:
ok,成功了,那我們就獲取我們想要的內容吧。
通過使用開發者工具和scrapy shell,我們先獲取ol下的所有li標籤,然後循環每個標籤提取我們需要的內容,分析步驟略了,大家多聯繫xpath和css使用方法即可。最終結果如下:
# 所有電影的標籤,每個電影的信息在一個li中,我們先獲取所有的li標籤
movies = response.css("ol.grid_view li")
# 循環每一個li標籤,也就是每一個電影的信息
for movie in movies:
# 排名
item['ranking'] = movie.css("div.pic em::text").extract_first()
# 電影名字
item['movie_name'] = movie.css("div.hd a span:first-child::text").extract_first()
# 得分
item['score'] = movie.css("div.star span.rating_num::text").extract_first()
# 評論人數
item['people_num'] = movie.css("div.star span:last-child::text").re("\d+")[0]
完善我們的spider代碼(douban_spider.py)如下:
# -*- coding: utf-8 -*-
import scrapy
from sp.items import SpItem
class DoubanSpider(scrapy.Spider):
"""
爬取豆瓣電影TOP250
"""
name = 'douban'
start_urls = ['https://movie.douban.com/top250']
def parse(self, response):
"""
解析response中的字段,傳送到items中
"""
item = SpItem()
movies = response.css("ol.grid_view li")
for movie in movies:
# 排名
item['ranking'] = movie.css("div.pic em::text").extract_first()
# 電影名字
item['movie_name'] = movie.css("div.hd a span:first-child::text").extract_first()
# 得分
item['score'] = movie.css("div.star span.rating_num::text").extract_first()
# 評論人數
item['people_num'] = movie.css("div.star span:last-child::text").re("\d+")[0]
yield item
2.4 運行spider
我的spider裏面沒有添加頭,我在settings.py中增加了如下參數:
這裏添加也是可以的 。
運行spider輸出到csv文件命令如下:
scrapy crawl douban -o douban.csv
執行結果如下:
這裏有兩個地方要注意:
1.如果直接用excel打開這個csv文件的話,會出現亂碼,需要用類似notepadd++工具打開後選擇編碼--轉碼爲utf-8格式,然後再用excel打開後就沒有這個問題了。
2.我們看輸出結果是有空行的,這個需要修改scrapy的源碼內容,在pycharm中連續點擊兩下shift鍵,彈出搜索框後搜索exporters.py,如下圖:
找到下面內容,添加一行newline=' ',如下圖:
我們再次執行操作後輸出結果如下:
是不是很神奇!
2.5 爬取所有頁面內容
首先還是分析頁面,找到最下面頁面內容部分,用瀏覽器工具找到後頁所對應的標籤,裏面的href內容就是我們要獲取的內容,如下:
注意這裏獲取的是相對鏈接,不是絕對鏈接,有一下兩種方式調用相對連接:
# scrapy 1.4版本以前的版本使用此段代碼
next_page = response.css("span.next a::attr(href)").extract_first()
if next_page is not None:
next_page = response.urljoin(next_page)
yield scrapy.Request(next_page, callback=self.parse)
# scrapy 1.4版本以後加入了follow方法,可以使用下面代碼
# next_page = response.css("span.next a::attr(href)").extract_first()
# if next_page is not None:
# yield response.follow(next_page, callback=self.parse)
我們的完整spider代碼如下:
# -*- coding: utf-8 -*-
import scrapy
from sp.items import SpItem
class DoubanSpider(scrapy.Spider):
"""
爬取豆瓣電影TOP250
"""
name = 'douban'
start_urls = ['https://movie.douban.com/top250']
def parse(self, response):
"""
解析response中的字段,傳送到items中
"""
item = SpItem()
movies = response.css("ol.grid_view li")
for movie in movies:
# 排名
item['ranking'] = movie.css("div.pic em::text").extract_first()
# 電影名字
item['movie_name'] = movie.css("div.hd a span:first-child::text").extract_first()
# 得分
item['score'] = movie.css("div.star span.rating_num::text").extract_first()
# 評論人數
item['people_num'] = movie.css("div.star span:last-child::text").re("\d+")[0]
yield item
# scrapy 1.4版本以前的版本使用此段代碼
next_page = response.css("span.next a::attr(href)").extract_first()
if next_page is not None:
next_page = response.urljoin(next_page)
yield scrapy.Request(next_page, callback=self.parse)
# scrapy 1.4版本以後加入了follow方法,可以使用下面代碼
# next_page = response.css("span.next a::attr(href)").extract_first()
# if next_page is not None:
# yield response.follow(next_page, callback=self.parse)
輸出結果如下:
除去標題,正好250。