这篇文章主要记录和讲解的是爬虫模块中parse方法的使用,构建Item pipeline,定制自己的的Item pipeline、Settings的设置以及代码的实现。
数据提取(续):
Spider类整体上来看是定义了爬虫爬取的动作(例如页面是否跟进)和分析网页的结构以及从网页的内容中提取结构化的数据item
整体的循环流程:
A.以入口的URL初始化Request,并设置回调函数。此Request下载完毕后返回Response,并作为参数返回给回调函数。spider中初始的Request是通过调用start_requests()方法获取,start_requests()读取start_urls中的URL,并以parse为回调函数生成Request。B.在回调函数中,分析参数Response,返回Item对象、dict、Request或者一个包括三者的可迭代容器。
C.在回调函数内,可以使用选择器(selector)或者其他第三方解释器来分析response,(这里可以为通过requests解析下载的网页内容),并根据分析的数据生成item。
D.通过使用yield而非return返回数据类型,可以将parse打造一个生成器,scrapy会逐一获取parse()方法中生成的结果,并判断生成的结果类型加以处理。
E.处理的过程如下:
- 如果是获取的是Request对象,会经过scrapy处理,下载响应的内容,并调用设置的回调函数,可以使parse()后者其他函数。如果是item类型则使用pipelines处理,其他类型则返回错误信息。
- parse()方法作为回调函数(通过callback来调用响应的方法)赋值给了Request,指定parse()方法来处理这些请求 scrapy.Request(url, callback=self.parse)。Request对象经过调度,执行生成 scrapy.http.response()的响应对象(实际上是对网页的下载),并将返回的Response参数送回给parse()方法,直到调度器中没有Request(递归的思路);
- 在此过程之中, scrapy取到第一部分的Request不会立马就去发送这个Request,只是把这个Request放到队列里,然后接着从生成器里获取;
- 取尽第一部分的Request,然后再获取第二部分的item,取到item后,就会放到对应的pipelines里处理;
- 当所有的返回对象取尽之后,parse()工作结束,引擎再根据Request队列和pipelines中的内容去执行相应的操作;程序在取得各个页面的items前,会先处理完之前所有的Request队列里的请求,然后再提取items。
- 以上的全部过程由Scrapy引擎和调度器负责到底。
scrapy整体架构图示:
再回头看scrapy运行的流程就会比较清晰了
- 首先是获取队列中第一个需要爬取的URL,即是start_urls,通过调度器Schedule和Request进行调度。目的事下载由初始的网页内容。
- 引擎向调度器请求爬取的URL,调度器将该URL返回给引擎。Scrapy Engine会使用Downloader Middlewares 下载中间件对网页进行下载。
- 一旦网页下载完毕,下载器将生成这个网页的Responses,并将其通过Downloader Middlewares 下载中间件发送给引擎。
- 引擎将从下载器中接收到的Response通过Spider Middlewares 返回给Spiders模块中的parse()方法进行处理。
- Spiders中回调函数(不仅仅有parse()也可是自己编写的回调函数)处理响应的Response,以yield返回爬取到的(跟进的)Request和item给引擎。
- 引擎将爬取到的Item交给Item Pipelines,进行数据的后续处理。
- 引擎将爬取到的Request交给调度器,重复开始新的调度过程。
- 当调度器中没有可调度的Request时,引擎关闭。
接下来我们可以实现parse方法
#!/usr/bin/python
# coding:utf-8
import scrapy
from ..items import Testdemo001Item
class YtlSpider(scrapy.Spider):
# 爬虫的唯一名字,不能为不同的spider设置相同的名字
name = 'ytl'
# 爬虫允许爬取URL的域名范围,这个爬虫允许的爬取的范围为yetianlian.com
allowed_domains = ['yetianlian.com']
# start_urls 是spider在启动时进行爬取的入口的URL列表。因此,第一个被获取的URL也是其中之一
# 后续的URL则会从初始的URL的响应中主动提取
start_urls = ['http://yetianlian.com']
# parse() 函数,是spider的一个方法。被调用时,根据初始的URL响应后的返回的Response对象
# 将会作为唯一的参数传递给该方法。该方法有三个功能
# (1.解析返回的数据(response data) 2.提取数据(生成item) 3.生成需要进一步处理的URL的Request对象)
def parse(self, response):
# 获取文章的标题
P_title = response.xpath('//*[@id="post-1135"]/div/h3/text()').extract()[0]
# C_titles = response.xpath('//*[@id="post-1135"]//h2/text()').extract()[1:]
# 获取文章章节跟进的url
urls = response.xpath('//*[@id="post-1135"]//ul/li/a/@href').extract()[8:]
#获取文章章节标题
A_titles = response.xpath('//*[@id="post-1135"]//ul/li/a/text()').extract()[8:]
for i in range(len(urls)):
# # 初始化item的两种方法
# item = Testdemo001Item(p_title = P_title,c_title = C_title,url = url,a_title = A_title)
# yield item
item = Testdemo001Item()
item['p_title'] = P_title
try:
item['chapternum'] = A_titles[i].split(' ')[0]
item['chaptertitle'] = A_titles[i].split(' ')[1]
item['chapterurl'] = urls[i]
request = scrapy.Request(urls[i], callback=self.body_parse)
# meta属性的说明:作用是传递信息给下一个函数,跟随请求并包含元数据的字典。也可以往字典中添加字段
request.meta['item'] = item
yield request
except Exception,e:
print 'exception',e
continue
def body_parse(self,response):
item = response.meta['item']
item['chaptercontent'] = response.xpath('//*[@id="post-3"]//p/text()').extract()
yield item
# pass是空语句,是为了保持程序结构的完整性。pass 不做任何事情,一般用做占位语句
pass
程序中使用callback调用body_parse方法,实现对每一个章节内容的获取。(
scrapy.Request(urls[i], callback=self.body_parse)
)
这个程序里面没有涉及到翻页的问题,关于翻页的问题,后续的文章中会有细致说明。
3.4 Item Pipelines.py
首先应该明白Item Pipeline的作用:它是为了将爬虫之前爬取到的数据Item进行持久化的存储。
两大步骤:a.定制Item Pipeline. b.Settings.py中激活Item Pipieline
四个典型的应用:
a.清理HTML数据
b.验证爬取数据的合法性,检查Item是否包含有某些字段
c.查重并丢弃
d.将爬取到的结果保存到文件或者数据库中每一个Item Pipeline组件是一个独立的Python类,必须实现process_item的方法,方法的原型是:process_item(self, item, spider)
该方法在Item数据处理的时候都会被Item Pipeline调用,该方法必须返回一个item或者是抛出的异常
参数item:表示被爬取的Item对象
参数spider:表示爬取该Item的spider
第一步:定制Item Pipeline
pipelines.py 代码
import codecs
import os
import json
import sqlite3
from scrapy.exceptions import DropItem
from . import settings
class Testdemo001Pipeline(object):
'''
'''
def process_item(self, item, spider):
# 创建文件保存路径
dir_path = '%s%s'%(settings.FILE_STORE,item['p_title'] + '---' + item['chapternum'])
print 'dir_path',dir_path
# 如果文件保存路径不存在,创建一个新的文件路径
if not os.path.exists(dir_path):
os.makedirs(dir_path)
# 在dir_path下创建文件 ‘ + '\\' ’
file_path = '%s%s' %(dir_path + '\\',item['chapternum'] + ' ' + item['chaptertitle'] + '.txt')
with codecs.open(file_path,'w',encoding='utf-8') as file_writer:
file_writer.writelines(item['chaptercontent'])
file_writer.write('\r\n')
file_writer.close()
return item
第二步:激活Item Pipeline
需要在settings.py文件中添加下列代码:
ITEM_PIPELINES = {
'TestDemo001.pipelines.Testdemo001Pipeline': 300,
}
其中
TestDemo001.pipelines
表示的是处理Item使用pipelines文件路径(从“包含Python项目模块的文件夹”开始的路径)
其中Testdemo001Pipeline
表示的是pipelines文件中调用的类名
300
表示的是该Pipeline调用的先后顺序(这些数字的定义范围是:0~1000)
另外需要设置FILE_STORE
定制Item Pipeline中使用到了settings.py中设置的文件路径,所以settings.py中应设置
FILE_STORE
# 创建文件保存路径
dir_path = '%s%s'%(settings.FILE_STORE,item['p_title'] + '---' + item['chapternum']
FILE_STORE = 'D:\\File\\Download\\ytl\\'
另外还有其他的数据存储一些内容,后续的文章中会详细介绍。有关于scrapy内置数据的存储,scrapy内置的图片和文件下载方式,个人定制图片和文件下载方式,常用的文件保存和图片保存方式以及利用数据库存储数据。
3.5 settings.py
在Item Pipeline中我们已经对settings.py文件进行修改,在settings.py中,我后续还要加入User-Agent,Downloader Middlewares,Spider Middlewares,IP_Proxy代理资源池等内容的设置。
settings.py代码:
#!/usr/bin/python
# coding:utf-8
BOT_NAME = 'TestDemo001'
SPIDER_MODULES = ['TestDemo001.spiders']
NEWSPIDER_MODULE = 'TestDemo001.spiders'
FILE_STORE = 'D:\\File\\Download\\ytl\\'
# Crawl responsibly by identifying yourself (and your website) on the user-agent
#USER_AGENT = 'TestDemo001 (+http://www.yourdomain.com)'
# Obey robots.txt rules
# 有些网站需要将该功能关闭 即该值设置为false
ROBOTSTXT_OBEY = True
ITEM_PIPELINES = {
'TestDemo001.pipelines.Testdemo001Pipeline': 300,
# 'TestDemo001.database.mypipeline.mypipeline':135,
}
3.6 运行spider crawl
这里提供两个方法来运行spider
a.打开cmd命令窗口,cd到项目文件夹下,运行scrapy crawl ytl
命令
b.在项目文件夹下建立一个XXX.py,在文件中写入调用运行爬虫的命令代码,在IDE下运行这个XXX.py文件即可
#!/usr/bin/python
# coding:utf-8
from scrapy import cmdline
cmdline.execute("scrapy crawl ytl".split())
# cmdline.execute("scrapy crawl ytl -o hysxm.csv".split())
结语:
两篇文章讲述了整体spider爬虫爬取数据的流程,这里爬取的是一个简单的教程,算是抛砖引玉。
如果在阅读的过程中如果遇到有问题请指出,若对文章中的内容如有不同的见解,欢迎一起学习讨论。