python爬⾍框架——scrapy
scrapy
流程图
Scrap Engine(引擎)
负责控制数据流在系统中所有组件中流动,并在相应动作发⽣时触发事件,是整个爬⾍的调度中⼼。
调度器( Scheduler)
调度器接收从引擎发送过来的 request,并将他们加⼊到爬取队列,以便之后引擎请求他们时提供给引擎。初始的爬取URL和后续在页⾯中获取的待爬取的URL将放⼊调度器中,等待引擎得统⼀调度爬取。同时调度器会⾃动去除重复的URL(如果特定的URL不需要去重也可以通过设置实现,如ρost请求的URL)
下载器( Downloader)
下载器负责获取页⾯数据并提供给引擎,⽽后将获取得response信息提供给 spider。
Spiders爬⾍
Spider是编写的类,作⽤如下:
编写⽤于分析 response并提取item即获取到的item)
分析页⾯中得url,提交给 Scheduler调度器继续爬取。
由于⽹站页⾯内容结构不同,⼀个spider⼀般负责处理⼀个(或⼀些)特定的⽹站。多个⽹站可以使⽤多个spider分别进⾏爬取。
Item pipeline
页⾯中饿内容被提取出来封装到⼀个数据结构中,即⼀个item,每⼀个item被发送到项⽬管道( Pipeline),并经过设置好次序的pipeline程序处理这些数据,最后将存⼊本地⽂件或存⼊数据库持久化。
item pipeline的⼀些典型应⽤
处理HTML数据
验证爬取的数据(检查item包含某些字段)
查重(或丢弃)
将爬取结果保存到数据库中
下载器中间件(Downloader middlewares)
下载器中间件是在引擎和下载器之间的特定钩⼦(specific hook),在下载进⾏下载前,以及下载完成返回数据的阶段进⾏拦截,处理请求和响应。它提供了⼀个简便的机制,通过插⼊⾃定义代码来扩展 Scrapy功能,通过设置下载器中间件可以实现爬⾍⾃动更换 user-agent、实现IP代理功能等功能。
Spider中间件( Spider middlewares)
Spider中间件,是在引擎和 Spider之间的特定钩⼦,处理 spider的输⼊response和输出(items或 requests)
安装
scrapy使⽤Twisted基于事件的⾼效异步⽹络框架来处理⽹络通信,可以加快下载速度,使⽤pip安装scrapy时会⾃动解决安装依赖,在windows下如果安装Twisted出现问题,⼿动下载编译好的Twisted包安装即可。
安装wheel⽀持:pip install wheel
安装scrapy框架:pip install scrapy
基本使⽤
创建项⽬
scrapy提供了命令快速的创建⼀个项⽬,⾃动⽣成项⽬框架
scrapy startproject <pro_name> . # 在当前⽬录创建项⽬
创建后项⽬⽬录如下:
pro_name/ # 项⽬⽬录
scrapy.cfg # 必要的配置⽂件
pro_name/ # 项⽬全局⽬录
spiders/ # 爬⾍类
__init__.py
__init__.py
items.py # item,定义数据结构储存数据
middlewares.py # 中间键类
pipelines.py # 管道
setting.py # 全局重要的设置
setting中的设置
BOT_NAME = 'spider_name'# 爬⾍名
SPIDER_MODULES = ['pro_name.spiders'] # 爬⾍模块
USER_AGENT # UA,配置
ROBOTSTXT_OBEY = False # 是否遵从robots协议
# CONCURRENT_REQUESTS = 32 # 最⼤请求数,默认16
# COOKIES_ENABLED = False # ⼀般登录时候使⽤cookie
# DEFAULT_REQUEST_HEADERS = { # 可定义请求头
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
'Accept-Language': 'en',
}
# 两个中间件,指定为模块中的类,将会⾃动按照优先级依次调⽤
# SPIDER_MIDDLEWARES = { # 中间键类们,key为模块位置,value为优先级,越⼩越优先
'pro_name.middlewares.Pro_nameSpiderMiddleware': 543,
}
# DOWNLOADER_MIDDLEWARES = {
'pro_name.middlewares.Pro_nameDownloaderMiddleware': 543,
}
ITEM_PIPELINES = { # 管道处理,指定处理类,按照顺序调⽤
'wuhan.pipelines.PeoplePipeline': 300,
}
Item
item可以看作是⼀个储存数据的数据结构,定义我们需要爬取内容的数据字段⽤于储存数据并将每⼀条数据封装为⼀个Item对象,再由scrapy核⼼交由pipelines依次处理即可。假如我们需要爬取的每⼀条招聘内容,包含了公司名,⼯作地点,薪资等字段数据,我们可以定义⼀个数据结构Item去储存这些数据
import scrapy
class workItem(scrapy.Item):
cp_name = scrapy.Field() # class scrapy.Field(dict): 继承于dict
cp_addr = scrapy.Field()
salary = scrapy.Field()
该类继承scrapy中的内部类,定义的属性值为⼀个scrapy.Field()实例,实际为⼀个字典对象。
爬⾍类
爬⾍类中分为两部分⼯作,提供想要爬取的url和解析返回的response信息。scrapy提供了模块快速的创建⼀个模板。scrapy genspider -t basic name domain -t指定模板的类型为basic,name为创建的py⽂件名,执⾏后将会在spider⽬录中⽣成⽂件name.py⽂件,⽂件内容如下。
# -*- coding: utf-8 -*-
import scrapy
class DomainSpider(scrapy.Spider):
name = 'spider_name'# 爬⾍名
allowed_domains = ['domain'] # 爬取的内容必须在这些域下,否则爬取
start_urls = ['domain/'] # 起始的url
# 下载器下载了⽬标页⾯的内容,封装为response对象并注⼊到response参数中,
def parse(self, response): # 返回的response值,类型为sponse.html.HtmlResponse
pass# 使⽤参数注解⽅便了解该对象的⽅法
parse⽅法中负责对response中的内容进⾏数据处理,⼀般包括数据数据提取和url提取,数据使⽤Item进⾏封装,url继续交由调度器进⾏再次继续进⾏访问。
解析response
返回的数据被scrapy封装为⼀个HtmlResponse对象,他是⼀个response对象的⼀个⼦类。
def parse(self, response):
, response.body, response.headers) # ⽂本内容和头信息
为了⽅便页⾯内容的提取,scrapy包装了lxml,并可以通过调⽤response的⽅法直接调⽤,并通过xpath语法进⾏提取。直接调⽤xpath⽅法即可
def parse(self, response):
tag = response.xpath("//xpath") # tag为提取html的标签封装的xpath对象,可以继续调⽤xpath
同样还⽀持css选择器,调⽤css⽅法即可
def parse(self, response):
css_tag = respons.css("li a::text")
Item封装数据
from ..items import WorkItem # 使⽤相对导⼊
def parse(self, response):
# 使⽤xpath 或者 css选择器提取到了内容
cp_name = response.xpath("//cp_name").extract()
cp_addr = response.xpath("//cp_addr").extract()
salary = response.xpath("//salary").extract()
# 实例化item对象
item = WorkItem
item["cp_name"] = cp_name # 将数据对应封装到item对象属性中。
item["cp_addr"] = cp_addr
item["salary"] = salary
return [item] # return值将会交给pipeline处理,需要⼀个可迭代对象
# yield item # 或者使⽤yield每次返回⼀个item,返回⼀个可迭代对象即可
# 该数据也可以通过命令直接保存到指定的⽂件中
# 命令⾏执⾏ scrapy crwal -h 查看帮助
# --output 或者 -o "path/to/" 存⼊指定的⽂件
# 例如 scrapy crawl -o "tmp/data/work.json" # ⽀持的⽂件格(json,csv, xml, marshal, pickle)
pipeline
每⼀个pipeline会依次处理返回的每⼀个item数据,多个pipeline同时存在可以设置优先级。使⽤pipeline只需要将对应pipeline类在setting列表中注册并指定优先级即可,scrapy将会依次调⽤这些pipeline⼯作。
在项⽬⽂件中有pipeline.py⽂件,在内部可根据需要定义pipeline。定义完成必须在setting.py的pipelines列表中声明即可。
开启并添加pipeline
ITEM_PIPELINES = {'pro_name.pipelines.TestPipeline':500} # 指定这个pipeline的模块位置进⾏注册
⾃定义pipeline
每⼀个pipeline类可以定以下⽅法,如果指定,scrapy将在对应的时刻调⽤这些⽅法执⾏啊。
class AaaPipeline(object):
def__init__(self): # pipeline创建时候调⽤⼀次
print("init +++++++++++++++")
def open_spider(self, spider):
print("open_spider ++++++++++++++++++") # spider开启时调⽤⼀次
print(spider, type(spider)) # 该spider对象即为spider⽂件中的DomainSpider对象
return"open ===="
def process_item(self, item, spider): # 必须定义该⽅法
# 处理⼀条条item的⽅法
# spider类中的parse⽅法每返回⼀条item数据,该⽅法调⽤⼀次。
print(item)
print(spider, type(spider))
return item
def close_spider(self, spider): # spider关闭时候调⽤
print("close_spider ++++++++++++++++++")
print(spider, type(spider))
return"close ===="
process_item⽅法负责对每个item对象进⾏处理,可以将数据保存存到磁盘或者数据库中,open_spider和close_spider⽅法通常⽤于创建和关闭数据库连接或者⽂件对象等操作。
url提取
我们需要从页⾯中提取下⼀页的url,再次进⾏爬取,在之前的parse函数中完成这个任务。
import scrapy
class Spider(scrapy.Spider):
name = "domain"
start_utl = ""
allowed_domain = []
def parse(self, response):
urls = response.xpath("//div[@class='paginator']/span[@class='next']/a/@href")
.re(r'start=/d+') # 匹配url使⽤正则过滤
# 将每个url拼接为为全路径,并封装为Request对象返回
yield from (scrapy.Request(response.urljoin(url)) for url in urls)
提取的url只需要将其封装为scrapy.Request对象进⾏返回,scrapy将会将其作为请求交给调度器,并由下载器再次发送请求。调度器对url⾃动有去重功能,重复的url提交给调度器,将会⾃动去重。
多页⾯定向爬取
如果该⽹站有多个⽹页需要进⾏爬取,但是页⾯的内容结构差异较⼤,这样⼀个parse函数将不能够很好的解析统⼀的解析。我们需要对页⾯进⾏分类。通过url的差异,使得不同类的内容执⾏不同解析pars
e函数,各⾃完成解析。
实现这个功能,scrapy提供了另⼀个模板-crawl,如同basic模板的创建⽅式
scrapy genspider -t crawl name domian进⾏创建,创建后内容如下:
import scrapy
from scrapy.linkextractors import LinkExtractor
from scrapy.spiders import CrawlSpider, Rule
class $classname(CrawlSpider): # CrawlSpider是Spider的⼦类
name = '$name'
allowed_domains = ['$domain']
start_urls = ['$domain/']
# 定义了⼀个规则,从response页⾯中提取指定的链接,链接可以匹配allow="Items/",将会爬取链接页⾯执⾏会执⾏callback函数
#
rules = (
Rule(LinkExtractor(allow=r'Items/',), callback='parse_item', follow=True),
。。。
)
def parse_item(self, response):
item = {}
#item['domain_id'] = response.xpath('//input[@id="sid"]/@value').get()
#item['name'] = response.xpath('//div[@id="name"]').get()
#item['description'] = response.xpath('//div[@id="description"]').get()
return item
awl.CrawlSpider是scrapy.spider.Spider的⼦类,增强了功能,在其中可以使⽤LinkExtractor,Rule
Rule的规则
rules元组中可以定义多条规则,每⼀条规则映射⼀个callback⽤于处理
LinkExtractor对象⽤于过滤url,其中参数包括:
allow参数传⼊⼀个正则字符串或者多个正则字符串组成可迭代对象,该正则字符串只会匹配页⾯的中的<a>中的href属性的值。将会提取页⾯中所有的href进⾏匹配。
deny:于allow相反,匹配的拒绝访问
allow_domain:允许域
deny_domain:拒绝域
follow:是否继续跟进链接。
爬取执⾏过程
执⾏爬⾍开始爬取过程,从打印的⽇志中可以看到相关的信息。scrapy list 可以列出当前存在的爬⾍名,使⽤scrapy crwal spider_name开始爬取c:\\users\username\python\aaa> scrapy crawl spider_name # 执⾏命令,开始scrapy进⾏爬取
2020-03-31 11:31:32 [scrapy.utils.log] INFO: Scrapy 1.8.0 started (bot: aaa)scrapy分布式爬虫
# 加载的库
2020-03-31 11:31:32 [scrapy.utils.log] INFO: Versions: lxml 4.4.2.0, libxml2 2.9.5, csssel
ect 1.1.0, parsel 1.5.2, w3lib 1.21.0, Twisted 19.10.0, Python 3.6.6 (v3.6.6:4cf1f54eb7, J
un 27 2018, 03:37:03) [MSC v.1900 64 bit (AMD64)], pyOpenSSL 19.1.0 (OpenSSL 1.1.1d 10 Se
p 2019), cryptography 2.8, Platform Windows-10-10.0.18362-SP0
# 爬⾍的初始化配置值,例如user_agent信息。
2020-03-31 11:31:32 [awler] INFO: Overridden settings: {'BOT_NAME': 'aaa', 'NEWSP
IDER_MODULE': 'aaa.spiders', 'SPIDER_MODULES': ['aaa.spiders'], 'USER_AGENT': 'Mozilla/5.0
(Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safar
i/537.36'}
2020-03-31 11:31:32 [lnet] INFO: Telnet Password: 8e9ba500a3d20796
# 中间件信息,这⾥没有使⽤,加载了默认的⼏个中间件
2020-03-31 11:31:32 [scrapy.middleware] INFO: Enabled extensions:
['stats.CoreStats',
'lnet.TelnetConsole',
'sions.logstats.LogStats']
# 下载器中间件downloadermiddlewares信息
2020-03-31 11:31:32 [scrapy.middleware] INFO: Enabled downloader middlewares:
['scrapy.downloadermiddlewares.httpauth.HttpAuthMiddleware',
'scrapy.downloadermiddlewares.downloadtimeout.DownloadTimeoutMiddleware',
'scrapy.downloadermiddlewares.defaultheaders.DefaultHeadersMiddleware',
'scrapy.downloadermiddlewares.useragent.UserAgentMiddleware',
'RetryMiddleware',
'direct.MetaRefreshMiddleware',
'scrapy.downloadermiddlewares.httpcompression.HttpCompressionMiddleware',
'direct.RedirectMiddleware',
'kies.CookiesMiddleware',
'scrapy.downloadermiddlewares.httpproxy.HttpProxyMiddleware',
'scrapy.downloadermiddlewares.stats.DownloaderStats']
# spidermiddlewares
2020-03-31 11:31:32 [scrapy.middleware] INFO: Enabled spider middlewares:
['scrapy.spidermiddlewares.httperror.HttpErrorMiddleware',
'scrapy.spidermiddlewares.offsite.OffsiteMiddleware',
'ferer.RefererMiddleware',
'scrapy.spidermiddlewares.urllength.UrlLengthMiddleware',
'scrapy.spidermiddlewares.depth.DepthMiddleware']
# ⾄此,配置信息和中间件信息加载完毕,开始执⾏爬取内容
# pipeline 初始化,执⾏了__init__⽅法,这是我们⾃⼰打印的内容
init +++++++++++++++
2020-03-31 11:31:32 [scrapy.middleware] INFO: Enabled item pipelines:
['aaa.pipelines.AaaPipeline']
# pipeline中open_spider⽅法执⾏,spider对象已被创建,即parse⽅法所属spider类
2020-03-31 11:31:32 [ine] INFO: Spider opened
open_spider ++++++++++++++++++
<TestspiserSpider 'testspider' at 0x1e9d2784198> <class'stspiser.Testspiser
Spider'>
2020-03-31 11:31:32 [sions.logstats] INFO: Crawled 0 pages (at 0 pages/min), s
craped 0 items (at 0 items/min)
2020-03-31 11:31:32 [lnet] INFO: Telnet console listening on 127.0.0.1
:6023
2020-03-31 11:31:32 [direct] DEBUG: Redirecting (301) to <G
ET www.douban/> from <GET douban/>
2020-03-31 11:31:32 [direct] DEBUG: Redirecting (301) to <G
ET www.douban/> from <GET www.douban/>
2020-03-31 11:31:33 [ine] DEBUG: Crawled (200) <GET www.douban/
> (referer: None)
# parse解析⽅法执⾏
200 ==================
# process_item⽅法执⾏。
{'age': 123, 'name': 'abc'}
<TestspiserSpider 'testspider' at 0x1e9d2784198> <class'stspiser.Testspiser
Spider'>
2020-03-31 11:31:33 [scraper] DEBUG: Scraped from <200 www.douban/
>
{'age': 123, 'name': 'abc'}
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论