创建一个scrapy项目:scrapy startproject mySpider
生成一个爬虫:scrapy genspider douban movie.douban.com
提取数据:完善spider,使用xpath等方法
保存数据:pipeline中保存数据
2 创建scrapy项目
下面以抓取豆瓣top250来学习scrapy的入门使用:豆瓣电影 Top 250
安装scrapy命令:sudo apt-get install scrapyscrapy==2.5.0 或者:pip install scrapy==2.5.0
1.创建项目面板
scrapy 2.5.0 - no active project usage: scrapy <command>[options] [args] Available commands : bench Run quick benchmark test #测试电脑性能 fetch Fetch a URL using the scrapy down1oader#将源代码下载下来并显示出来 genspider Generate new spider using pre-defined temp1ates#创建一个新的spider文件 runspider Run a self-contained spider (without creating a project)# 这个和通过craw1启动爬虫不同,scrapy runspider爬虫文件名称 settings Get settings values#获取当前的配置信息 she11 Interactive scraping console#进入scrapy 的交互模式 startproject create new project#创建爬虫项目 version Print scrapy version#显示scrapy框架的版本 view open URL in browser,as seen by scrapy#将网页document内容下载下来,并且在浏览器显示出来
创建scrapy项目的命令:scrapy startproject +<项目名字>
示例:scrapy startproject myspider
生成的目录和文件结果如下:
3 创建爬虫
命令:在项目路径下执行:scrapy genspider +<爬虫名字> + <允许爬取的域名>
示例:
cd myspider scrapy genspider douban movie.douban.com
生成的目录和文件结果如下:
4 完善spider
完善spider即通过方法进行数据的提取等操作
在\myspider\myspider\douban.py中修改内容如下:
import scrapy from scrapy.http import HtmlResponse # 自定义spider类,继承scrapy.spider class DoubanSpider(scrapy.Spider): # 爬虫名字 name = 'douban' # 允许爬取的范围,防止爬虫爬到别的网站 allowed_domains = ['douban.com'] # 开始爬取的url地址 start_urls = ['https://movie.douban.com/top250'] # 数据提取的方法,接受下载中间件传过来的response def parse(self, response:HtmlResponse, **kwargs): # scrapy的response对象可以直接进行xpath ol_list = response.xpath('//ol[@class="grid_view"]/li') print(ol_list) for ol in ol_list: # 创建一个数据字典 item = {} # 利用scrapy封装好的xpath选择器定位元素,并通过extract()或extract_first()来获取结果 item['title'] = ol.xpath('.//div[@class="hd"]/a/span[1]/text()').extract_first() item['rating'] = ol.xpath('.//div[@class="bd"]/div/span[2]/text()').extract_first() item['quote'] = ol.xpath('.//div[@class="bd"]//p[@class="quote"]/span/text()').extract_first() print(item)
注意:
response.xpath方法的返回结果是一个类似list的类型,其中包含的是selector对象,操作和列表一样,但是有一些额外的方法
extract() 返回一个包含有字符串的列表
extract_first() 返回列表中的第一个字符串,列表为空没有返回None
spider中的parse方法必须有
需要抓取的url地址必须属于allowed_domains,但是start_urls中的url地址没有这个限制
启动爬虫的时候注意启动的位置,是在项目路径下启动
4.1 response对象属性
url HTTP相应的 URL地址,str类型的
status HTTP响应状态码,int类型的(在pycharm的控制台中你可以看到,例如200,404)
body HTTP响应正文,bytes类型
text 文本形式的HTTP响应正文,str类型,由response.body使用response.encoding解码得到
encoding HTTP响应正文的编码
request 产生该HTTP响应的Requset对象
selector (这个比较重要了)选择器对象用来提取response中的数据
xpath(query) 即xml路径语言,用来确定xml文档中某部分位置的语言(html属性xml)
css(query) 也是一种选择器,用来提取页面内中的数据,但是不如xpath强大。
urljoin(url) 用来构造绝对url
5 利用管道pipeline来处理(保存)数据
5.1 对douban爬虫进行修改完善
在爬虫文件douban.py中parse()函数中最后添加
yield item
注意:yield能够传递的对象只能是:BaseItem,Request,dict,None
5.2 修改pipelines.py文件
class MyspiderPipeline: # 爬虫文件中提取数据的方法每yield一次item,就会运行一次 # 该方法为固定名称函数 def process_item(self, item, spider): print(item)
5.3 在settings.py设置开启pipeline
ITEM_PIPELINES = { 'myspider.pipelines.MyspiderPipeline': 300, } # 协议证书配置 # Obey robots.txt rules ROBOTSTXT_OBEY = False # 带上请求头 # Override the default request headers: DEFAULT_REQUEST_HEADERS = { 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8', 'Accept-Language': 'en', 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36' }
6 运行scrapy
6.1命令运行
命令:在项目目录下执行scrapy crawl +<爬虫名字>
示例:scrapy crawl douban --nolog
6.2 debug运行
# scrapy封装的命令执行 from scrapy import cmdline if __name__ == '__main__': # 解析的是一个列表对象 # 获取的json文件会乱码 需要修改配置 FEED_EXPORT_ENCODING = 'utf-8' cmdline.execute('scrapy crawl douban -o douban.json -s FEED_EXPORT_ENCODING="utf-8"'.split())
注意:
安装2.5版本之后运行代码可能会遇到以下错误
AttributeError: module 'OpenSSL.SSL' has no attribute 'SSLv3_METHOD'
ImportError: cannot import name 'HTTPClientFactory' from 'twisted.web.client' (unknown location)
降低OpenSSL和cryptography以及Twisted的版本
pip install pyOpenSSL==22.0.0 pip install cryptography==38.0.4 pip install Twisted==20.3.0 # 或者 pip install cryptography==3.4.8 pip install pyOpenSSL==21.0.0
异步报错
TypeError: ProactorEventLoop is not supported, got: <ProactorEventLoop running=False closed=False debug=False>
这个错误通常表示正在尝试在不支持
ProactorEventLoop
的操作系统上运行Scrapy。Scrapy目前不支持Windows上的ProactorEventLoop
,因此您需要将其更改为SelectorEventLoop
。可以通过在Scrapy项目的settings.py文件中添加以下代码来更改默认的事件循环
import asyncio if hasattr(asyncio, "WindowsSelectorEventLoopPolicy"): asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
2.5的版本适配的是3.7的py版本,需要找到你解释器对应的版本信息
可以运行尝试
7.输出的数据日志信息
7.1 日志等级
CRITICAL:严重错误
ERROR:一般错误
WARNING:警告
INFO: 一般信息
DEBUG:调试信息
注意: 默认的日志等级是DEBUG
7.2日志输出信息
Versions:使用的工具版本信息 Overridden settings: 重写的配置 Telnet Password:Telnet 平台密码(Scrapy附带一个内置的telnet控制台,用于检查和控制Scrapy运行过程) Enabled extensions :开启的拓展功能 Enabled downloader middlewares:开启的下载器中间件 Enabled spider middlewares:开启的爬虫中间件 Enabled item pipelines:开启的管道 Dumping Scrapy stats:所以的信息汇总
7.3 日志等级设置
修改settings.py文件
LOG_LEVEL = 'WARNING' # 设置日志显示的等级为WARNING LOG_FILE = './log.txt' # 将日志信息全部记录到log.txt文件中
三、scrapy发送翻页请求
1. 翻页请求的思路
回顾requests模块是如何实现翻页请求的:
找到下一页的URL地址
调用requests.get(url)
scrapy实现翻页的思路:
找到下一页的url地址
构造url地址的请求,传递给引擎
2 scrapy实现翻页请求
2.1 实现方法
确定url地址
构造请求,scrapy.Request(url,callback)
callback:指定解析函数名称,表示该请求返回的响应使用哪一个函数进行解析
把请求交给引擎:yield scrapy.Request(url,callback)
2.2 豆瓣爬虫
通过爬取豆瓣top250页面的电影信息,学习如何实现翻页请求
地址:豆瓣电影 Top 250
思路分析:
获取首页的数据
寻找下一页的地址,进行翻页,获取数据
注意:
可以在settings中设置ROBOTS协议
# False表示忽略网站的robots.txt协议,默认为True ROBOTSTXT_OBEY = False
可以在settings中设置User-Agent:
# scrapy发送的每一个请求的默认UA都是设置的这个User-Agent USER_AGENT = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36'
2.3 代码实现
在爬虫文件的parse方法中:
# 数据提取的方法,接受下载中间件传过来的response def parse(self, response): # print(response.urljoin('asfd')) # scrapy的response对象可以直接进行xpath ol_list = response.xpath('//ol[@class="grid_view"]/li') # 提取下一页的href并拼接url # print(ol_list) for ol in ol_list: # 创建一个数据字典 # item = MyspiderItem() item = {} # 利用scrapy封装好的xpath选择器定位元素,并通过extract()或extract_first()来获取结果 item['title'] = ol.xpath('.//div[@class="hd"]/a/span[1]/text()').extract_first() item['rating'] = ol.xpath('.//div[@class="bd"]/div/span[2]/text()').extract_first() item['quote'] = ol.xpath('.//div[@class="bd"]//p[@class="quote"]/span/text()').extract_first() print(item) yield item if response.xpath("//a[text()='后页>']/@href").extract_first() != None: next_url = response.urljoin(response.xpath("//a[text()='后页>']/@href").extract_first()) print(next_url) yield scrapy.Request(url=next_url, callback=self.parse)
2.4 scrapy.Request的更多参数
scrapy.Request(url[,callback,method="GET",headers,body,cookies,\ meta,dont_filter=False])
参数解释
中括号中的参数为可选参数
callback:表示当前的url的响应交给哪个函数去处理
meta:实现数据在不同的解析函数中传递,meta默认带有部分数据,比如下载延迟,请求深度等
dont_filter:默认为False,会过滤请求的url地址,即请求过的url地址不会继续被请求,对需要重复请求的url地址可以把它设置为Ture,比如贴吧的翻页请求,页面的数据总是在变化;start_urls中的地址会被反复请求,否则程序不会启动
method:指定POST或GET请求
headers:接收一个字典,其中不包括cookies
cookies:接收一个字典,专门放置cookies
body:接收一个字典,为POST的数据
2.5 重写start_requests方法
上述方法可以帮助我们实现翻页的问题,但是这种翻页并不是框架的最优解,我们可以重写Spider的start_requests方法,自己生成对应的请求网址给到引擎进行调度
def start_requests(self): for i in range(0, 10): url = 'https://movie.douban.com/top250?start={}&filter='.format(i * 25) yield scrapy.Request(url)
3 meta参数的使用
meta的形式:字典
meta的作用:meta可以实现数据在不同的解析函数中的传递
在爬虫文件的parse方法中,提取详情页增加之前callback指定的parse_detail函数:
def parse(self,response): ... yield scrapy.Request(detail_url, callback=self.parse_detail,meta={"item":item}) ... def parse_detail(self,response): #获取之前传入的item item = resposne.meta["item"]
特别注意
meta参数是一个字典
meta字典中有一个固定的键
proxy
,表示代理ip,关于代理ip的使用我们将在scrapy的下载中间件的学习中进行介绍meta的download_timeout设置请求超时
4. item的使用
4.1 Item能够做什么
定义item即提前规划好哪些字段需要抓取,scrapy.Field()仅仅是提前占坑,通过item.py能够让别人清楚自己的爬虫是在抓取什么,同时定义好哪些字段是需要抓取的,没有定义的字段不能使用,防止手误
在python大多数框架中,大多数框架都会自定义自己的数据类型(在python自带的数据结构基础上进行封装),目的是增加功能,增加自定义异常
4.2 定义Item
在items.py文件中定义要提取的字段:
class MyspiderItem(scrapy.Item): title = scrapy.Field() # 标题 rating = scrapy.Field() # 评估 quote = scrapy.Field() # 概述
4.3 使用Item
Item使用之前需要先导入并且实例化,之后的使用方法和使用字典相同
修改爬虫文件douban.py:
from myspider.items import MyspiderItem # 导入Item,注意路径 ... def parse(self, response): # print(response.meta['data']) ol_list = response.xpath('//ol[@class="grid_view"]/li') for ol in ol_list: item = MyspiderItem() item['title'] = ol.xpath('.//div[@class="hd"]/a/span[1]/text()').extract_first() item['rating'] = ol.xpath('.//div[@class="bd"]/div/span[2]/text()').extract_first() item['quote'] = ol.xpath('.//div[@class="bd"]//p[@class="quote"]/span/text()').extract_first() # item['yeshu'] = response.meta['data'] # print(item) yield item
注意:
from myspider.items import MyspiderItem这一行代码中 注意item的正确导入路径,忽略pycharm标记的错误
python中的导入路径要诀:从哪里开始运行,就从哪里开始导入
四、scrapy的管道使用
1. 了解scrapyShell
scrapy shell是scrapy提供的一个终端工具,能够通过它查看scrapy中对象的属性和方法,以及测试xpath
使用方法:
scrapy shell https://movie.douban.com/top250
在终端输入上述命令后,能够进入python的交互式终端,此时可以使用:
response.xpath():直接测试xpath规则是否正确
response.url:当前响应的url地址
response.request.url:当前响应对应的请求的url地址
response.headers:响应头
response.body:响应体,也就是html代码,默认是byte类型
response.requests.headers:当前响应的请求头
3 settings.py中的设置信息
3.1 为什么项目中需要配置文件
在配置文件中存放一些公共变量,在后续的项目中方便修改,如:本地测试数据库和部署服务器的数据库不一致
3.2 配置文件中的变量使用方法
变量名一般全部大写
导入即可使用
3.3 settings.py中的重点字段和含义
USER_AGENT 设置ua
ROBOTSTXT_OBEY 是否遵守robots协议,默认是遵守
CONCURRENT_REQUESTS 设置并发请求的数量,默认是16个
DOWNLOAD_DELAY 下载延迟,默认无延迟
COOKIES_ENABLED 是否开启cookie,即每次请求带上前一次的cookie,默认是开启的
DEFAULT_REQUEST_HEADERS 设置默认请求头
SPIDER_MIDDLEWARES 爬虫中间件,设置过程和管道相同
DOWNLOADER_MIDDLEWARES 下载中间件
LOG_LEVEL 控制终端输出信息的log级别,终端默认显示的是debug级别的log信息
LOG_LEVEL = "WARNING"
LOG_FILE 设置log日志文件的保存路径,如果设置该参数,终端将不再显示信息
LOG_FILE = "./test.log"
4 pipeline管道的深入使用
4.1 pipeline中常用的方法:
process_item(self,item,spider):实现对item数据的处理
open_spider(self, spider): 在爬虫开启的时候仅执行一次
close_spider(self, spider): 在爬虫关闭的时候仅执行一次
4.2 管道文件的修改
在pipelines.py代码中完善
import pymysql import pymongo class MyspiderMySQLPipeline: def open_spider(self, spider): # 判断是哪个爬虫 名字不同可能执行的爬虫项目也不同 if spider.name == 'douban': self.db = pymysql.connect(host="localhost", user="root", password="root", db="spiders9") self.cursor = self.db.cursor() # 创建变语法 sql = ''' CREATE TABLE IF NOT EXISTS douban( id int primary key auto_increment not null, quote VARCHAR(255) NOT NULL, rating VARCHAR(255) NOT NULL, title VARCHAR(255) NOT NULL) ''' try: self.cursor.execute(sql) print("CREATE TABLE SUCCESS.") except Exception as ex: print(f"CREATE TABLE FAILED,CASE:{ex}") def process_item(self, item, spider): # SQL 插入语句 sql = 'INSERT INTO douban(id, quote, rating, title) values(%s, %s, %s, %s)' # 执行 SQL 语句 try: self.cursor.execute(sql, (0, item['quote'], item['rating'], item['title'])) # 提交到数据库执行 self.db.commit() print('mysql数据插入成功...') except Exception as e: print(f'数据插入失败: {e}') # 如果发生错误就回滚 self.db.rollback() # 不return的情况下,另一个权重较低的pipeline将不会获得item,否则后一个pipeline取到的数据为None值 return item def close_spider(self, spider): # 在爬虫关闭的时候仅执行一次 # 关闭文件 if spider.name == 'douban': self.db.close() class MyspiderMongoDBPipeline: def open_spider(self, spider): # 在爬虫开启的时候仅执行一次 if spider.name == 'douban': con = pymongo.MongoClient(host='127.0.0.1', port=27017) # 实例化mongoclient self.collection = con.spiders9.douban # 创建数据库名为spiders9,集合名为douban的集合操作对象 def process_item(self, item, spider): if spider.name == 'douban': print('mongo保存成功') self.collection.insert_one(dict(item)) # 此时item对象需要先转换为字典,再插入 # 不return的情况下,另一个权重较低的pipeline将不会获得item return item
4.3 开启管道
在settings.py设置开启pipeline
ITEM_PIPELINES = { 'myspider.pipelines.MyspiderMySQLPipeline': 300, 'myspider.pipelines.MyspiderMongoDBPipeline': 400, }
思考:pipeline在settings中能够开启多个,为什么需要开启多个?
不同的pipeline可以处理不同爬虫的数据,通过spider.name属性来区分
不同的pipeline能够对一个或多个爬虫进行不同的数据处理的操作,比如一个进行数据清洗,一个进行数据的保存
同一个管道类也可以处理不同爬虫的数据,通过spider.name属性来区分
4.4 pipeline使用注意点
使用之前需要在settings中开启
pipeline在setting中键表示位置(即pipeline在项目中的位置可以自定义),值表示距离引擎的远近,越近数据会越先经过
有多个pipeline的时候,process_item的方法必须return item,否则后一个pipeline取到的数据为None值
pipeline中process_item的方法必须有,否则item没有办法接受和处理
process_item方法接受item和spider,其中spider表示当前传递item过来的spider
open_spider(spider) :能够在爬虫开启的时候执行一次
close_spider(spider) :能够在爬虫关闭的时候执行一次
上述俩个方法经常用于爬虫和数据库的交互,在爬虫开启的时候建立和数据库的连接,在爬虫关闭的时候断开和数据库的连接