Scrapy爬虫Section1
了解更多爬虫知识,点击访问我的博客!!!
认识Scrapy爬虫
网络爬虫是指在互联网上自动爬取网站内容信息的程序,也被称作网络蜘蛛或网络机器人。大型的爬虫程序被广泛应用于搜索引擎、数据挖掘等领域,个人用户或企业也可以利用爬虫收集对自身有价值的数据。
网络爬虫的基本执行流程
-
下载页面 一个网页的内容本质上就是一个HTML文本,爬取一个网页内容之前,首先要根据网页的URL下载网页。 -
提取页面数据 当一个网页(HTML)下载完成后,对页面中的内容进行分析,并提取出我们感兴趣的数据,提取到的数据可以以多种形式保存起来,比如将数据以某种格式(CSV、JSON)写入文件中,或存储到数据库(MySQL、MongoDB)中。 -
提取页面链接 通常,我们想要获取的数据并不只在一个页面中,而是分布在多个页面中,这些页面彼此联系,一个页面中可能包含一个或多个到其他页面的链接,提取完当前页面中的数据后,还要***把页面中的某些链接也提取出来***,然后对链接页面进行爬取(循环1-3步骤)。
使用Python可以从头开始写一个爬虫程序,为了避免因制造轮子 而消耗大量时间,在实际应用中我们可以选择使用一些优秀的爬虫框 架,使用框架可以降低开发成本,提高程序质量。所以就有了***Scrapy***的出现。
Scrapy的安装
pip install scrapy
出现图示则代表安装成功。
编写Spider(Scrapy 爬虫程序中最核心的组件)
Scrapy框架结构及工作原理
对***各个组件***的介绍如下图所示
对框架中的***数据流***介绍如图所示
Request和Response是HTTP协议中的术语,即HTTP请求和 HTTP响应,Scrapy框架中定义了相应的Request和Response类,这里的Item代表Spider从页面中爬取的一项数据。
数据在框架中的流动过程
-
当SPIDER要爬取某URL地址的页面时,需使用该URL构造一个Request对象,提交给ENGINE。 -
Request对象随后进入SCHEDULER按某种算法进行排队,之后的某个时刻SCHEDULER将其出队,送往DOWNLOADER。 -
DOWNLOADER根据Request对象中的URL地址发送一次HTTP请求 到网站服务器,之后用服务器返回的HTTP响应构造出一个Response对象,其中包含页面的HTML文本。 -
Response对象最终会被递送给SPIDER的页面解析函数(构造Request对象时指定)进行处理,页面解析函数从页面中提取数据,封装成Item后提交给ENGINE,Item之后被送往ITEM PIPELINES进行处理,最终可能由EXPORTER以某种数据格式写入文件(csv,json);另一方面,页面解析函数还从页面中提取链接(URL),构造出新的 Request对象提交给ENGINE。
Request对象
Request对象用来描述一个HTTP请求,下面是其构造器方法的参数列表:
Request(url[, callback, method=‘GET’, headers, body, cookies, meta, encoding=‘utf-8’, priority=0, dont_filter=False,errback,flags])
Request参数介绍:
虽然参数很多,但除了url参数外,其他都带有默认值。在构造Request对象时,通常我们只需传递一个url参数或再加一个callback参数,其他使用默认值即可。
>>> request = scrapy.Request(‘http://books.toscrape.com/’)
Response对象
Response对象用来描述一个HTTP响应,Response只是一个基类,根据响应内容的不同有如下子类:
- TextResponse
- HtmlResponse
- XmlResponse
当一个页面下载完成时,下载器依据HTTP响应头部中的 Content-Type信息创建某个Response的子类对象。我们通常爬取的网页,其内容是HTML文本,创建的便是HtmlResponse对象,其 中***HtmlResponse和XmlResponse是TextResponse的子类***。实际上,这3个子类只有细微的差别
?
HtmlResponse对象的属性与方法
Spider开发流程
爬虫开发的逻辑:
import scrapy
from ..items import BookItem
from scrapy.linkextractors import LinkExtractor
class BooksSpider(scrapy.Spider):
name = 'books'
def start_requests(self):
yield scrapy.Request('http://books.toscrape.com/',
callback=self.parse,
headers={'User-Agent':'Mozill/5.0'},
dont_filter=True)
def parse(self, response):
for sel in response.css('article.product_pod'):
book = BookItem()
book['name'] = sel.xpath('./h3/a/@title').extract_first()
book['price'] = sel.css('p.price_color::text').extract_first()
yield book
'''
<ul class="pager">
<li class="current"> Page 1 of 50 </li>
<li class="next"><a href="catalogue/page-2.html">next</a></li>
</ul>
#default > div > div > div > div > section > div:nth-child(2) > div > ul.pager > li.next > a
'''
le = LinkExtractor(restrict_css='ul.pager li.next')
links = le.extract_links(response)
if links:
next_url = links[0].url
yield scrapy.Request(next_url, callback=self.parse)
继承scrapy.Spider
Scrapy框架提供了一个Spider基类,我们编写的Spider需要继承它
import scrapy
class BooksSpider(scrapy.Spider):
这个Spider基类实现了以下内容:
- 供Scrapy引擎调用的接口,例如用来创建Spider实例的类方法from_crawler。
● 供用户使用的实用工具函数,例如可以调用log方法将调试信息输出到日志。
● 供用户访问的属性,例如可以通过settings属性访问配置文件中的配置。
为Spider命名
在一个Scrapy项目中可以实现多个Spider,每个Spider需要有一个能够区分彼此的唯一标识,Spider的类属性name便是这个唯一标识。
class BooksSpider(scrapy.Spider):
name = "books"
...
执行scrapy crawl命令时就用到了这个标识,告诉Scrapy使用哪个Spider进行爬取。
设定起始爬取点
Spider必然要从某个或某些页面开始爬取,我们称这些页面为***起始爬取点***,可以通过类属性start_urls来设定起始爬取点:
class BooksSpider(scrapy.Spider):
...
start_urls = ['http://books.toscrape.com/']
...
start_urls通常被实现成一个列表,其中放入所有起始爬取点的url。
请求页 面下载一定要提交Request对象,这时候,我们仅仅定义了URL列表,Spider基类的start_requests方法帮助我们基于URL构造并提交了Request对象。
class Spider(object_ref):
...
def start_requests(self):
for url in self.start_urls:
yield self.make_requests_from_url(url)
def make_requests_from_url(self, url):
return Request(url, dont_filter=True)
def parse(self, response):
raise NotImplementedError ..
由于起始爬取点的下载请求是由引擎调用Spider对象的 start_requests方法产生的,因此我们也可以在BooksSpider中实现start_requests方法(覆盖基类Spider的start_requests方法),直接构造并提交起始爬取点的Request对象。在某些场景下使用这种方式更加灵活,例如有时想为Request添加特定的HTTP请求头部,或想为Request指定特定的页面解析函数
实现页面解析函数
页面解析函数也就是构造Request对象时***通过callback***的回调函数(或默认的parse方法)。页面解析函数是实现Spider中最核心的部分,它需要完成以下两项工作:
一个页面中可能包含多项数据以及多个链接,因此页面解析函数被要求返回一个可迭代对象(通常被实现成一个生成器函数),每次迭代返回一项数据(Item或字典)或一个Request对象。
|