前言:
昨天使用Scrapy框架簡單爬取並下載了豆瓣Top250首頁的排名前25個電影的電影名。
感覺略過寒酸,今天咱就來仔細搞一搞,搞到更加詳細的信息!!!
1.首先,我們知道改變我們提取的數據是在我們的spider爬蟲文件裏的parse函數下進行操作的!
(注意哦!咱這個parse函數就是來解析和提取數據的!!!)
(1)那麼,既然我們要獲取更加詳細的信息,我們就要來分析網頁源碼:(經過簡單觀察之後,我們發現,這25個電影的各自的所有信息都分別放在了class屬性值爲info的div標籤裏,那麼,我們要提取出這些電影的信息的話,就要首先提取到這些特定的div標籤,然後再對這些個div標籤進行操作即可!)
‘’‘
網頁源碼剖析:
<div class="info">
<div class="hd">
<a href="https://movie.douban.com/subject/1292052/" class="">
<span class="title">肖申克的救贖</span>
<span class="title"> / The Shawshank Redemption</span>
<span class="other"> / 月黑高飛(港) / 刺激1995(臺)</span>
</a>
<span class="playable">[可播放]</span>
</div>
<div class="bd">
<p class="">
導演: 弗蘭克·德拉邦特 Frank Darabont 主演: 蒂姆·羅賓斯 Tim Robbins /...<br>
1994 / 美國 / 犯罪 劇情
</p>
<div class="star">
<span class="rating5-t"></span>
<span class="rating_num" property="v:average">9.7</span>
<span property="v:best" content="10.0"></span>
<span>1983356人評價</span>
</div>
<p class="quote">
<span class="inq">希望讓人自由。</span>
</p>
</div>
</div>
‘’‘
2.其次:既然要分析如何分別匹配到各個電影的詳細的信息,那麼我們就考慮使用scrapy shell來十分方便快捷的進行各種嘗試
(1)終端進入scrapy shell交互式界面命令:
scrapy shell https://movie.douban.com/top250
(2)首先,分析下咱剛剛那個思想是否正確(每個電影的信息都藏在class屬性值爲info的div中)
這就說明咱的分析是沒得錯的!
(2)第二步,分析如何獲取到電影名字
1.首先,咱嘗試下獲取下第一個電影的電影名字。結合源碼使用xpath匹配:
2.然後,一個電影的名字咱都獲取成功了,使用循環嘗試能否成功獲取到25個電影名字:
會發現灰常的成功哦!
(3)第三步:分析如何獲取到電影的主演:
1.首先,觀察網頁源碼可知:這個主演所在的標籤裏面非常混亂,所以,咱先嚐試匹配出25個電影的主演所在的標籤:
包含電影主演的標籤:
也是非常的成功!!!
2.第二步:我們要想辦法匹配到每一個電影的主演的名字,但是!觀察剛剛匹配到25個電影的包含主演的標籤的text信息可知,這裏面有坑哦!有些電影不一定有主演:
所以,咱再匹配這25個電影的主演名字的時候,一定要注意使用判斷避坑!(見下:)
if "主" in con_star_name:
star_name=re.findall("主演?:? ?(.*)",con_star_name)[0]
else:
star_name="空"
(4)分析如何獲取電影的評分:
1.首先,咱嘗試下獲取下第一個電影的評分。結合源碼使用xpath匹配:
又是美好的成功哦!
2.然後,一個電影的評分咱都獲取成功了,使用循環嘗試能否成功獲取到25個電影的評分:
非常的完美!
總結:
def parse(self, response): #解析和提取數據
# 獲取電影信息數據
# films_name=response.xpath('//div[@class="info"]/div/a/span[1]/text()').extract()
node_list=response.xpath('//div[@class="info"]') #25個
for node in node_list:
# 電影名字
film_name=node.xpath('./div/a/span[1]/text()').extract()[0]
# 主演 拿標籤內容,再正則表達式匹配
con_star_name=node.xpath('./div/p[1]/text()').extract()[0]
if "主" in con_star_name:
star_name=re.findall("主演?:? ?(.*)",con_star_name)[0]
else:
star_name="空"
#評分
score=node_list.xpath('./div/div/span[@property="v:average"]/text()').extract()[0]
2.然後,數據已經解析並且可以正確的提取到我們想要的數據。下一步就是將其交給管道進行儲存。
(1)首先,引擎交給spider(爬蟲文件)的信息已經被爬蟲解析並提取正確的信息,下一步是spider(爬蟲文件)將數據再次交給引擎,而能否成功交給引擎,這就取決於items.py文件(定義結構化數據字段)是否有對應的字段名等待插入數據:
# -*- coding: utf-8 -*-
# Define here the models for your scraped items
#
# See documentation in:
# https://docs.scrapy.org/en/latest/topics/items.html
import scrapy
class DoubanItem(scrapy.Item):
# define the fields for your item here like:
# name = scrapy.Field()
#需要定義字段名 就像數據庫那樣,有字段名,才能插入數據(即存儲數據)
# films_name=scrapy.Field() #定義字段名
film_name=scrapy.Field()
star_name=scrapy.Field()
score=scrapy.Field()
(2)第二步,items.py文件中已經創建好字段名等待插入數據了,那麼,爬蟲文件裏就要將數據一一對應字段名:
#在剛剛的爬蟲文件的循環下寫入即可:
# 使用字段名 收集數據
item=item=DoubanItem()
item["film_name"]=film_name
item["star_name"]=star_name
item["score"]=score
注意:在settings.py文件中開啓管道並關閉robots協議!
(3)第三步:引擎將數據交給管道的中間的item這一步已經成功走過去了,下一步就是如何將數據返回給管道:在這裏要注意一個問題:慣性思維會讓我們在這個函數裏定性的使用return返回item,但是,切記return返回的是parse這個方法(會造成只會傳給管道第一個電影信息的bug)。而我們所需要的是返回每次解析後的數據,so我們想到了生成器,使用yield返回,這樣每搞一次數據出來,生成器就給一次數據,循環如此,非常nice!!!
#我們只需在爬蟲文件的循環最底下加入即可:
# 形式:{"film_name":"肖申克的救贖","star_name":"蒂姆","score":"9.7"}
yield item
(4)第四步:引擎已經將數據交給管道(即pipelines.py管道文件),但是,我們這是25個請求,就會進行25次處理,也就意味着,引擎會交給管道25次數據,那麼在我們在管道里進行儲存數據的時候,按往常來說就要來來回回打開關閉文件,進行數據的寫入。這樣顯然不好。所以:在此引入管道文件裏的兩種牛批的方法:
#爬蟲文件開啓,此方法就開啓
def open_spider(self,spider):
pass
#爬蟲文件關閉,此方法就開啓
def close_spider(self,spider):
pass
這就非常合我們的需求,結合這兩個方法,我們只需要在open_spider()方法里加入打開文件操作;在close_spider()方法里加入關閉文件操作即可,這樣就避免了文件的來來回回的開啓關閉,好處不言而喻。
來看看酷炫吊炸天的升級版的pipelines.py文件(管道文件):
# -*- coding: utf-8 -*-
# Define your item pipelines here
#
# Don't forget to add your pipeline to the ITEM_PIPELINES setting
# See: https://docs.scrapy.org/en/latest/topics/item-pipeline.html
import json
class DoubanPipeline(object):
def open_spider(self,spider): #爬蟲文件開啓,此方法就開啓
self.f=open("films.txt","w",encoding="utf-8") #打開文件
def process_item(self, item, spider): #會來25次,就會調用25次這個方法 如果按常規來寫,文件就會被操作25次打開關閉
#爲了能寫進text json.dumps將dic數據轉換爲str
json_str=json.dumps(dict(item),ensure_ascii=False)+"\n"
self.f.write(json_str) #爬蟲文件開啓時,文件就已經打開,在此直接寫入數據即可!
return item
def close_spider(self,spider): #爬蟲文件關閉,此方法就開啓
self.f.close() #爬蟲文件關閉時,引擎已經將全部數據交給管道,關閉文件
3.爬取豆瓣Top250首頁25個電影的更多的信息已經實現,效果如下:
拓展:剛剛咱們實現了豆瓣首頁25個電影信息的爬取,那麼,如果我們想要爬取更多頁(比如:10頁)該怎麼做呢?
(1)首先,分析如何爬取兩頁,注意:Scrapy框架運行的流程,任何模塊發出的請求都是給到引擎的,所以,我們只需要在爬蟲文件中的第一次請求的數據提交給管道之後,使用yield發起一個請求(豆瓣電影Top250第二頁的url),那麼,這個請求就會自動的交給了引擎,就開啓了新一輪的自動化走流程,但是,再最後引擎需要將數據再送給spider爬蟲文件讓其解析並獲取真正想要存儲到管道的數據,所以就需要callback回調自身這個parse函數。
邏輯如下圖:
(2)獲取兩頁的信息都可以實現了,那麼獲取更多(比如:10頁),只要找到每頁url的規律,給個循環就OK啦!
1.剖析每頁url可知:每加一頁,對應的start參數的值就會加25。
'''
各頁url規律剖析:
https://movie.douban.com/top250
https://movie.douban.com/top250?start=0&filter=
https://movie.douban.com/top250?start=25&filter=
https://movie.douban.com/top250?start=50&filter=
'''
2.在爬蟲文件每個請求執行完成功之後,利用格式化輸出.format()方法向引擎發送每頁的請求即可!(每完成一個請求的全部流程,page_num就會加一,25倍的page_num就對應了每頁url的start參數的值;注意:我們會發現總共就10頁有信息,多了就沒得信息了,所以設置了if判斷,如果url獲取的信息爲空,就返回空,引擎接到空的請求,就會自動關閉)
# -*- coding: utf-8 -*-
import scrapy
import re
from ..items import DoubanItem #因爲我們要使用包含定義字段名的類,所以需要導入
class DbSpider(scrapy.Spider):
name = 'db'
allowed_domains = ['movie.douban.com']
start_urls = ['https://movie.douban.com/top250']
page_num=0 #類變量
def parse(self, response): #解析和提取數據
# 獲取電影信息數據
# films_name=response.xpath('//div[@class="info"]/div/a/span[1]/text()').extract()
node_list=response.xpath('//div[@class="info"]') #25個
if node_list: #此判斷的作用:在爬取到10頁之後,就獲取不到了!判斷每次是否獲取到數據,如果沒有則返回空(即停止了)
for node in node_list:
# 電影名字
film_name=node.xpath('./div/a/span[1]/text()').extract()[0]
# 主演 拿標籤內容,再正則表達式匹配
con_star_name=node.xpath('./div/p[1]/text()').extract()[0]
if "主" in con_star_name:
star_name=re.findall("主演?:? ?(.*)",con_star_name)[0]
else:
star_name="空"
#評分
score=node_list.xpath('./div/div/span[@property="v:average"]/text()').extract()[0]
# 使用字段名 收集數據
item=item=DoubanItem()
item["film_name"]=film_name
item["star_name"]=star_name
item["score"]=score
# 形式:{"film_name":"肖申克的救贖","star_name":"蒂姆","score":"9.7"}
yield item #不使用return的原因是,return返回的是parse這個方法(會造成只會傳給管道第一個電影信息的bug)。而我們所需要的是返回每次解析後的數據
self.page_num+=1
print("page_num:",self.page_num)
page_url="https://movie.douban.com/top250?start={}&filter=".format(self.page_num*25)
yield scrapy.Request(page_url, callback=self.parse)
# 注意:各個模塊的請求都會交給引擎,然後經過引擎的一系列操作;但是,切記:引擎最後要把得到的數據再來給到
# spider爬蟲文件讓它解析並獲取到真正想要的數據(callback=self.parse)這樣就可以再給到自身。
else:
return
'''
各頁url規律剖析:
https://movie.douban.com/top250
https://movie.douban.com/top250?start=0&filter=
https://movie.douban.com/top250?start=25&filter=
https://movie.douban.com/top250?start=50&filter=
'''
'''
網頁源碼剖析:
<div class="info">
<div class="hd">
<a href="https://movie.douban.com/subject/1292052/" class="">
<span class="title">肖申克的救贖</span>
<span class="title"> / The Shawshank Redemption</span>
<span class="other"> / 月黑高飛(港) / 刺激1995(臺)</span>
</a>
<span class="playable">[可播放]</span>
</div>
<div class="bd">
<p class="">
導演: 弗蘭克·德拉邦特 Frank Darabont 主演: 蒂姆·羅賓斯 Tim Robbins /...<br>
1994 / 美國 / 犯罪 劇情
</p>
<div class="star">
<span class="rating5-t"></span>
<span class="rating_num" property="v:average">9.7</span>
<span property="v:best" content="10.0"></span>
<span>1983356人評價</span>
</div>
<p class="quote">
<span class="inq">希望讓人自由。</span>
</p>
</div>
</div>
'''
切記:
要在settings.py文件中開啓延時。
不然:服務器很容易檢測出來,後果嚴重!!!
運行會發現很棒哦!
拓展:來仔細研讀研讀settings.py配置文件!
# -*- coding: utf-8 -*-
# Scrapy settings for douban project
#
# For simplicity, this file contains only settings considered important or
# commonly used. You can find more settings consulting the documentation:
#
# https://docs.scrapy.org/en/latest/topics/settings.html
# https://docs.scrapy.org/en/latest/topics/downloader-middleware.html
# https://docs.scrapy.org/en/latest/topics/spider-middleware.html
BOT_NAME = 'douban' #scrapy項目名字
SPIDER_MODULES = ['douban.spiders'] #爬蟲模塊
NEWSPIDER_MODULE = 'douban.spiders' #使用genspider命令創建的爬蟲模塊
# Crawl responsibly by identifying yourself (and your website) on the user-agent
#USER_AGENT = 'douban (+http://www.yourdomain.com)' #默認的User_Agent 用戶代理
# Obey robots.txt rules
ROBOTSTXT_OBEY = False #機器人協議
#配置 最大 併發 請求 通過scrapy執行的
# Configure maximum concurrent requests performed by Scrapy (default: 16) #併發請求的最大值,默認是16。
#CONCURRENT_REQUESTS = 32
#設置網站請求延遲 默認是0
# Configure a delay for requests for the same website (default: 0)
# See https://docs.scrapy.org/en/latest/topics/settings.html#download-delay
# See also autothrottle settings and docs
DOWNLOAD_DELAY = 2 #2秒
# The download delay setting will honor only one of:
#CONCURRENT_REQUESTS_PER_DOMAIN = 16 #單個域名允許的併發請求 16個 注意:一個域名可以對應多個IP
#CONCURRENT_REQUESTS_PER_IP = 16 #單個IP允許的併發請求 16個
# Disable cookies (enabled by default)
#COOKIES_ENABLED = False #設置是否使用cookies
# Disable Telnet Console (enabled by default)
#TELNETCONSOLE_ENABLED = False #設置是否使用監控控制檯
# Override the default request headers:
DEFAULT_REQUEST_HEADERS = { #設置全局的請求頭
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
'Accept-Language': 'en',
# 爬取網頁的請求頭中的User-Agent
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.162 Safari/537.36"
}
# Enable or disable spider middlewares #是否打開爬蟲中間件(類)的封印
# See https://docs.scrapy.org/en/latest/topics/spider-middleware.html
#SPIDER_MIDDLEWARES = {
# 'douban.middlewares.DoubanSpiderMiddleware': 543,
#}
# Enable or disable downloader middlewares #是否打開下載器中間件(類)的封印
# See https://docs.scrapy.org/en/latest/topics/downloader-middleware.html
#DOWNLOADER_MIDDLEWARES = {
# 'douban.middlewares.DoubanDownloaderMiddleware': 543,
#}
# Enable or disable extensions #擴展程序
# See https://docs.scrapy.org/en/latest/topics/extensions.html
#EXTENSIONS = {
# 'scrapy.extensions.telnet.TelnetConsole': None,
#}
# Configure item pipelines #設置管道 管道是一個類,可以設置優先級(0-1000),越小則級別越高
# See https://docs.scrapy.org/en/latest/topics/item-pipeline.html
# ITEM_PIPELINES = {
# 'douban.pipelines.DoubanPipeline': 200,
# 'douban.pipelines.DoubanSqlPipeline': 300,
# }
#啓用或配置 AutoThrottle 拓展
# Enable and configure the AutoThrottle extension (disabled by default)
# See https://docs.scrapy.org/en/latest/topics/autothrottle.html
#AUTOTHROTTLE_ENABLED = True
#初始下載延遲
# The initial download delay
#AUTOTHROTTLE_START_DELAY = 5
#在高延遲情況下設置最大下載延遲
# The maximum download delay to be set in case of high latencies
#AUTOTHROTTLE_MAX_DELAY = 60
#每個遠程服務器並行發送的請求的平均數
# The average number of requests Scrapy should be sending in parallel to
# each remote server
#AUTOTHROTTLE_TARGET_CONCURRENCY = 1.0
#啓用顯示收到的每個響應的調節信息
# Enable showing throttling stats for every response received:
#AUTOTHROTTLE_DEBUG = False
#啓用配置默認緩存
# Enable and configure HTTP caching (disabled by default)
# See https://docs.scrapy.org/en/latest/topics/downloader-middleware.html#httpcache-middleware-settings
#HTTPCACHE_ENABLED = True
#HTTPCACHE_EXPIRATION_SECS = 0
#HTTPCACHE_DIR = 'httpcache'
#HTTPCACHE_IGNORE_HTTP_CODES = []
#HTTPCACHE_STORAGE = 'scrapy.extensions.httpcache.FilesystemCacheStorage'
DATABASE_CONFIG={
"type":"mysql",
"config":{
"host":"localhost",
"port":3306,
"user":"root",
"password":"123456",
"db":"spider39",
"charset":"utf8"
}
}