scrapy框架
简介:所谓的框架就是集合一个很多功能且具有很强的通用性的一个项目模板
scrapy:是一个专门用于异步爬虫的框架
- 高性能的数据解析。请求发送。持久化存储,全站数据爬取,中间件,分布式。。。
环境安装:
-
mac,linux:pip install scrapy
-
windows:
-
首先,pip install wheel
-
其次,下载twisted文件。下载地址:http://www.lfd.uci.edu/~gohlke/pythonlibs/#twisted
-
进入下载目录,执行pip install Twisted‑17.1.0‑cp35‑cp35m‑win_amd64.whl
其实就是下载的twisted文件名
-
Twisted:就是一个异步的架构。被作用在了scrapy中。 - 安装报错:需要更换另一个版本的twisted文件进行安装即可。
-
-
然后。pip install pywin32
-
最后pip install scrapy
-
- 测试:cmd中scrapy按下回车,如果没有报错说明安装成功。
scrapy的基本使用
创建工程
- scrapy startproject name (name就是你创建的工程名字)
- 目录结构
- spiders:爬虫文件夹
- 必须要放一个爬虫源文件
- settings.py:工程配置文件
- spiders:爬虫文件夹
创建爬虫源文件
- cd 到 name下 ,就是工程文件夹下。
- 输入 scrapy genspider spiderName www.xxxx.com (spiderName就是源文件的名字)
- 编写对应代码在爬虫文件中
执行工程
-
scrapy crawl spiderName (就是你源文件的名字)
-
执行工程后,默认会输出工程所有的日志。
-
修改类型日志的输出:
指定类型日志的输出: - settings.py: - LOG_LEVEL = 'ERROR'
爬虫文件spiderName内容:
- name:爬虫文件名称,该文件的唯一表示
- start_urls:起始url列表,存储的都是url,url可以被自动进行get请求的发送
- parse方法:请求后的数据解析操作
settings.py:
-
禁止robots
-
指定日志类型:LOG_LEVEL="ERROR"
-
UA伪装
USER_AGENT ="Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.100 Safari/537.36"
-
scrapy数据解析
- 使用:response.xpath("xpath表达式")
- scarpy中xpath直接将定位到的标签中存储的值或者属性取出,但是返回的是Selector对象,且相关数据都存储在Selector对象的data属性中,需要调用extract、extract_first()取出字符串数据
持久化存储
-
基于终端指令的持久化存储
- 要求:这中方式只能将parse方法的返回值存储到本地指定后缀的文本文件中。
- 执行指令:scrapy crawl spidersName -o filePath
此方式没有txt类型,最好选择csv后缀!
-
基于管道的持久化存储(重点)
-
在爬虫源文件parse方法中进行解析
-
在item.py中定义相关属性
- 步骤1中解析除了几个字段的数据,就在item.py文件中定义几个属性
-
在爬虫文件中将解析到数据存储封装到Item类型的对象中
-
将Item类型对象提交管道、
-
在管道文件(pipelines.py)中,接收爬虫文件提交过来的Item类型对象,且对其进行任意形式的的持久化存储
-
在配置文件中开启管道机制
settings.py
-
基于管道实现数据得备份
-
将爬取到的数据分别存储到不同的载体中
-
实现:将数据一份被备份到mysql.一份备份到redis
-
问题:管道文件中的一个管道类如何实现这么一组操作
- 一个管道类对应一种形式的持久化存储,如果将数据存储到不同的载体中,就需要使用多个管道类
- 已经定义好三个管道类,将数据写入三个载体中进行存储。
- item会不会一次提交给三个管道类
- 不会,爬虫文件中的item只会被提交给优先级最高的那一个管道类
- 优先级高的管道类需要在process_item中实现return item,就item传递给下一个即将被执行的管道类
-
scrapy的手动请求发送实现的全栈数据爬取
- yield scrapy.Request(url,callback):get
- callback指定解析函数,用于解析数据
- yield scrapy.FormRquest(url,callback,formdata):POST
- formdata:字典,请求参数
-
为什么start_urls列表中的url会被自动进行get请求的发送?
-
因为列表中的url其实是被start_requests这个父类方法实现的get请求发送
-
def start_requests(self): for u in self.start_urls: yield scrapy.Request(url=u,callback=self.parse)
-
如何将start_urls中的url默认进行post请求的发送?
-
重写start_requests方法即可
-
def start_requests(self): for u in self.start_urls: yield scrapy.FormRequest(url=u,callback=self.parse)
-
-
-
实现最基本的scrapy框架
1 . 首先执行命令创建工程
scrapy startproject name
2 . 然后执行创建源文件
当然记得cd 到工程目录下
scrapy genspider spidername
目录结构:
在settings文件先设置,UA、robots协议,还有日志等级
打开spider源文件
# -*- coding: utf-8 -*-
import scrapy
import os
import sys
from Zhou.items import ZhouItem
class ZhoushenSpider(scrapy.Spider):
name = 'Zhoushen'
# allowed_domains = ['www.xxxx.com']
start_urls = ['https://duanziwang.com/category/经典段子/']
#基于指令做持久化存储
#scrapy crawl spidersName -o filePath
# def parse(self, response):
# article_list = response.xpath('/html/body/section/div/div/main/article')
# all_list =[]
# for article in article_list:
#下面解析出来的内容不是字符串数据,说明和etree中的xpath使用方式不同
#xpath返回的列表中存储而是Selector对象,其实我们想要的字符串数据被存储在了该对象的data属性中了
# title = article.xpath('./div[1]/h1/a/text()')[0]
# content = article.xpath('./div[2]/p/text()')[0]
#将selector对象data属性值取出
# title = article.xpath('./div[1]/h1/a/text()')
# title = article.xpath('./div[1]/h1/a/text()').extract_first()
# title = article.xpath('./div[1]/h1/a/text()')[0].extract()
# content = article.xpath('./div[2]/p/text()')[0].extract()
# dict={
# "title":title,
# "content":content
# }
# all_list.append(dict)
# return all_list
#基于管道进行持久化存储
def parse(self, response):
article_list = response.xpath('/html/body/section/div/div/main/article')
print(os.path)
print(sys.path)
all_list =[]
for article in article_list:
title = article.xpath('./div[1]/h1/a/text()')[0].extract()
content = article.xpath('./div[2]/p/text()')[0].extract()
#实例化一个Item类型对象,将解析到的数据存储在该对象中
Item = ZhouItem()
#不可以通过.的方式调用属性(此对象比较特别)
Item["title"]=title
Item["content"]=content
yield Item
items.py
import scrapy
class ZhouItem(scrapy.Item):
# define the fields for your item here like:
title = scrapy.Field()
content = scrapy.Field()
pipelines.py
# -*- coding: utf-8 -*-
# Define your item pipelines here
#
# Don't forget to add your pipeline to the ITEM_PIPELINES setting
# See: https://docs.scrapy.org/en/latest/topics/item-pipeline.html
import pymysql
from redis import Redis
class ZhouPipeline(object):
fp = None
#重写父类的两个方法
def open_spider(self,spider):
print("我是open_spider(),我只会在爬虫开始的时候执行一次!")
self.fp = open("zhou.txt","w",encoding="utf-8")
def close_spider(self,spider):
print("我是close_spider(),我只会在爬虫结束的时候执行一次")
self.fp.close()
#该方法是用来接收Item对象,一次只能接受一个item,说明该方法被多次调用
#参数item:就是用来接受item对象的
def process_item(self, item, spider):
# print(item) #就是一个字典
self.fp.write(item["title"]+":"+item["content"]+"
")
#将item存储到本地文件
return item
#将数据存储在mysql中
class MysqlPileLine(object):
conn = None
cursor = None
def open_spider(self,spider):
self.conn = pymysql.Connect(host="127.0.0.1",port=3306,user="root",password="123",db="zhoushen",charset="utf8")
print(self.conn)
def process_item(self,item,spider):
self.cursor = self.conn.cursor()
sql='insert into zhou values ("%s","%s")'%(item["title"],item["content"])
#事务的处理
try:
self.cursor.execute(sql)
self.conn.commit()
except Exception as e:
print(e)
self.conn.rollback()
return item
def close_spider(self,spider):
self.cursor.close()
self.conn.close()
class RedisPileLine(object):
conn = None
def open_spider(self,spider):
self.conn = Redis(host="127.0.0.1",port=6379)
print(self.conn)
def process_item(self,item,spider):
self.conn.lpush('duanzi',item)