一、Scrapy的日志等級
在使用scrapy crawl spiderFileName運行程序時,在終端里打印輸出的就是scrapy的日志信息。
1、日志等級(信息種類)
- ERROR:錯誤
- WARNING:警告
- INFO:一般信息
- DEBUG:調試信息(默認)
2、設置日志信息指定輸出
在settings.py配置文件中任意位置加入:
# 設置終端輸出指定種類的日志信息
LOG_LEVEL = 'ERROR' # 只打印ERROR級別的日志信息
將日志信息存儲在指定文件中,而不再顯示在終端里:
# 設置終端輸出指定種類的日志信息
LOG_LEVEL = 'ERROR' # 只打印ERROR級別的日志信息
LOG_FILE = 'log.txt' # 指定日志存儲到一個文件中
二、請求傳參
請求傳參針對場景:爬取的數據值不在同一個頁面中。
需求:將id97電影網站中電影詳情數據進行爬取(名稱、類型、導演、語言、片長)
1、問題:如何將兩個方法解析的電影詳情數據存儲到一個item對象中
meta參數可實現item對象的傳遞。scrapy.Request()方法中有一個參數meta.通過meta可以將items對象傳遞給回調函數。
注意:meta只能接收字典類型的數據值。因此需要將items封裝到字典中,將字典賦值給meta參數,meta就可以將字典傳遞給回調函數。
def parse(self, response):
div_list = response.xpath('/html/body/div[1]/div[1]/div[2]/div') # 獲取/html/body/div[1]/div[1]/div[2]下所有子div
for div in div_list:
"""省略代碼"""
# 創建items對象
item = MovieproItem()
item['name'] = name
item['kind'] = kind
# 手動發起請求
yield scrapy.Request(url=url, callback=self.parseBySecondPage, meta={'item': item})
隨后可以在parseBySecondPage函數中取出Request方法的meta參數傳遞過來的字典。
取出方法是response.meta,如下所示:
def parseBySecondPage(self, response):
"""專門用於解析二級子頁面中的數據值"""
# 取出Request方法的meta參數傳遞過來的字典:取出方法是response.meta
item = response.meta['item']
item['actor'] = actor
item['language'] = language
item['longTime'] = longTime
2、爬蟲文件movie.py編寫如下
import scrapy
from moviePro.items import MovieproItem
class MovieSpider(scrapy.Spider):
name = 'movie'
# allowed_domains = ['www.id97.com']
start_urls = ['https://www.55xia.com/movie'] # 網站地址更改。。
def parseBySecondPage(self, response):
"""專門用於解析二級子頁面中的數據值"""
# 導演、語言、片長
actor = response.xpath('/html/body/div[1]/div/div/div[1]/div[1]/div[2]/table/tbody/tr[1]/td[2]/a/text()').extract_first()
language = response.xpath('/html/body/div[1]/div/div/div[1]/div[1]/div[2]/table/tbody/tr[6]/td[1]/text()').extract_first()
longTime = response.xpath('/html/body/div[1]/div/div/div[1]/div[1]/div[2]/table/tbody/tr[8]/td[2]/text()').extract_first()
# 取出Request方法的meta參數傳遞過來的字典:取出方法是response.meta
item = response.meta['item']
item['actor'] = actor
item['language'] = language
item['longTime'] = longTime
# 將item提交給管道
yield item
def parse(self, response):
# 名稱、類型、導演、語言、片長
div_list = response.xpath('/html/body/div[1]/div[1]/div[2]/div') # 獲取/html/body/div[1]/div[1]/div[2]下所有子div
for div in div_list:
# 電影名稱
name = div.xpath('.//div[@class="meta"]/h1/a/text()').extract_first()
# 電影種類: //text() 該div下所有文本數據均獲取
# 如下xpath方法返回的是一個列表,且列表長度為4
kind = div.xpath('.//div[@class="otherinfo"]//text()').extract()
# 將kind列表轉化為字符串
kind = "".join(kind)
# 影片詳情url
url = div.xpath('.//div[@class="meta"]/h1/a/@href').extract_first()
# 創建items對象
item = MovieproItem()
item['name'] = name
item['kind'] = kind
# 問題:如何將兩個方法解析的電影詳情數據存儲到一個item對象中——meta
# 下一步:對url發起請求,獲取頁面數據,進行指定數據解析
# 手動發起請求
yield scrapy.Request(url=url, callback=self.parseBySecondPage, meta={'item': item})
3、其他文件配置
(1)items.py文件封裝所有屬性
import scrapy
class MovieproItem(scrapy.Item):
# define the fields for your item here like:
# name = scrapy.Field()
# 封裝所有屬性
name = scrapy.Field()
kind = scrapy.Field()
actor = scrapy.Field()
language = scrapy.Field()
longTime = scrapy.Field()
(2)管道文件pipeline.py
import json
class MovieproPipeline(object):
fp = None
def open_spider(self, spider):
self.fp = open('movie.txt', 'w', encoding='utf-8')
def process_item(self, item, spider):
# detail = item['name'] + ':' + item['kind'] + ':' + item['actor'] + ':' + item['language'] + ':' + item['longTile'] + '\n\n\n'
detail = dict(item)
json.dump(detail, self.fp, ensure_ascii=False)
return item
def close_spider(self, spider ):
self.fp.close()
(3)settings.py配置文件
# Crawl responsibly by identifying yourself (and your website) on the user-agent
USER_AGENT = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36' # 偽裝請求載體身份
# Obey robots.txt rules
ROBOTSTXT_OBEY = False # 不遵從門戶網站robots協議,避免某些信息爬取不到
# Configure item pipelines
# See https://doc.scrapy.org/en/latest/topics/item-pipeline.html
ITEM_PIPELINES = {
'moviePro.pipelines.MovieproPipeline': 300,
}
3、特別處理
(1)kind(電影種類)特別處理:
kind = div.xpath('.//div[@class="otherinfo"]//text()').extract()
# 將kind列表轉化為字符串
kind = "".join(kind)
解析它的xpath方法返回的是一個列表,且列表長度為4。因此不能再使用extract_first方法,要使用extract()方法獲取列表。
獲取列表后需要將列表轉化為字符串。在這里使用"".join(list)實現。
(2)在管道文件中完成數據持久化
# 方法一:拼接字符串寫入文件中
def process_item(self, item, spider):
detail = item['name'] + ':' + item['kind'] + ':' + item['actor'] + ':' + item['language'] + ':' + item['longTile'] + '\n\n\n'
self.fp.write(detail)
return item
# 方法二:json.dump()將dict類型的數據轉成str,並寫入到json文件中
def process_item(self, item, spider):
detail = dict(item)
json.dump(detail, self.fp, ensure_ascii=False)
return item