Date: 2019-07-15html
Author: Sunpython
Scrapy是一个为了爬取网站数据、提取结构化数据而编写的爬虫应用框架。Scrapy内部实现了包括并发请求、免登陆、URL去重等不少复杂操做,用户不须要明白Scrapy内部具体的爬取策略,只须要根据本身的须要,编写小部分的代码,就能抓取到所须要的数据mysql
此节咱们学习下如何采用采用scrapy进行项目流程开发和配置web
使用startproject命令建立项目sql
scrapy startproject scrapy_proj #使用scrapy产生一个scrapy_name爬虫项目
使用genspider命令在项目中建立爬虫脚本shell
cd scrapy_proj/scrapy_proj scrapy genspider --list scrapy genspider myspider "www.myspider_domain.com"
此时会在scrapy_proj/spiders/产生一个新的文件myspider.py数据库
这个是咱们的爬取页面的主入口和页面下载完成后解析主入口。json
设置settings.py 文件,设置相关的配置信息,具体配置见下面参数的说明api
配置文件参数说明浏览器
(1)ROBOTSTXT_OBEY = True ————— 是否遵照robots.txt规则
说明:
robots.txt 是遵循 Robot协议 的一个文件,它保存在网站的服务器中,它的做用是,告诉搜索引擎爬虫,本网站哪些目录下的网页 不但愿 你进行爬取收录。在Scrapy启动后,会在第一时间访问网站的 robots.txt 文件,而后决定该网站的爬取范围。(在某些状况下咱们想要获取的内容偏偏是被 robots.txt 所禁止访问的。因此,某些时候,咱们就要将此配置项设置为 False ,拒绝遵照 Robot协议 !)
(2)CONCURRENT_REQUESTS = 16-----------开启线程数量,默认16,能够自行设置
这个参数涉及到scrapy爬取的并发量,items的处理速度
(3)DOWNLOAD_DELAY = 3 ——— 下载延迟时间。下载器在下载同一个网站下一个页面前须要等待的时间。该选项能够用来限制爬取速度, 减轻服务器压力。(反爬策略之一)
(4)CONCURRENT_REQUESTS_PER_DOMAIN = 16 将对任何单个域执行的并发(即同时)请求的最大数量。
CONCURRENT_REQUESTS_PER_IP = 16 将对任何单个IP执行的并发(即同时)请求的最大数量。若是非零,CONCURRENT_REQUESTS_PER_DOMAIN则忽略该 设置,而改成使用此设置。换句话说,并发限制将应用于每一个IP,而不是每一个域。
(5)COOKIES_ENABLED = False
是否启用cookie。是否启用cookies middleware。若是关闭,cookies将不会发送给web server。
除非您真的 须要,不然请禁止cookies。在进行通用爬取时cookies并不须要, (搜索引擎则忽略cookies)。禁止cookies能减小CPU使用率及Scrapy爬虫在内存中记录的踪影,提升性能。
COOKIES_DEBUG:默认: False
若是启用,Scrapy将记录全部在request(cookie 请求头)发送的cookies及response接收到的cookies(set-cookie接收头)
(6)AUTOTHROTTLE_START_DELAY = 5
初始下载延迟时间(单位:秒)
(7)AUTOTHROTTLE_MAX_DELAY = 60
高并发请求时最大延迟时间(单位:秒)
(8) USER_AGENT 用户代理
这个是相当重要的,大部分服务器在请求快了会首先检查User_Agent,而scrapy默认的浏览器头是scrapy1.1 咱们须要开启而且修改为浏览器头,如:Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.1 (KHTML, like Gecko) Chrome/22.0.1207.1 Safari/537.1。 可是最好是这个USER-AGENT会随机自动更换最好了。
(8)DEFAULT_REQUEST_HEADERS
默认请求头部信息,例如以下配置
DEFAULT_REQUEST_HEADERS = { 'accept': 'image/webp,*/*;q=0.8', 'accept-language': 'zh-CN,zh;q=0.8', 'referer': 'https://www.taobao.com/', 'user-agent': 'Mozilla/5.0 (Windows NT 6.3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1650.63 Safari/537.36', }
这个是浏览器请求头,不少网站都会检查客户端的headers,好比豆瓣就是每个请求都检查headers的user_agent,不然只会返回403,能够开启user-agent
(9) SPIDER_MIDDLEWARES
Spider中间件是介入到Scrapy中的spider处理机制的钩子框架,能够插入自定义功能来处理发送给 Spiders 的response,以及spider产生的item和request。
要启用Spider中间件(Spider Middlewares),能够将其加入到 SPIDER_MIDDLEWARES 设置中。 该设置是一个字典,键为中间件的路径,值为中间件的顺序(order)。
(10) DOWNLOADER_MIDDLEWARES
要激活下载器中间件组件,将其加入到 DOWNLOADER_MIDDLEWARES 设置中。 该设置是一个字典(dict),键为中间件类的路径,值为其中间件的顺序(order)。
(11)ITEM_PIPELINES
每一个Item Pipeline组件其实就是一个实现了一个简单方法的Python类。他们接受一个item并在上面执行逻辑,还能决定这个item究竟是否还要继续往下传输,若是不要了就直接丢弃。
(12)AUTOTHROTTLE — 自动限速 (反爬策略之一)
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 #Scrapy请求的平均数量应该并行发送每一个远程服务器 # 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
(13)是否启用在本地缓存,若是开启会优先读取本地缓存,从而加快爬取速度,视状况而定
HTTPCACHE_ENABLED = True
HTTPCACHE_EXPIRATION_SECS = 0
HTTPCACHE_DIR = 'httpcache'
HTTPCACHE_IGNORE_HTTP_CODES = []
HTTPCACHE_STORAGE = 'scrapy.extensions.httpcache.FilesystemCacheStorage'
答案:在settings.py中加入以下信息
LOG_LEVEL= 'INFO' #日志级别 LOG_FILE ='log.txt' #日志打印的文件名称
DEBUG < INFO < WARNING < ERROR
日志案例:(settings.py中设置以下信息)
############### log settings begin ###################### LOG_LEVEL = "INFO" from datetime import datetime import os today = datetime.now() LOG_DIR = "logs" if not os.path.exists(LOG_DIR): os.mkdir(LOG_DIR) LOG_FILE = "{}/scrapy_{}_{}_{}.log".format(LOG_DIR, today.year, today.month, today.day) ############### log settings end ######################
糗事百科的主页:www.qiushibaike.com
使用startproject命令建立项目(糗事百科爬虫项目)
scrapy startproject qiubai_proj #使用scrapy产生一个scrapy_name爬虫项目
使用genspider命令在项目中建立爬虫脚本
cd qiubai_proj/qiubai_proj scrapy genspider qiubai "www.qiushibaike.com"
此时会在qiubai_proj/spiders/产生一个新的文件qiubai.py
这个是咱们的爬取页面的主入口和页面下载完成后解析主入口。
修改配置文件settings.py:
BOT_NAME = 'qiubai_proj' SPIDER_MODULES = ['qiubai_proj.spiders'] NEWSPIDER_MODULE = 'qiubai_proj.spiders' # Crawl responsibly by identifying yourself (and your website) on the user-agent #USER_AGENT = 'qiubai_proj (+http://www.yourdomain.com)' USER_AGENT = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) " \ "AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36" # Obey robots.txt rules #ROBOTSTXT_OBEY = True ROBOTSTXT_OBEY = False LOG_LEVEL= 'DEBUG'
分析糗事百科网站,定义数据模型用于保存爬取的数据。
编辑文件qiubai_proj/qiubai_proj/items.py, 内容以下:
# -*- coding: utf-8 -*- # Define here the models for your scraped items # # See documentation in: # https://doc.scrapy.org/en/latest/topics/items.html import scrapy class QiubaiProjItem(scrapy.Item): # define the fields for your item here like: #保存头像连接 image_url = scrapy.Field() #保存名字 name = scrapy.Field() #保存年龄 age = scrapy.Field() #保存内容 content = scrapy.Field() #可笑的个数 haha_count = scrapy.Field()
关于xpath语法使用请参考day03中的xpath语法章节。
# -*- coding: utf-8 -*- import scrapy class QiubaiSpider(scrapy.Spider): name = 'qiubai' allowed_domains = ['www.qiushibaike.com'] start_urls = ['http://www.qiushibaike.com/'] #主入口url #parse函数就是文件解析函数,response就是响应对象 def parse(self, response): # with open("qiubai.html", 'w', encoding="utf-8") as f: # f.write(response.text) div_list = response.xpath('//div[starts-with(@id, "qiushi_tag_")]') print(type(div_list)) #遍历列表,获取列表内容 item_list = [] for div in div_list: ''' 先经过xpath获取内容,返回的是一个列表 而后经过extract()转换成unicode字符串,再获取第0个, 也就是指定的内容 将解析到的内容保存到字典中 ''' image_url = div.xpath('./div[@class="author clearfix"]//img/@src').extract_first() name = div.xpath('./div[@class="author clearfix"]//h2/text()').extract_first() age = div.xpath('./div[@class="author clearfix"]/div/text()').extract_first() content = div.xpath('./a/div[@class="content"]/span/text()').extract() content = ' '.join(content) haha_count = div.xpath('./div[@class="stats"]/span[@class="stats-vote"]/i/text()').extract()[0] item = dict( image_url = image_url, name = name, age = age, content = content, haha_count = haha_count ) yield item #item_list.append(item) #return item_list
(1)保存爬取的内容到json文件中
scrapy crawl qiubai -o qiubai.json
能够查看产生的json文件,将内容拷贝到json在线格式网站
https://www.json.cn/, 看数据爬取是否和真实相符。
(2)保存爬取的数据到xml文件中
scrapy crawl qiubai -o qiubai.xml
(3)保存爬取的数据到数据报表csv文件中
scrapy crawl qiubai -o qiubai.csv
知识总结:
经过指令建立爬虫文件 cd qiubai_proj/qiubai_proj scrapy genspider qiubai "www.qiushibaike.com" 那么就会在firstSpider/firstSpider/spiders里面自动建立一个qiubai.py name: 爬虫的名字,启动的时候根据爬虫的名字启动项目 allowed_domains:容许的域名,就是爬取的时候这个请求要不要发送,若是是该容许域名之下的url,就会发送,若是不是,则过滤掉这个请求,这是一个列表,能够写多个容许的域名 start_urls:爬虫起始url,是一个列表,里面能够写多个,通常只写一个 def parse(self, response): 这个函数很是重要,就是你之后写代码的地方,parse函数名是固定的,当收到下载数据的时候会自动的调用这个方法,该方法第二个参数为response,这是一个响应对象,从该对象中获取html字符串,而后解析之。
糗事百科的主页:www.qiushibaike.com
本节咱们将对上节数据进行数据保存(持久化)
本节分别将数据保存到json文件和mysql数据库中
使用 Scrapy 提供的 exporter 存储 Json 数据
Scrapy 为咱们提供了一个 JsonItemExporter 类来进行 Json 数据的存储,很是方便
修改上节中的spiders
使用yield改造,使得spider成为一个生成器,不断地往pipeline里面流入待处理的数据
# -*- coding: utf-8 -*- import scrapy class QiubaiSpider(scrapy.Spider): name = 'qiubai' allowed_domains = ['www.qiushibaike.com'] start_urls = ['http://www.qiushibaike.com/'] # 主入口url # parse函数就是文件解析函数,response就是响应对象 def parse(self, response): # with open("qiubai.html", 'w', encoding="utf-8") as f: # f.write(response.text) div_list = response.xpath('//div[starts-with(@id, "qiushi_tag_")]') #print(type(div_list)) # 遍历列表,获取列表内容 item_list = [] for div in div_list: ''' 先经过xpath获取内容,返回的是一个列表 而后经过extract()转换成unicode字符串,再获取第0个, 也就是指定的内容 将解析到的内容保存到字典中 ''' image_url = div.xpath('./div[@class="author clearfix"]//img/@src').extract_first() name = div.xpath('./div[@class="author clearfix"]//h2/text()').extract_first().strip("\n") age = div.xpath('./div[@class="author clearfix"]/div/text()').extract_first() contents = div.xpath('./a/div[@class="content"]/span/text()').extract() content = ' '.join([ct.strip() for ct in contents]) haha_count = div.xpath('./div[@class="stats"]/span[@class="stats-vote"]/i/text()').extract()[0] item = dict( image_url=image_url, name=name, age=age, content=content, haha_count=haha_count ) yield item # item_list.append(item) # return item_list
在settings.py文件中开启ITEM_PIPELINES选项,开启以下信息
# Configure item pipelines # See https://doc.scrapy.org/en/latest/topics/item-pipeline.html ITEM_PIPELINES = { 'qiubai_proj.pipelines.QiubaiProjPipeline': 300, }
pipelines的逻辑处理
经过pipelines将spiders转发过来的item数据进行导入到json报表中
编辑qiubai_proj/pipelines.py文件,添加以下内容
# -*- coding: utf-8 -*- # Define your item pipelines here # # Don't forget to add your pipeline to the ITEM_PIPELINES setting # See: https://doc.scrapy.org/en/latest/topics/item-pipeline.html from scrapy.exporters import JsonItemExporter class QiubaiProjPipeline(object): # 调用 scrapy 提供的 json exporter 导出 json 文件 def __init__(self): self.file = open('questions_exporter.json', 'wb') # 初始化 exporter 实例,执行输出的文件和编码 self.exporter = JsonItemExporter(self.file, encoding='utf-8', ensure_ascii=False) # 开启倒入数据 self.exporter.start_exporting() def close_spider(self, spider): self.exporter.finish_exporting() self.file.close() def process_item(self, item, spider): ''' 使用 scrapy.exporters.JsonItemExporter 生成的文件 ''' print("########### pipelines begin ###############") #print(item) self.exporter.export_item(item) print("########### pipelines end ###############") return item
上述pipeline将统计的item结果数据保存到questions_exporter.json中
添加日志功能
settings.py中添加日志功能 文件及路径,log目录须要先建好
today = datetime.now()
log_file_path = "log/scrapy_{}{}{}.log".format(today.year, today.month, today.day)
日志输出
LOG_LEVEL = 'DEBUG'
LOG_FILE = log_file_path
scrapy crawl qiubai
运行成功后,能够查看questions_exporter.json文件
依赖库:pymysql
Twisted 是一个异步网络框架,不幸的是大部分数据库api实现只有阻塞式接口,twisted.enterprise.adbapi为此产生,它是DB-API 2.0 API的非阻塞接口,能够访问各类关系数据库。
不一样于直接建立数据库链接, 而是使用 adbapi.ConnectionPool 类来管理链接. 这就可让 adbapi 来使用多个链接, 好比每一个线程一个链接,这很简单:
使用前面例子的 "dbmodule" , 来建立一个 ConnectionPool 对象
from twisted.enterprise import adbapi链接mysql,要这样:
_conn = adbapi.ConnectionPool(' db='test', user='root', passwd='aaaa', host='localhost',use_unicode=True, charset='utf8'*)
数据库定义
建立数据库
create database qiubai_db default charset=utf8; CREATE TABLE qiubai ( id int(11) PRIMARY KEY auto_increment COMMENT '设置主键自增', image_url varchar(150) NOT NULL COMMENT '图片url连接', name VARCHAR(50) COMMENT '名称', age VARCHAR(10) COMMENT '年龄', content VARCHAR(500) COMMENT '内容', haha_count INT COMMENT '笑点数' ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
将上述内容放入到table.sql文件,进入mysql终端中,执行 source ./table.sql;
建立数据库和表成功!
settings.py文件中添加以下mysql数据库配置信息:
MYSQL_SETTINGS = { 'HOST':'192.168.51.63', #数据库地址 'DATABASE':'qiubai_proj', #数据库名称 'USER':'zhougy', #登录用户名 'PASSWORD':'123456', #登录密码 'CHARSET':'utf8', #字符编码 }
添加文件 mysql_pipelines.py, 内容添加以下:
scrapy框架底层网络库是twisted,同时他也
此文件采用twisted.enterprise.adbapi异步处理接口处理mysql操做
# -*- coding: utf-8 -*- __author__ = 'zhougy' __date__ = '2018/9/2 下午7:26' import json import pymysql from twisted.enterprise import adbapi from .settings import MYSQL_SETTINGS import logging class MysqlTwistedPipline(object): def __init__(self, dbpool): self.dbpool = dbpool @classmethod def from_settings(cls, settings): dbparms = dict( host=MYSQL_SETTINGS['HOST'], db=MYSQL_SETTINGS['DATABASE'], user=MYSQL_SETTINGS['USER'], passwd=MYSQL_SETTINGS['PASSWORD'], charset=MYSQL_SETTINGS['CHARSET'], cursorclass=pymysql.cursors.DictCursor, # 指定 curosr 类型 use_unicode=True, ) # 指定擦作数据库的模块名和数据库参数参数 dbpool = adbapi.ConnectionPool("pymysql", **dbparms) return cls(dbpool) def process_item(self, item, spider): ''' 使用twisted将mysql插入变成异步处理 :param item: :param spider: :return: ''' query = self.dbpool.runInteraction(self.db_insert, item) # 指定异常处理方法 query.addErrback(self.handle_error, item, spider) # 处理异常 return item def handle_error(self, failure, item, spider): # 处理异步插入的异常 print("######################1") #print(item) #print(failure) logging.error(f"handler_error has error failure: {failure}") logging.warning() print("########################2") def db_insert(self, cursor, item): # 执行具体的插入 # 根据不一样的item 构建不一样的sql语句并插入到mysql中 insert_sql, params = self.get_insert_sql(item) cursor.execute(insert_sql, params) logging.info(f"write db ok with data:{item['name']}") def get_insert_sql(self, item): insert_sql = """ insert into qiubai(image_url, name, age, content, haha_count) VALUES (%s, %s, %s, %s, %s) """ params = ( item["image_url"], item["name"], item["age"], item["content"], item["haha_count"]) return insert_sql, params
至此,基于mysql的pipeline异步处理管道逻辑就已经完成。
修改配置文件settings.py
将上述MysqlTwistedPipline管道处理类添加到配置文件中
ITEM_PIPELINES = { 'qiubai_proj.pipelines.QiubaiProjPipeline': 300, 'qiubai_proj.mysql_pipelines.MysqlTwistedPipline': 300, }
运行启动spider
scrapy crawl qiubai
运行成功后,能够查看数据库表数据。