使用Scrapy框架爬取百思不得姐
通过Scrapy框架创建项目后,接下来对项目进行操作,爬取相应的内容
爬取段子的作者,图片,和文字内容
scrapy自带xpath模块和css选择模块两种解析方式
1、解析数据
进入网站,右键检查元素,使用xpath解析信息,获取相应的模块
代码使用xpath解析信息就不用和以前一样导入etree了,Scrapy框架已经封装好了
尝试是否能获取jrlist的信息,在pycharm的Terminal终端使用命令,驱动引擎
scrapy crawl budejie
可以在日志中找到这种信息,就是解析出来的内容
# <Selector xpath="//div[@class='j-r-list']/ul/li" data='<li>\n \n \n<!--用户信息'>
scrapy自带的xpath解析出来是一个Selector对象,我们需要用extract将内容取出
解析完内容后,使用for循环,获取其中的内容:段子的作者,图片,和文字内容
依旧使用xpath方法
解析完所有要获取的内容后,打印一下 item,依旧是使用scrapy crawl budejie命令,查看获取的信息是否正确,确认无误后,用生成器返回出去
budejie .py
import scrapy
class BudejieSpider(scrapy.Spider):
# 这个类是Spider的派生类,是一个基础模板的爬虫
# 这个类的作用:1)引擎被启动以后,在开启调度之前首先要从这个类的对象中提取起始url 2)下载器下载完成以后,会通过一个回调方法把响应对象传递给这个类对象来进行下一步的解析
name = 'budejie' # name属性是爬虫的名字,这个爬虫在当前工程中的唯一标识,我们在启动引擎的时候,需要将爬虫名字加在后面,以告诉引擎当前我们使用是哪个爬虫,引擎就会根据爬虫的名字取相应的对象中提取起始url,并且调度下载,然后还要根据爬虫名字将数据回调
allowed_domains = ['budejie.com'] # 允许访问的域名,引擎在从start_urls中提取了url之后,要对比提取的url是否在allowed_domains中,如果在则允许调度,如果不在直接剔除
start_urls = ['http://www.budejie.com/'] # 这个列表用于给引擎提供起始url
def parse(self, response):
# 这个函数是一个回调函数,下载器下载完数据以后就会回调这个函数,把下载下来的响应内容同参数response传递过来
print(response)
# 解析响应体
# 【注意】scrapy自带xpath模块和css选择模块两种解析方式
jrlist = response.xpath("//div[@class='j-r-list']/ul/li")
# print(jrlist) # <Selector xpath="//div[@class='j-r-list']/ul/li" data='<li>\n \n \n<!--用户信息'>
# scrapy自带的xpath解析出来是一个Selector对象,我们需要用extract将内容取出
for jr in jrlist:
# 创建一个item字典
item = {}
item["author"] = jr.xpath(".//a[@class='u-user-name']/text()").extract()[0]
item["content"] = jr.xpath(".//div[@class='j-r-list-c-desc']/a/text()").extract()[0]
item["pic"] = jr.xpath(".//img[@class='lazy']/@src").extract()[0]
# 当爬虫解析完一个url的响应体以后,需要将解析的内容用一个可迭代的容器装起来,并且返回出去
yield item
# 这个容器最终会被引擎再次接收,并且迭代
2、数据的存储
2.1 方式一:放入到对应的csv、json或者xml文件中
我们可以通过在Terminal终端输入scrapy crawl budejie -o budejie.csv/budejie.json/budejie.xml这个三种指令把每次迭代的数据放入到对应的csv、json或者xml文件中
但是一般情况下数据都不这么存储,这样存储数据不够灵活,这里就可以使用管道的方式了
2.2 方式二:管道
在setting .py文件中
大概60~80行,找到管道的配置
setting .py文件中,管道指向的文件是pipelines .py文件
所以pipelines .py文件中MyfisrtscrapyproPipeline这个类,就是管道指向的类, 这个类本来是一个普通类,但是我们把它加入到管道组件了,它变成了一个管道类(具有了管道的功能,一个管道的生命周期)
pipelines .py
class MyfisrtscrapyproPipeline(object):
#一个管道的生命周期有以下3个生命周期函数
def open_spider(self,spider):
# 当爬虫开启的时候,这个函数调用
print("%s爬虫已经开启"%spider)
def process_item(self, item, spider):
# 爬虫解析完数据以后会返回一个可迭代的容器,这个容器每被迭代一次就会调用一次这个方法
print("爬虫%s正在迭代数据:%s"%(spider,item))
return item
def close_spider(self,spider):
print("爬虫%s被关闭!"%spider)
pass
同样,使用命令scrapy crawl budejie查看打印的内容
这里可以通过管道存入将数据redis数据库
import redis
class MyfisrtscrapyproPipeline(object):
# 这个类本来是一个普通类,但是我们把它加入到管道组件了,它变成了一个管道类(具有了管道的功能)
# 一个管道的生命周期
def open_spider(self,spider):
# 当爬虫开启的时候,这个函数调用
print("%s爬虫已经开启"%spider)
# 创建一个redis链接
self.rds = redis.StrictRedis(host='www.fanjianbo.com', port=6379, db=7)
def process_item(self, item, spider):
# 爬虫解析完数据以后会返回一个可迭代的容器,这个容器每被迭代一次就会调用一次这个方法
print("爬虫%s正在迭代数据:%s"%(spider,item))
self.rds.lpush("budejie", item)
return item
def close_spider(self,spider):
print("爬虫%s被关闭!"%spider)
这里说一个概念:
管道组件可以设置多个,所以管道组件(ITEM_PIPELINES)需要用一个字典来表示
字典的键代表组件位置,字典的值代表这个位置的组件的优先级,数组越小优先级越高,数据先从优先级高的组件经过,然后向优先级低组件传递
这是什么意思呢?
举个例子
在管道内再创建一个类,BudejiePipeline
pipelines .py
class MyfisrtscrapyproPipeline(object):
# 这个类本来是一个普通类,但是我们把它加入到管道组件了,它变成了一个管道类(具有了管道的功能)
# 一个管道的生命周期
def open_spider(self,spider):
# 当爬虫开启的时候,这个函数调用
print("%s爬虫已经开启"%spider)
def process_item(self, item, spider):
# 爬虫解析完数据以后会返回一个可迭代的容器,这个容器每被迭代一次就会调用一次这个方法
print("爬虫%s正在迭代数据:%s"%(spider,item))
return item
def close_spider(self,spider):
print("爬虫%s被关闭!"%spider)
class BudejiePipeline(object):
def open_spider(self,spider):
# 当爬虫开启的时候,这个函数调用
print("%s爬虫已经开启"%spider)
def process_item(self, item, spider):
# 爬虫解析完数据以后会返回一个可迭代的容器,这个容器每被迭代一次就会调用一次这个方法
print("管道“budejie”正在迭代数据:%s"%item)
return item # 【注意】process_item函数用于迭代数据用的,每次迭代完要将数据重新返回给系统,一共后面的管道和系统使用,如果不返回数据就会在这个管道中直接销毁,后面的管道和系统就无法再使用
def close_spider(self,spider):
print("爬虫%s被关闭!"%spider)
pass
然后在setting .py中配置它的管道配置,将值设置为200,数组越小优先级越高,所以会先调用BudejiePipeline这个管道
# 管道
ITEM_PIPELINES = {
'MyFisrtScrapyPro.pipelines.MyfisrtscrapyproPipeline': 300,
'MyFisrtScrapyPro.pipelines.BudejiePipeline': 200,
}
如果把 return item注释
使用命令scrapy crawl budejie查看打印的内容,会发现 None
process_item函数用于迭代数据用的,每次迭代完要将数据重新返回给系统,以供共\后面的管道和系统使用,如果不返回数据就会在这个管道中直接销毁,比如这里把 return item注释,后面的管道和系统就无法再使用,所以BudejiePipeline这个管道会没有数据,而且MyfisrtscrapyproPipeline会无法获取数据