目录
Scrapy 框架
谈起爬虫必然要提起 Scrapy 框架,因为它能够帮助提升爬虫的效率,从而更好地实现爬虫。
Scrapy 是一个为了抓取网页数据、提取结构性数据而编写的应用框架,该框架是封装的,包含 request (异步调度和处理)、下载器(多线程的 Downloader)、解析器(selector)和 twisted(异步处理)等。对于网站的内容爬取,其速度非常快捷。
也许读者会感到迷惑,有这么好的爬虫框架,为什么前面的章节还要学习使用 requests 库请求网页数据。其实,requests 是一个功能十分强大的库,它能够满足大部分网页数据获取的需求。其工作原理是向服务器发送数据请求,至于数据的下载和解析,都需要自己处理,因而灵活性高;而由于 Scrapy 框架的封装,使得其灵活性降低。
scrapy五大核心组件简介
- scrapy的基本使用我们已经掌握,但是各位心中一定会有些许的疑问,我们在编写scrapy工程的时候,我们只是在定义相关类中的属性或者方法,但是我们并没有手动的对类进行实例化或者手动调用过相关的方法,那么这些操作都是谁做的呢?接下来我们就来看看scrapy的五大核心组件的工作流程,然后大家就会上述的疑问有基本了解了。
- 引擎(Scrapy)
- 用来处理整个系统的数据流处理, 触发事务(框架核心)
- 调度器(Scheduler)
- 用来接受引擎发过来的请求, 压入队列中, 并在引擎再次请求的时候返回. 可以想像成一个URL(抓取网页的网址或者说是链接)的优先队列, 由它来决定下一个要抓取的网址是什么, 同时去除重复的网址
- 下载器(Downloader)
- 用于下载网页内容, 并将网页内容返回给蜘蛛(Scrapy下载器是建立在twisted这个高效的异步模型上的)
- 爬虫(Spiders)
-
爬虫是主要干活的, 用于从特定的网页中提取自己需要的信息, 即所谓的实体(Item)。用户也可以从中提取出链接,让Scrapy继续抓取下一个页面
-
- 项目管道(Pipeline)
- 负责处理爬虫从网页中抽取的实体,主要的功能是持久化实体、验证实体的有效性、清除不需要的信息。当页面被爬虫解析后,将被发送到项目管道,并经过几个特定的次序处理数据。
提升scrapy的爬取效率
-
增加并发:
- 默认scrapy开启的并发线程为32个,可以适当进行增加。在settings配置文件中修改CONCURRENT_REQUESTS = 100值为100,并发设置成了为100。
-
降低日志级别:
- 在运行scrapy时,会有大量日志信息的输出,为了减少CPU的使用率。可以设置log输出信息为INFO或者ERROR即可。在配置文件中编写:LOG_LEVEL = ‘INFO’
-
禁止cookie:
- 如果不是真的需要cookie,则在scrapy爬取数据时可以禁止cookie从而减少CPU的使用率,提升爬取效率。在配置文件中编写:COOKIES_ENABLED = False
-
禁止重试:
- 对失败的HTTP进行重新请求(重试)会减慢爬取速度,因此可以禁止重试。在配置文件中编写:RETRY_ENABLED = False
-
减少下载超时:
- 如果对一个非常慢的链接进行爬取,减少下载超时可以能让卡住的链接快速被放弃,从而提升效率。在配置文件中进行编写:DOWNLOAD_TIMEOUT = 10 超时时间为10s
scrapy基本使用
环境安装:
- linux和mac操作系统:
- pip install scrapy
- windows系统:(使用pycharm等ide工具,直接进行pip install scrapy 也可以)
- pip install wheel
- 下载twisted,下载地址为http://www.lfd.uci.edu/~gohlke/pythonlibs/#twisted
- 安装twisted:pip install Twisted‑17.1.0‑cp36‑cp36m‑win_amd64.whl
- pip install pywin32
- pip install scrapy
测试:在终端里录入scrapy指令,没有报错即表示安装成功!
scrapy使用流程:
此处创建的工程名为vegetable_pro
创建的爬虫文件为vegetable
- 创建工程:
- scrapy startproject ProName (scrapy startproject vegetable_pro)
- 进入工程目录:
- cd ProName (cd vegetable_pro)
- 创建爬虫文件:
- scrapy genspider spiderName www.xxx.com (scrapy genspider vegetable www.xxx.com)
- 编写相关操作代码
- 执行工程:
- scrapy crawl spiderName (scrapy crawl vegetable )
爬虫文件解析
import scrapy
from vegetable_pro.items import VegetableProItem
class VegetableSpider(scrapy.Spider):
name = 'vegetable' # 应用名称
# allowed_domains = ['www.xxx.com'] # 允许爬取的域名(如果遇到非该域名的url则爬取不到数据)
# url = 'http://www.jnmarket.net/import/list-1_'
start_urls = ['http://www.xinfadi.com.cn/marketanalysis/1/list/1.shtml'] #起始爬取的url
#访问起始URL并获取结果后的回调函数,该函数的response参数就是向起始的url发送请求后,获取的响应对象.该函数返回值必须为可迭代对象或者NUll
url = 'http://www.xinfadi.com.cn/marketanalysis/1/list/%d.shtml'
page_num = 2
def parse(self, response):
#xpath为response中的方法,可以将xpath表达式直接作用于该函数中
li_list = response.xpath('/html/body/div[2]/div[4]/div[1]/table/tr[position()>1]') # 使用xpath进行内容解析
for li in li_list:
#xpath函数返回的为列表,列表中存放的数据为Selector类型的数据。我们解析到的内容被封装在了Selector对象中,需要调用extract()函数将解析的内容从Selecor中取出。
name = li.xpath('./td[1]/text()').extract_first()
low_price = li.xpath('./td[2]/text()').extract_first()
avg_price = li.xpath('./td[3]/text()').extract_first()
high_price = li.xpath('td[4]/text()').extract_first()
size = li.xpath('td[5]/text()').extract_first()
new_time = li.xpath('td[7]/text()').extract_first()
item = VegetableProItem() #将解析到的数据封装至items对象中
item['name'] = name
item['low_price'] = low_price
item['avg_price'] = avg_price
item['high_price'] = high_price
item['size'] = size
item['new_time'] = new_time
yield item # #提交item到管道文件(pipelines.py)
# 分页爬取,通过控制url参数,来获取多页面的数据
if self.page_num <= 60:
new_url = format(self.url % self.page_num)
self.page_num +=1
yield scrapy.Request(url=new_url,callback=self.parse)
scrapy的数据持久化存储
基于终端指令的持久化存储
- 保证爬虫文件的parse方法中有可迭代类型对象(通常为列表or字典)的返回,该返回值可以通过终端指令的形式写入指定格式的文件中进行持久化操作。
import scrapy
class QiubaiSpider(scrapy.Spider):
name = 'qiubai'
allowed_domains = ['https://www.qiushibaike.com/']
start_urls = ['https://www.qiushibaike.com/']
def parse(self, response):
#xpath为response中的方法,可以将xpath表达式直接作用于该函数中
odiv = response.xpath('//div[@id="content-left"]/div')
content_list = [] #用于存储解析到的数据
for div in odiv:
#xpath函数返回的为列表,列表中存放的数据为Selector类型的数据。我们解析到的内容被封装在了Selector对象中,需要调用extract()函数将解析的内容从Selecor中取出。
author = div.xpath('.//div[@class="author clearfix"]/a/h2/text()')[0].extract()
content=div.xpath('.//div[@class="content"]/span/text()')[0].extract()
#将解析到的内容封装到字典中
dic={
'作者':author,
'内容':content
}
#将数据存储到content_list这个列表中
content_list.append(dic)
return content_list
执行指令:
- 执行输出指定格式进行存储:将爬取到的数据写入不同格式的文件中进行存储
scrapy crawl 爬虫名称 -o xxx.json
scrapy crawl 爬虫名称 -o xxx.xml
scrapy crawl 爬虫名称 -o xxx.csv
基于管道的持久化存储操作
- scrapy框架中已经为我们专门集成好了高效、便捷的持久化操作功能,我们直接使用即可。要想使用scrapy的持久化操作功能,我们首先来认识如下两个文件:
- items.py:数据结构模板文件。定义数据属性。
-
pipelines.py:管道文件。接收数据(items),进行持久化操作。
-
持久化流程
-
爬虫文件爬取到数据后,需要将数据封装到items对象中
-
使用yield关键字将items对象提交给pipelines管道进行持久化操作。
-
在管道文件中的process_item方法中接收爬虫文件提交过来的item对象,然后编写持久化存储的代码将item对象中存储的数据进行持久化存储
-
settings.py配置文件中开启管道
-
配置文件内开启管道:
ITEM_PIPELINES = {
'picturename.pipelines.PicturenamePipeline': 301,
'picturename.pipelines.MysqlPipeline': 300,
}
下列结构为字典,字典中的键值表示的是即将被启用执行的管道文件和其执行的优先级
值越小优先级越高
items文件:
import scrapy
class PicturenameItem(scrapy.Item):
# define the fields for your item here like:
# name = scrapy.Field()
picture_desc = scrapy.Field() # 存储信息
存储到文件内:
class PicturenamePipeline(object):
def __init__(self):
self.fp = None
def open_spider(self,spider):
print('开始爬虫')
self.fp = open('./xiaohua.txt','w',encoding='utf-8')
def process_item(self, item, spider):
self.fp.write(item['picture_desc'] + '\n' + '\r')
return item
def close_spider(self,spider):
print('爬虫结束')
self.fp.close()
存储到数据库:
class MysqlPipeline(object):
def __init__(self):
self.conn = pymysql.connect(host='127.0.0.1', port=3306, user='root', password='123456@yz', database='spider')
self.cursor = self.conn.cursor()
def process_item(self, item, spider):
self.cursor.execute('insert into xiaohua values("{}")'.format(item["picture_desc"]))
self.conn.commit()
return item
def close_spider(self,spider):
self.conn.close()
self.cursor.close()
实例:爬取蔬菜网站的近十天内蔬菜的价格等信息
1:生成工程以及生产爬虫文件后的目录结构
2:settings.py文件进行配置
1:user_agent代理
USER_AGENT = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.100 Safari/537.36 ,'
2:不遵循君子协议
ROBOTSTXT_OBEY = False
3:Scrapy下载器将执行的最大并发(即并发)请求数
CONCURRENT_REQUESTS = 100
4:日志等级
LOG_LEVEL = "ERROR"
5:设置管道
ITEM_PIPELINES = {
'vegetable_pro.pipelines.VegetableProPipeline': 300,
}
3:爬虫文件的编写
import scrapy
from vegetable_pro.items import VegetableProItem
class VegetableSpider(scrapy.Spider):
name = 'vegetable' # 应用名称
# allowed_domains = ['www.xxx.com'] # 允许爬取的域名(如果遇到非该域名的url则爬取不到数据)
# url = 'http://www.jnmarket.net/import/list-1_'
start_urls = ['http://www.xinfadi.com.cn/marketanalysis/1/list/1.shtml'] #起始爬取的url
#访问起始URL并获取结果后的回调函数,该函数的response参数就是向起始的url发送请求后,获取的响应对象.该函数返回值必须为可迭代对象或者NUll
url = 'http://www.xinfadi.com.cn/marketanalysis/1/list/%d.shtml'
page_num = 2
def parse(self, response):
#xpath为response中的方法,可以将xpath表达式直接作用于该函数中
li_list = response.xpath('/html/body/div[2]/div[4]/div[1]/table/tr[position()>1]') # 使用xpath进行内容解析
for li in li_list:
#xpath函数返回的为列表,列表中存放的数据为Selector类型的数据。我们解析到的内容被封装在了Selector对象中,需要调用extract()函数将解析的内容从Selecor中取出。
name = li.xpath('./td[1]/text()').extract_first()
low_price = li.xpath('./td[2]/text()').extract_first()
avg_price = li.xpath('./td[3]/text()').extract_first()
high_price = li.xpath('td[4]/text()').extract_first()
size = li.xpath('td[5]/text()').extract_first()
new_time = li.xpath('td[7]/text()').extract_first()
item = VegetableProItem() #将解析到的数据封装至items对象中
item['name'] = name
item['low_price'] = low_price
item['avg_price'] = avg_price
item['high_price'] = high_price
item['size'] = size
item['new_time'] = new_time
yield item # #提交item到管道文件(pipelines.py)
# 分页爬取,通过控制url参数,来获取多页面的数据
if self.page_num <= 60:
new_url = format(self.url % self.page_num)
self.page_num +=1
yield scrapy.Request(url=new_url,callback=self.parse)
4:创建数据库,获取的数据保存到数据库内
5:items文件的编写
import scrapy
class VegetableProItem(scrapy.Item):
# define the fields for your item here like:
# name = scrapy.Field()
# 存储爬取信息
name = scrapy.Field()
low_price = scrapy.Field()
avg_price = scrapy.Field()
high_price = scrapy.Field()
size = scrapy.Field()
new_time = scrapy.Field()
6:pipelines.py文件的编写
from itemadapter import ItemAdapter
import pymysql
class VegetableProPipeline(object):
def __init__(self):
print('开始')
# 连接数据库
self.conn = pymysql.connect(host='127.0.0.1', port=3306, user='root', password='123456@yz',
database='graduation_project')
self.cursor = self.conn.cursor()
def process_item(self, item, spider):
# 编写sql语句,将数据存到mysql数据库
insert_sql = """
insert into weather_vegetable(name,low_price,avg_price,high_price,szie,new_time) values(%s,%s,%s,%s,%s,%s)
"""
self.cursor.execute(insert_sql, (item["name"],item["low_price"],item["avg_price"],item["high_price"],item["size"],item["new_time"]))
self.conn.commit()
return item
def close_spider(self,spider):
self.conn.close()
self.cursor.close()
print('结束')
7:执行程序
scrapy crawl vegetable