爬虫框架之Scrapy(一)

时间:2021-07-20 23:35:39

scrapy简介

scrapy是一个用python实现为了爬取网站数据,提取结构性数据而编写的应用框架,功能非常的强大。

scrapy常应用在包括数据挖掘,信息处理或者储存历史数据的一系列程序中。

scrapy框架图

爬虫框架之Scrapy(一)

绿线是数据流向

Scrapy Engine(引擎):负责Spiders、Item Pipeline,Downloader、Scheduler中间的通信、信号和数据传递等。

Scheduler(调度器):负责接收引擎传递过来的requests请求,并按照一定的方式整理队列,入队,当引擎需要时,交还给引擎。

Downloader(下载器):负责下载引擎发送过的所有requests请求,将它获得的responses交还给引擎,由引擎交给Spiders处理。

Spiders(爬虫):负责处理所有的responses,从中分析提取数据,获得item字段需要的数据,并将需要跟进的URl提供给引擎,再次进入调度器。

Item Pipline(管道):它负责处理Spiders中获取到的item,并进行后期处理的地方(分析,过滤,储存等)。

Downloader Middlewares(下载中间件):可以当做一个自定义扩展下载功能的组件。

Spiders MIddlewares(爬虫中间件 ):可以理解为一个自定义扩展和操作引擎与Spiders中间通信功能的组件。(比如进入Spiders的responses和从Spiders出去的requests)

Scrapy的核心由引擎控制,其基本流程是这样的:

1 引擎打开一个域名,Spiders处理这个域名,并让Spiders处理第一个爬取获取的url。

2 引擎从Spiders那获取第一个需要爬取的URL,然后作为请求在调度中进行调度。

3 引擎从调度那获取接下来进行爬取的页面。

4 调度将下一个爬取的URL返回给引擎,引擎将他们通过下载中间件发送到下载器。

5 当网页被下载器下载完成以后,响应内容通过下载中间件被发送到引擎。

6 引擎收到下载器的响应并将它通过Spiders中间件发送到Spiders进行处理。

7 Spiders处理响应并返回爬取到的item,然后给引擎发送新的请求。

8 引擎发送处理后的item到项目管道,然后把处理结果返回给调度器,调度器计划处理下一个请求抓取。

9 系统重复2-9的操作,直到调度中没有请求,然后断开引擎与域之间的联系。

制作Scrapy一般需要4个步骤:

1 新建项目(scrapy startproject xxx):创建一个爬虫项目

2 明确目标(编写items.py):明确要抓取的目标

3 制作爬虫(spiders/xxxspiders.py):制作爬虫开始网页爬取

4 储存内容(pipelines.py):设计管道储存爬取内容

安装创建一个爬虫项目

我使用的pycharm,并且创建了虚拟环境。

爬虫框架之Scrapy(一)

查看新环境的package

爬虫框架之Scrapy(一)

在新的环境中安装scrapy,在这里安装的是1.52版本

爬虫框架之Scrapy(一)

打开terminal,创建新的爬虫项目

爬虫框架之Scrapy(一)

scrapy startproject happy

创建了一个新的爬虫项目,可以看下它的目录结构

爬虫框架之Scrapy(一)

这些文件分别是:

scrapy.cfg:项目的主配置信息(真正的配置信息在setting.py中)

items.py:项目的目标文件,设置数据储存模板,用于数据结构化,比较行Django的model

middlewares.py :中间件文件,设计中间件

pipelines.py : 管道文件,数据的持久化处理

setting.py :配置文件,比如递归的层数,并发数,延迟下载等。

spiders:爬虫目录,创建文件,编写爬虫解析规则

新创建一个爬虫程序

1 cd 项目名称

2 scrapy genspider 应用名称 爬取网页的起始url (例如:scrapy genspider qiushi www.qiushibaike.com)

比如按照上面项目要输如的:

1 cd happy

2 scrapy genspider qiushi www.qiushibaike.com

发现项目创建了一个qiushi.py的文件

爬虫框架之Scrapy(一)

解读下这段代码:

# -*- coding: utf-8 -*-
import scrapy class QiushiSpider(scrapy.Spider):
# 应用名称
name = 'qiushi'
# 允许爬取的域名,如果遇到非该域名的url则爬取不到数据 一般会把它给注释掉
allowed_domains = ['www.qiushibaike.com']
# 起始爬取的url
start_urls = ['http://www.qiushibaike.com/']
'''
访问起始的url并获取及结果后的回调函数,该函数的responses参数就是对起始url发送请求后获得的响应对象
该函数的返回必须为可迭代对象或者null
'''
def parse(self, response):
pass

3 修改settings.py

19行
USER_AGENT = 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.71 Safari/537.36'
# 伪装请求载体身份
22 行
不遵守robots协议
ROBOTSTXT_OBEY = False

4 执行爬虫程序

第一种方式:

scrapy crawl 应用名称 # 该种执行方式会显示执行的日志

scrapy crawl 应用名称 --nolog # 该种执行方式不会显示执行的日志信息

第二种方式:

在最外层的project_name文件下新建一个py文件,名字随便写,在文件里写入:

爬虫框架之Scrapy(一)

从此以后,只要执行start.py文件,就可以让程序跑起来

初始实例

将糗百首页中段子的内容和标题进行爬取

qiushi.py

# -*- coding: utf-8 -*-
import scrapy class QiushiSpider(scrapy.Spider):
name = 'qiushi'
allowed_domains = ['www.qiushibaike.com']
start_urls = ['http://www.qiushibaike.com/']
def parse(self, response):
# xpath为response中的方法
odiv = response.xpath('//div[@class="recommend-article"]//li')
print(odiv)
con_lst = []
for li in odiv:
# 连续的li里面混有广告,屏蔽掉广告
try:
author = li.xpath('.//div[@class="recmd-right"]/a/text()')[0].extract()
title = li.xpath('.//span[@class="recmd-name"]/text()')[0].extract()
dic = {'标题':title, '作者':author}
con_lst.append(dic)
except:
continue
return con_lst

将其保存为xml格式,运行可以看到

爬虫框架之Scrapy(一)

<?xml version="1.0" encoding="utf-8"?>
<items>
<item><标题>尛鑫惢</标题><作者>你是真的皮</作者></item>
<item><标题>金小莲</标题><作者>哥,嫂子说原谅你了,回去吧!</作者></item>
<item><标题>逗太兔</标题><作者>女朋友生气的样子</作者></item>
<item><标题>聊天不撩...</标题><作者>公司车库门口这几天一直大黄蜂在飞,原来是靠近门边有个蜂巢,我让老张去拿个挮子,把蜂巢处理了。老张去搬*,我回去拿了二个头盔,然后两人全付武装起来了。*架好,</作者></item>
<item><标题>湘雅婷</标题><作者>绳子想挽救你一把,可惜拽的不是地方啊,绳子本想拽你脖子的</作者></item>
<item><标题>我打豆豆...</标题><作者>我驾照是98年。教练车是东风141那种老款大货车。刚学大家离合都用不好老是灭车,教练怕车亏电就让用摇把摇车。女生摇不动都是男生替。有天有个女孩熄火了,一个男生就</作者></item>
<item><标题>许诺的蛋...</标题><作者>智障女友的智障日常非要捉迷藏,我不去找,给我教育了一顿,让我深刻的检讨了下自己的问题。是我错了!</作者></item>
<item><标题>糗百精华...</标题><作者>游着游着就爱了</作者></item>
<item><标题>夜半无人...</标题><作者>就这就是我们要的英伦乡村贵族爱情</作者></item>
<item><标题>零點糗事</标题><作者>真搞不懂,哥们为什么大婚当日就带着老婆去荒岛上了</作者></item>
<item><标题>.遇见更...</标题><作者>爸妈吵架,老爸一怒之下把电视砸了,老妈也把微波炉砸了,没几天就和好了。我问老爸当时干嘛砸电视,老爸说,早想换一台大的了,你妈一直不肯。我后来问老妈,干嘛砸微波炉</作者></item>
<item><标题>有什么呀...</标题><作者>谈谈感受吧</作者></item>
<item><标题>零點糗事</标题><作者>网友盘点世界上最聪明的狗狗,看完你是不是这么觉得</作者></item>
<item><标题>尛鑫惢</标题><作者>校长说经费有限……</作者></item>
<item><标题>欢乐二哈</标题><作者>你们怕老婆也不用这样吧!</作者></item>
<item><标题>砸妳家玻...</标题><作者>宁错怪好人,也不错放坏人</作者></item>
<item><标题>又一盏素...</标题><作者>小丽说“男友一两个月不主动联系我,是几个意思?”她其中一个朋友说“大家回答得都太阴暗,什么劈腿变心从来没爱过你之类的,凡事要往好的方面想。我猜你男朋友应该是死了</作者></item>
<item><标题>蜀南熟男</标题><作者>狗狗:TMD居然会功夫,老子不打了!</作者></item>
<item><标题>包袱侠</标题><作者>在俄罗斯,狮子被分配在可爱动物触摸区</作者></item>
<item><标题>笨小孩(...</标题><作者>中国武侠小说三大宗师陨落殆尽,从此,再无江湖,亦无武侠。飞雪连天射白鹿,笑书神侠倚碧鸳。金老走好!</作者></item>
</items>

Scrapy中selenium的使用

在通过Scrapy框架进行某些网站的数据爬取的时候,往往会碰到页面数据动态加载的情况发生,如果直接使用Scrapy对其url发送请求,是绝对获取不到动态加载出来的数据,但是浏览器进行url发送会加载出对应的动态数据。

如果我想要在scrapy也获取动态加载出的数据,则必须使用selenium创建浏览器对象,然后通过该浏览器对象进行请求发送,获取动态加载的数据值。

爬取网易新闻国内板块

https://news.163.com/domestic/

分析:当点击国内超链进入国内对应的页面时,会发现当前页面展示的新闻数据是被动态加载出来的,如果直接通过程序对url进行请求,是获取不到动态加载出的新闻数据的。则就需要我们使用selenium实例化一个浏览器对象,在该对象中进行url的请求,获取动态加载的新闻数据。

通过上面的Scra流程可以分析出:

1 当引擎将国内板块url对应的请求提交给下载器后,下载器进行网页数据的下载,然后将下载到的页面数据,封装到response中,提交给引擎,引擎将response在转交给Spiders。

2 Spiders接受到的response对象中存储的页面数据里是没有动态加载的新闻数据的。

3 要想获取动态加载的新闻数据,则需要在下载中间件中对下载器提交给引擎的response响应对象进行拦截,切对其内部存储的页面数据进行篡改,修改成携带了动态加载出的新闻数据,然后将被篡改的response对象最终交给Spiders进行解析操作。

selenium在Scrapy中的使用流程

1 重写爬虫文件的构造方法,在方法中使用selenium实例化一个浏览器对象。(因为浏览器对象只需要被实例化一次)

2 重写爬虫文件的close方法,在其内部关闭浏览器,该方法在爬虫结束时被调用。

3 重写下载中间件的process_response,让该方法对响应进行拦截,并篡改response中存储的页面数据

4 在配置文件中开启下载中间件

代码示例

wangyi.py

class WangyiSpider(scrapy.spider):
name = 'wangyi'
#allowed_domains = ['www.xxxx.com']
start_urls = ['https://news.163.com']
def __init__(self):
#实例化一个浏览器对象(实例化一次)
self.bro = webdriver.Chrome(executable_path='/Users/bobo/Desktop/chromedriver') #必须在整个爬虫结束后,关闭浏览器
def closed(self,spider):
print('爬虫结束')
self.bro.quit()

中间件文件:

from scrapy.http import HtmlResponse
#拦截到响应对象(下载器传递给Spider的响应对象)
#request:响应对象对应的请求对象
#response:拦截到的响应对象
#spider:爬虫文件中对应的爬虫类的实例
def process_response(self, request, response, spider):
#响应对象中存储页面数据的篡改
if request.url in['http://news.163.com/domestic/','http://news.163.com/world/','http://news.163.com/air/','http://war.163.com/']:
spider.bro.get(url=request.url)
js = 'window.scrollTo(0,document.body.scrollHeight)'
spider.bro.execute_script(js)
time.sleep(2) #一定要给与浏览器一定的缓冲加载数据的时间
#页面数据就是包含了动态加载出来的新闻数据对应的页面数据
page_text = spider.bro.page_source
#篡改响应对象
return HtmlResponse(url=spider.bro.current_url,body=page_text,encoding='utf-8',request=request)
else:
return response 

settings.py

DOWNLOADER_MIDDLEWARES = {
'happy1.middlewares.Happy1DownloaderMiddleware': 543,
}

scrapy框架的持久化存储

基于终端指令的持久化存储

条件:保证parse文件中有可迭代类型对象的返回,该返回值可以通过终端指令的方式写入指定格式的文件中进行持久化操作。

指定输出格式进行存储    
scrapy crawl 爬虫名称 -o xxx.json
scrapy crawl 爬虫名称 -o xxx.xml
scrapy crawl 爬虫名称 -o xxx.csv
基于管道的持久化存储

主要作用的两个文件:

  1 iems.py:数据结构模板文件,主要定义数据属性。

  2 pipelines.py:管道文件,接收数据,进行持久化操作。

持久化操作流程:

  1 爬虫文件爬取到数据后,需要将数据封装到item对象中

  2 使用yield关键字将items对象提供给pipelines对象进行持久化操作

  3 在管道文件中的process_item方法中接收爬虫文件提供过来的items对象,然后编写代码进行持久化存储

  4 设置settings.py

例子 上面糗事百科 本地存储

qiushi.py

# -*- coding: utf-8 -*-
import scrapy
from happy.items import HappyItem class QiushiSpider(scrapy.Spider):
name = 'qiushi'
allowed_domains = ['www.qiushibaike.com']
start_urls = ['http://www.qiushibaike.com/']
def parse(self, response):
# xpath为response中的方法
odiv = response.xpath('//div[@class="recommend-article"]//li')
for li in odiv:
# 连续的li里面混有广告,屏蔽掉广告
try:
title = li.xpath('.//div[@class="recmd-right"]/a/text()')[0].extract()
author = li.xpath('.//span[@class="recmd-name"]/text()')[0].extract() except:
continue
# 提供item到管道文件
item = HappyItem()
item['title'] = title
item['author'] = author
yield item

items.py

import scrapy

class HappyItem(scrapy.Item):
# define the fields for your item here like:
# name = scrapy.Field()
# 储存标题
title = scrapy.Field()
# 储存作者
author = scrapy.Field()

pipelines.py

class HappyPipeline(object):
# 构造方法
def __init__(self):
# 定义一个文件描述符属性
self.fp = None # 下面都是重写父类的方法
# 开始爬虫时,执行一次
def open_spider(self, spider):
print('爬虫开始!')
self.fp = open('data.txt', 'w', encoding='utf-8') # 因为该方法会被调用多次,所以文件的开启和关闭都被写在了另外2个只会执行一次的方法中
def process_item(self, item, spider):
self.fp.write(item['title'] + ':' + item['author'] + '\n')
return item #爬虫结束时执行一次
def close_spider(self, spider):
self.fp.close()
print('爬虫结束')

settings.py

ITEM_PIPELINES = {
'happy.pipelines.HappyPipeline': 300,
}

例子 上面糗事百科 mysql存储

新建数据库qiushi和表qiushi,我使用的是navicat

爬虫框架之Scrapy(一)

修改pipelines.py

import pymysql

class QiubaiPipelineByMysql(object):
# mysql的连接对象声明
con = None
# 右表声明
cursor = None
# 下面都是重写父类的方法
# 开始爬虫时,执行一次
def open_spider(self, spider):
print('爬虫开始!')
# 连接数据库
self.con = pymysql.Connect(host='127.0.0.1', port=3306, user='root', password='', db='qiushi') # 因为该方法会被调用多次,所以文件的开启和关闭都被写在了另外2个只会执行一次的方法中
def process_item(self, item, spider):
# sql语句
sql = 'insert into qiushi values("%s","%s")'%(item['title'], item['author'])
# 创建游标
self.cursor = self.con.cursor()
# 执行事务
try:
self.cursor.execute(sql)
self.con.commit()
except Exception as e:
print(e)
# 事务回滚
self.con.rollback()
return item #爬虫结束时执行一次
def close_spider(self, spider):
# 关闭连接
self.cursor.close()
# 关闭游标
self.con.close()
print('爬虫结束')

修改settings.py 把最后的类名字换了

ITEM_PIPELINES = {
'happy.pipelines.QiubaiPipelineByMysql': 300,
}

得到结果:

爬虫框架之Scrapy(一)

例子 糗事百科 redis存储

我使用的是redis的可视化软件

爬虫框架之Scrapy(一)

pipelines.py

import redis

class QiubaiPipelineByRedis(object):
# redis的连接对象声明
con = None
# 下面都是重写父类的方法
# 开始爬虫时,执行一次
def open_spider(self, spider):
print('爬虫开始!')
# 连接数据库
self.con = redis.Redis(host='127.0.0.1', port=6379, db=0, decode_responses=True) # 因为该方法会被调用多次,所以文件的开启和关闭都被写在了另外2个只会执行一次的方法中
def process_item(self, item, spider):
dic = {'title':item['title'], 'author':item['author']}
self.con.lpush('data', str(dic))
return item #爬虫结束时执行一次
def close_spider(self, spider):
print('爬虫结束')

settings.py

ITEM_PIPELINES = {
'happy.pipelines.QiubaiPipelineByRedis': 300,
}

得到结果:

爬虫框架之Scrapy(一)

持久化存储扩展

如果最终需要将爬取到的数据值一份存储到磁盘文件,一份存储到mysql数据库中,一份存储到redis中该,该如何操作?

pipelines.py中的操作:

# redis操作
class QiubaiPipelineByRedis(object):
pass
#mysql操作
class QiubaiPipelineByMysql(object):
pass
#本地操作
class HappyPipeline(object):
pass

settings.py的操作:

ITEM_PIPELINES = {
'happy.pipelines.QiubaiPipelineByRedis': 300,
'happy.pipelines.QiubaiPipelineByMysql': 300,
'happy.pipelines.HappyPipeline': 300,
}

爬虫框架之Scrapy(一)的更多相关文章

  1. 06 爬虫框架:scrapy

    爬虫框架:scrapy   一 介绍 Scrapy一个开源和协作的框架,其最初是为了页面抓取 (更确切来说, 网络抓取 )所设计的,使用它可以以快速.简单.可扩展的方式从网站中提取所需的数据.但目前S ...

  2. Python-S9-Day125-Web微信&amp&semi;爬虫框架之scrapy

    01 今日内容概要 02 内容回顾:爬虫 03 内容回顾:网络和并发编程 04 Web微信之获取联系人列表 05 Web微信之发送消息 06 为什么request.POST拿不到数据 07 到底使用j ...

  3. 九、爬虫框架之Scrapy

    爬虫框架之Scrapy 一.介绍 二.安装 三.命令行工具 四.项目结构以及爬虫应用简介 五.Spiders 六.Selectors 七.Items 八.Item Pipelin 九. Dowload ...

  4. 洗礼灵魂,修炼python(72)--爬虫篇—爬虫框架:Scrapy

    题外话: 前面学了那么多,相信你已经对python很了解了,对爬虫也很有见解了,然后本来的计划是这样的:(请忽略编号和日期,这个是不定数,我在更博会随时改的) 上面截图的是我的草稿 然后当我开始写博文 ...

  5. 爬虫框架之Scrapy

    一.介绍 二.安装 三.命令行工具 四.项目结构以及爬虫应用简介 五.Spiders 六.Selectors 七.Items 八.Item Pipelin 九. Dowloader Middeware ...

  6. 爬虫框架之Scrapy(四 ImagePipeline)

    ImagePipeline 使用scrapy框架我们除了要下载文本,还有可能需要下载图片,scrapy提供了ImagePipeline来进行图片的下载. ImagePipeline还支持以下特别的功能 ...

  7. 爬虫框架:scrapy

    一 介绍 Scrapy一个开源和协作的框架,其最初是为了页面抓取 (更确切来说, 网络抓取 )所设计的,使用它可以以快速.简单.可扩展的方式从网站中提取所需的数据.但目前Scrapy的用途十分广泛,可 ...

  8. 爬虫框架之Scrapy(三 CrawlSpider)

    如何爬取一个网站的全站数据? 可以使用Scrapy中基于Spider的递归方式进行爬取(Request模块回调parse方法) 还有一种更高效的方法,就是基于CrawlSpider的自动爬取实现 简介 ...

  9. 爬虫框架之Scrapy(二)

    递归解析 糗事百科递归解析 在前面的例子里只是爬取了糗事百科热门的第一个页面,但是当我们需要爬取更多的页面时,需要对每个页面的url依次发起请求,然后通过解析的方法进行作者和标题的解析. 我们可以构建 ...

随机推荐

  1. TCP连接状态与2MSL等待时间

    1 连接状态图 2 建立连接:三次握手,不使用DNS和使用DNS 3 关闭连接-四次握手 连接双方任何一方调用close()后,连接的两个传输方向都关闭,不能再发送数据了.如果一方调用shutdown ...

  2. 查看特定View的默认属性值

    当我在分析focus.touch事件处理代码时发现,有些属性对代码的逻辑有非常重要的影响,比如clickable.focusable 这些属性.这时我们自然而然的想到,那么这些属性的默认值是什么呢?在 ...

  3. delphi 10 seattle 安卓服务开发&lpar;三&rpar;

    delphi 10 里面的安卓服务有四种,上面的一篇文章里面的图有介绍. 今天做一个remote service 的例子.(里面一部分代码是抄别人的,如果不太清楚,自行恶补) remote servi ...

  4. Microsoft Office下载地址

    文件名: cn_office_professional_plus_2016_x86_x64_dvd_6969182.iso 语言: Chinese – Simplified 文件大小:2.41 GB ...

  5. PhpStorm 获取注册码

    另一种方法是:直接用浏览器打开 http://idea.lanyus.com/ ,点击页面中的“获得注册码”,然后在注册时切换至Activation Code选项,输入获得的注册码一长串字符串,便可以 ...

  6. 开始使用 HBuilder 和 Mui - 1 - 分析 index&period;html &semi;

    转自:http://ask.dcloud.net.cn/article/240 好吧,在比较了 Codenameone 和 HBuilder 以后,俺反复考虑后,终于还是决定使用 HBuilder 这 ...

  7. HashTab---Windows资源管理器的文件属性窗口中添加了一个叫做”文件校验”的标签

    HashTab 是一个优秀的 Windows 外壳扩展程序,它在 Windows 资源管理器的文件属性窗口中添加了一个叫做”文件校验”的标签.该标签可以帮助你方便地计算文件的 MD5.SHA1 与 C ...

  8. pureftpd&period;passwd解析

    将pureftpd.passwd文件的内容转换成sql语句,导入到mysql pureftp.passwd格式: <account>:<password>:<uid&gt ...

  9. jxl获取excel中的合并的单元格&lpar;主要是方法介绍&rpar;

    Range[] rangeCells = sheet.getMergedCells();// 返回sheet中合并的单元格数组 for (Range r : rangeCells) {//对数组遍历拿 ...

  10. 阿里2016实习offer五面经验与总结

    前言 眼下楼主已经拿到阿里实习offer,一共经历了5次面试,当中4轮技术面.1轮HR面试.在这里分享一下自己的面试经验和学习总结.写这篇面经主要是希望可以帮助很多其它的小伙伴.我本科毕业于中南大学信 ...