基于Scrapy爬虫框架的豆瓣电影数据采集
项目介绍项目简介项目开发环境项目需求分析Scrapy框架Scrapy框架基础知识Scrapy框架安装Scrapy框架使用项目功能实现爬虫主程序数据采集模块数据信息采集实现403反爬虫控制台输出采集结果多元数据持久化模块json文件存储csv文件存储Excel文件存储Excel文件存储xml文件存储二进制数据采集模块深度自动采集模块日志系统模块项目介绍
项目简介
项目名称:《基于Scrapy爬虫框架的豆瓣电影数据采集》项目领域:大数据采集项目开发环境
编程语言:Python 3.6.5开发IDE:PyCharm使用技术:Scrapy框架数据库:MySQL数据库IDE:SQLYOG项目需求分析
爬虫主程序数据采集模块多元数据持久化模块二进制数据采集模块深度自动采集模块日志系统模块Scrapy框架
Scrapy框架基础知识
Scrapy框架概念
Scrapy是一个为了爬取网站数据,提取结构性数据而编写的应用框架。
Scrapy框架功能
爬虫程序基于Scrapy框架创建,借助于Scrapy的item以及pipeline功能实现数据抽取,并将爬取的数据存储到item中。最后通过pipeline对数据进一步处理(存储、打印)。
Scrapy框架安装
安装第三方爬虫离线包
在DOS环境下安装第三方爬虫离线包:
E:\>pip install Twisted-18.4.0-cp36-cp36m-win_amd64.whlProcessing e:\twisted-18.4.0-cp36-cp36m-win_amd64.whl
安装Scrapy爬虫框架
E:\>pip install -U scrapyCollecting scrapy
查看框架版本号
E:\>pip install scrapy==Collecting scrapy==
用固定版本号安装
E:\>pip install scrapy==1.5.0Collecting scrapy==1.5.0
Scrapy框架使用
创建一个Scrapy项目scrapy startproject 项目名
scrapy startproject doubanmov;
生成一个爬虫
scrapy genspider 脚本程序名称 域名
scrapy genspider movie "”
提取数据
完善spider,使用xpath等方法保存数据
pipeline管道中保存数据
项目功能实现
爬虫主程序数据采集模块
数据信息采集
定义item字段数据(items.py),使用XPATH定位并解析网页源代码,并获取所需要的“电影排名”和“电影名称”数据文本(movie.py)。将采集数据已生成器方式返回至item。import scrapyclass DoubanmovItem(scrapy.Item):# define the fields for your item here like:# name = scrapy.Field()#排名rank = scrapy.Field()#电影名称title = scrapy.Field()#电影图片pic = scrapy.Field()
import scrapyimport reimport mathfrom doubanmov.items import DoubanmovItemfrom doubanmov.logmessage import cloggerclass MovieSpider(scrapy.Spider):name = 'movie'allowed_domains = ['']#爬取首地址start_urls = ['/top250']def __init__(self):self.log = clogger.demo()def parse(self, response):#定位采集数据的标签items_mov = response.xpath('//div[@class="item"]')#遍历结果for items in items_mov:#创建采集对象mov = DoubanmovItem()#排名#XPATH解析并赋值mov['rank'] = items.xpath('div[@class="pic"]/em/text()').extract()# 名字解析并赋值mov['title'] = items.xpath('div[@class="info"]/div[@class="hd"]/a/span[@class="title"][1]/text()').extract()mov['pic'] =items.xpath('div[@class="pic"]/a/img/@src').extract()#生成生成器输出yield mov
实现403反爬虫
禁用框架自带的浏览器标识(settings.py)重新设置浏览器标识(rotate_useragent.py)使用IP代理器# 导入random模块import random# 导入useragent用户代理模块中的UserAgentMiddleware类from scrapy.downloadermiddlewares.useragent import UserAgentMiddleware# RotateUserAgentMiddleware类,继承 UserAgentMiddleware 父类# 作用:创建动态代理列表,随机选取列表中的用户代理头部信息,伪装请求。# 绑定爬虫程序的每一次请求,一并发送到访问网址。# 发爬虫技术:由于很多网站设置反爬虫技术,禁止爬虫程序直接访问网页,# 因此需要创建动态代理,将爬虫程序模拟伪装成浏览器进行网页访问。class RotateUserAgentMiddleware(UserAgentMiddleware):def __init__(self, user_agent=''):self.user_agent = user_agentdef process_request(self, request, spider):#这句话用于随机轮换user-agentua = random.choice(self.user_agent_list)if ua:# 输出自动轮换的user-agentprint(ua)request.headers.setdefault('User-Agent', ua)# the default user_agent_list composes chrome,I E,firefox,Mozilla,opera,netscape# for more user agent strings,you can find it in /pages/useragentstring.php# 编写头部请求代理列表user_agent_list = [\"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.1 (KHTML, like Gecko) Chrome/22.0.1207.1 Safari/537.1"\"Mozilla/5.0 (X11; CrOS i686 2268.111.0) AppleWebKit/536.11 (KHTML, like Gecko) Chrome/20.0.1132.57 Safari/536.11",\"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.6 (KHTML, like Gecko) Chrome/20.0.1092.0 Safari/536.6",\"Mozilla/5.0 (Windows NT 6.2) AppleWebKit/536.6 (KHTML, like Gecko) Chrome/20.0.1090.0 Safari/536.6",\"Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.1 (KHTML, like Gecko) Chrome/19.77.34.5 Safari/537.1",\"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/536.5 (KHTML, like Gecko) Chrome/19.0.1084.9 Safari/536.5",\"Mozilla/5.0 (Windows NT 6.0) AppleWebKit/536.5 (KHTML, like Gecko) Chrome/19.0.1084.36 Safari/536.5",\"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1063.0 Safari/536.3",\"Mozilla/5.0 (Windows NT 5.1) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1063.0 Safari/536.3",\"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_0) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1063.0 Safari/536.3",\"Mozilla/5.0 (Windows NT 6.2) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1062.0 Safari/536.3",\"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1062.0 Safari/536.3",\"Mozilla/5.0 (Windows NT 6.2) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1061.1 Safari/536.3",\"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1061.1 Safari/536.3",\"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1061.1 Safari/536.3",\"Mozilla/5.0 (Windows NT 6.2) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1061.0 Safari/536.3",\"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/535.24 (KHTML, like Gecko) Chrome/19.0.1055.1 Safari/535.24",\"Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/535.24 (KHTML, like Gecko) Chrome/19.0.1055.1 Safari/535.24"]
DOWNLOADER_MIDDLEWARES = {'doubanmov.middlewares.DoubanmovDownloaderMiddleware': 543,# 禁用框架自带的浏览器标识'scrapy.contrib.downloadermiddleware.useragent.UserAgentMiddleware': None,# 设置浏览器标识'doubanmov.rotate_useragent.RotateUserAgentMiddleware': 400,}
控制台输出采集结果
通过Scrapy框架的pipeline管道将需要的数据通过控制台显示。开放管道输出ITEM_PIPELINES = {'doubanmov.pipelines.DoubanmovPipeline': 300,'doubanmov.pipelinesjson.DoubanmovPipeline': 301,'doubanmov.pipelinesexc.DoubanmovPipeline': 302,'doubanmov.pipelinesSQL.DoubanmovPipeline': 303,'doubanmov.pipelinespic.DoubanmovPipeline': 304,'doubanmov.pipelinescsv.DoubanmovPipeline': 305,'doubanmov.pipelinesjson1.DoubanmovPipeline': 301,}
from doubanmov.logmessage import cloggerclass DoubanmovPipeline(object):def __init__(self):self.log1 = clogger.demo()def process_item(self, item, spider):#管道输出try:print('电影排名:{0}'.format(item['rank'][0]))print('电影名字:<<{0}>>'.format(item['title'][0]))self.log1.logginfo('输出了<<{0}>>电影'.format(item['title'][0]))except:self.log1.loggerror('输出<<{0}>>电影失败'.format(item['title'][0]))passreturn item
多元数据持久化模块
json文件存储
方法一: 将item数据存储为列表包字典的模式通过json.jump()将列表写入json文件import jsonfrom doubanmov.logmessage import clogger# list1 = []class DoubanmovPipeline(object):def __init__(self):self.list1 = []self.log = clogger.demo()def process_item(self, item, spider):try:#将item数据存储至字典中dict1={'排名':item['rank'][0],'名字':item['title'][0]}# 将字典数据追加至列表中中self.list1.append(dict1)# json文件存储with open('data/jsondate.json',mode='w',encoding='utf-8') as file:json.dump(self.list1,file,ensure_ascii=False)#日志存储self.log.logginfo('向"data/jsondate.json"中存储了<<{0}>>电影'.format(item['title'][0]))except:self.log.loggerror('向"data/jsondate.json"中存储<<{0}>>电影失败'.format(item['title'][0]))return item
方法二: 将爬取的数据存储为字典类型将字典数据序列化并增加换行功能
import jsonfrom doubanmov.logmessage import cloggerclass DoubanmovPipeline(object):def process_item(self, item, spider):#获取爬取的数据存成字典模式dict1 = {'排名': item['rank'][0], '名字': item['title'][0]}#将列表序列化并增加换行filejson = json.dumps(dict1, ensure_ascii=False) + '\n'#用文本方式存储json数据with open('data/jsondate.json', mode='a', encoding='utf-8') as file:file.write(filejson)return item
方法三:
利用Scrapy命令存储json文件
#设置存储编码FEED_EXPORT_ENCODING='UTF-8'
scrapy crawl movie -o myjson.json -t json
csv文件存储
方法一: 将爬取的数据存储为字典类型将字典数据追加至列表将列表按表头和内容存储数据import csvfrom doubanmov.logmessage import cloggerclass DoubanmovPipeline(object):def __init__(self):self.list1 = []self.log = clogger.demo()def process_item(self, item, spider):try:#爬取数据存储为字典类型dict1={'排名':item['rank'][0],'名字':item['title'][0]}#将字典数据追加为列表self.list1.append(dict1)with open('data/movie.csv',mode='w',encoding='utf-8',newline='') as file:#csv文件写入write = csv.DictWriter(file,self.list1[0])# csv文件头写入write.writeheader()# csv文件内容写入write.writerows(self.list1)self.log.logginfo('向"data/movie.csv"中存储了<<{0}>>电影'.format(item['title'][0]))except:self.log.loggerror('向"data/movie.csv"中存储<<{0}>>电影失败'.format(item['title'][0]))return item
方法二:
利用Scrapy命令存储csv文件
#设置存储编码FEED_EXPORT_ENCODING='UTF-8'
scrapy crawl movie -o mycsv.csv -t csv
Excel文件存储
创建Excel文件获取爬取数据头文件将数据存储为列表包列表的模式按行和列存储数据至Excel文件import xlwtlist1=[]from doubanmov.logmessage import cloggerclass DoubanmovPipeline(object):def __init__(self):# Excel文件创建工作薄self.workbook = xlwt.Workbook()#获取单页对象self.sheet = self.workbook.add_sheet('sheet1')#添加数据---文件头写self.header = ['电影排名', '电影名字']for i in range(len(self.header)):self.sheet.write(0, i, self.header[i])self.log = clogger.demo()def process_item(self, item, spider):#添加数据---文件内容获取firlist=[item['rank'][0],item['title'][0]]list1.append(firlist)return itemdef close_spider(self, spider):try:#按行遍历for i in range(1,len(list1)+1):#按列遍历for j in range(len(self.header)):# 添加数据---文件内容按行列存储self.sheet.write(i,j,list1[i-1][j])self.log.logginfo('向"data/movie.xls"中存储了<<{0}>>电影'.format(list1[i - 1][0]))except:self.log.loggerror('向"data/movie.xls"中存储<<{0}>>电影失败'.format(list1[i - 1][0]))#保存Excel文件self.workbook.save('data/movie.xls')
Excel文件存储
连接数据库(主机地址,端口,用户名,密码,数据库名)获取数据库对象执行SQL语句(增删改查)提交事务import pymysqlfrom doubanmov.logmessage import cloggerlist1=[]class DoubanmovPipeline(object):def __init__(self):# 连接数据库self.con = pymysql.connect(host='localhost', port=3306, user='root', passwd='root', db='db_movie', )# 获取游标对象self.cur = self.con.cursor()self.log = clogger.demo()def process_item(self, item, spider):#执行SQL语句try:self.cur.execute('insert into movie values(%d,"%s")' %(int(item["rank"][0]),item["title"][0]))#提交事务mit()self.log.logginfo('向数据库中存储了<<{0}>>电影'.format(item['title'][0]))except:self.con.rollback()self.log.loggerror('向数据库中存储<<{0}>>电影失败'.format(item['title'][0]))return itemdef close_spider(self,spider):self.cur.close()self.con.close()
xml文件存储
利用Scrapy命令存储xml文件
#设置存储编码FEED_EXPORT_ENCODING='UTF-8'
scrapy crawl movie -o myxml.xml -t xml
二进制数据采集模块
通过urllib模块获取海报二进制数据将二进制数据流写入方式将数据写入文件中import urllib.request as urfrom doubanmov.logmessage import clogger# import spiders.movie as smclass DoubanmovPipeline(object):def __init__(self):self.log = clogger.demo()def process_item(self, item, spider):try:#通过url获取网页数据res = ur.urlopen(item['pic'][0])#读取网页返回数据res = res.read()name = item['title'][0] + '.jpg'#通过基本数据存储海报存储至文件中with open('data/pic/{0}'.format(name),mode='wb')as file:file.write(res)self.log.logginfo('向"data/pic"中下载了<<{0}>>电影'.format(item['title'][0]))except:self.log.loggerror('向"data/pic"中下载<<{0}>>电影失败'.format(item['title'][0]))return item
深度自动采集模块
获取下一页数据采集地址(movie.py)判断下一页地址是否为空判断是否继续翻页通过scrapy.request和新地址回调爬去数据函数#通过第一页数据源代码查找到下一页地址并获取地址next_di = response.xpath('//div[@class="paginator"]/span[3]/a/@href').extract()#将地址中的翻页具体数据提取作为日志用page1 = re.match(r"\?start=(.*?)&",next_di[0])page = math.floor(int(page1[1])/25)+1#判断是否为最后一页,是则为空if next_di:#判断是否继续翻页choose = input('是否继续翻页:(y/n)>>>')if choose.lower()=='y':self.log.logginfo('选择了继续翻页翻至第{0}页'.format(page))#拼接下一页的新地址next_page = '/top250' + next_di[0]#回调函数,递归爬虫方法yield scrapy.Request(next_page,callback=self.parse)else:self.log.logginfo('选择了退出')
日志系统模块
方法一: 通过代用logging模块创建系统日志模块增加日志等级的方法(info,error)通过模块调用至所需的py文件中。调用日志等级方法并存储日志信息import loggingimport osimport time#创建日志系统模块class clogger:a = Nonedef __init__(self):#获取logger对象self.logger = logging.getLogger(__name__)#设置日志等级self.logger.setLevel(logging.INFO)#设置格式化输出logmat = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s --- %(message)s')#设置文件路径if not os.path.exists('log'):os.mkdir('log')filepath = os.path.join('log','系统日志'+'.log')#设置FileHandler对象filehand = logging.FileHandler(filepath)#设置级别filehand.setLevel(logging.INFO)#设置格式化输出filehand.setFormatter(logmat)#添加filehandler对象self.logger.addHandler(filehand)#定义静态方法判断是否继续创建类对象@staticmethoddef demo():if clogger.a == None:clogger.a=clogger()return clogger.adef logginfo(self,mess):self.logger.info(mess)passdef loggerror(self,mess):self.logger.error(mess)pass
#调用创建日志模块from doubanmov.logmessage import cloggerclass DoubanmovPipeline(object):def __init__(self):self.log1 = clogger.demo()def process_item(self, item, spider):#管道输出try:print('电影排名:{0}'.format(item['rank'][0]))print('电影名字:<<{0}>>'.format(item['title'][0]))#调用日志模块的方法self.log1.logginfo('输出了<<{0}>>电影'.format(item['title'][0]))except:self.log1.loggerror('输出<<{0}>>电影失败'.format(item['title'][0]))passreturn item
方法二:
利用Scrapy框架的日志系统存储日志(settings.py)日志等级
critical------严重错误
error--------一般错误
warning----警告信息
info----------一般信息
debug------调试信息日志设置
LOG_ENABLE ----默认:TRUE,启用logging
LOG_FILE----------默认:None,在当前目录里创建logging输出文件名
LOG_LEVEL--------默认:DEBUG,log的最低等级
LOG_STDOUT-----默认:FALSE,如果为TRUE,进程将所有标准化输出重定向到log文件中
LOG_ENCODING----默认:utf-8
LOG_ENABLED = FalseLOG_FILE = 'log/aaa.log'LOG_LEVEL = "INFO"LOG_STDOUT = True