python爬虫--scrapy框架的学习和使用⭐⭐⭐---第一部分
Posted 胜天半月子
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了python爬虫--scrapy框架的学习和使用⭐⭐⭐---第一部分相关的知识,希望对你有一定的参考价值。
文章目录
前言
- 什么是框架?
就是一个集成了很多功能并且具有很强通用性的一个项目模板。
- 如何学习框架?
专门学习框架封装的各种功能的详细用法。
- 什么是scrapy?
爬虫中封装好的一个明星框架。
功能:
- 高性能的持久化操作
- 异步的数据下载操作
- 高性能的数据解析操作
- 分布式操作
一、scrapy框架的基本使用
- 环境安装
linux和mac操作系统:pip install scrapy
windows系统:
pip install wheel
下载twisted,下载地址为http://www.lfd.uci.edu/~gohlke/pythonlibs/#twisted
安装twisted:pip install Twisted‑17.1.0‑cp36‑cp36m‑win_amd64.whl
pip install pywin32
pip install scrapy
测试:在终端里录入scrapy指令,没有报错即表示安装成功!
1.1 windows下安装scrapy
- 安装wheel
pip install wheel
- 下载twisted
- 安装pywin32
pip install pywin32
- 安装scrapy
pip install scrapy
一开始安装失败,系统提示让我更新pip,更新pip后从新安装成功。
更新pip的命令:
python -m pip install --upgrade pip
1.2 scrapy的基本使用
- 步骤流程
- 通过终端指令创建一个工程:
scrapy startproject xxxPro
- cd xxxPro
- 在spiders子目录中创建一个爬虫文件:
scrapy genspider spiderName 域名
- 执行工程:
scrapy crawl spiderName
【不显示日志:scrapy crawl spiderName --nolog
】
- 终端命令在VScode中演示
spiders文件里一定要放一个.py的爬虫文件
- 讲解scrapy创建的spiderName爬虫文件
import scrapy
class FirstSpider(scrapy.Spider):
# 爬虫文件的名称 :就是爬虫源文件的唯一标识,即不能够重复
name = 'first'
# 允许的域名:用来限定start_urls列表中哪些url可以进行请求发送,一般该字段不用
# allowed_domains = ['www.xxx.com']
# 起使的url列表:该列表中存放的url会被scrapy自动进行请求的发送,可以有多个url
start_urls = ['https://www.baidu.com/','https://www.sogou.com/']
# 用于数据解析 response参数表示的就是请求成功之后对应的响应对象
# parse方法调用的次数由start_urls中url的个数决定的
def parse(self, response):
pass # 将print(response代替pass进行验证)
发现上述输出并没有有关start_urls中url的相关内容,
修改settings.py
中ROBOTSTXT_OBEY = False
不显示日志:
scrapy crawl spiderName --nolog
在settings.py中加入LOG_LEVEL ='ERROR'
,显示指定类型的日志信息
- 对比信息
- 使用上述scrapy框架
- 使用requests模块
import requests
if __name__ == "__main__":
# 1. 指定url
url = "https://www.sogo.com/"
# 2. 发起请求
response = requests.get(url=url)
# get方法灰返回一个响应对象
# 3. 获取相应数据 .text返回的是字符串形式的响应数据
page_text = response
print(page_text)
二、scrapy数据解析
scrapy框架的基本流程操作完毕后,一定要对settings.py文件进行如下操作:
- 修改USER_AGENT
- ROBOTSTXT_OBEY = False
- LOG_LEVEL = 'ERROR’
- 爬取xx百科中的段子⭐⭐
import scrapy
class QiubaiSpider(scrapy.Spider):
name = 'qiubai'
# allowed_domains = ['www.xxx.com']
# 爬取糗事百科中段子的作者和内容
start_urls = ['https://www.qiushibaike.com/text/']
# 数据解析
def parse(self, response):
# 解析:作者的名称+段子内容
div_list = response.xpath('//div[contains(@class,"col1")]/div')
# print('div_list:',div_list)
for div in div_list:
# 所有的xpath返回的都是列表,但是列表元素一定是Selector类型的对象
# extract()可以将Selector类型的对象中的字符串提取出来
# author = div.xpath('./div[1]/a[2]/h2/text()')[0].extract()
# 如果保证返回的列表元素只有一个列表元素是可以使用extract_first()
author = div.xpath('./div[1]/a[2]/h2/text()').extract_first()
# 列表调用extract()表示将列表中每一个Selector对象中的字符串提取出来,并返回列表
# 即列表调用extract()后返回还是列表
content = div.xpath('./a[1]/div/span//text()').extract() # 由于有的标签中含有<br>标签,取所有内容用//
content = ''.join(content)# 列表转换为字符串
print(author, content)
break # 只输出一次用来查看
- 问题总结
- 遇到class中含有多个属性值
三、scrapy持久化存储
- 基于终端指令
要求:只可以将parse方法的返回值存储到本地的文本文件中【往数据库中存储是不行的】- 基于管道⭐⭐
- 数据解析
- 在Item类中定义相关的属性
- 将解析的数据封装到item类型的对象【使用items.py文件】
- 将Item类型对象提交给管道进行持久化存储操作
- 在管道类的process_item中呀将其接收到的item对象中存储的数据进行持久化存储操作【使用pipelines.py】
- 在配置文件中开启管道【scrapy默认情况是没有开启管道功能需要手动开启】
3.1 基于终端指令
例如第二章中爬取xx百科中的段子进行存储的代码如下:
import scrapy
class QiubaiSpider(scrapy.Spider):
name = 'qiubai'
# allowed_domains = ['www.xxx.com']
# 爬取糗事百科中段子的作者和内容
start_urls = ['https://www.qiushibaike.com/text/']
# 数据解析
def parse(self, response):
div_list = response.xpath('//div[contains(@class,"col1")]/div')
all_data = [] # 存储所有数据
# print('div_list:',div_list)
for div in div_list:
# 所有的xpath返回的都是列表,但是列表元素一定是Selector类型的对象
# extract()可以将Selector类型的对象中的字符串提取出来
# author = div.xpath('./div[1]/a[2]/h2/text()')[0].extract()
# 如果保证返回的列表元素只有一个列表元素是可以使用extract_first()
author = div.xpath('./div[1]/a[2]/h2/text()').extract_first()
# 列表调用extract()表示将列表中每一个Selector对象中的字符串提取出来,并返回列表
# 即列表调用extract()后返回还是列表
content = div.xpath('./a[1]/div/span//text()').extract() # 由于有的标签中含有<br>标签,取所有内容用//
content = ''.join(content)# 列表转换为字符串
dic = {
'author':author,
'content':content
}
all_data.append(dic)
return all_data
现在可以通过终端指令对parse方法的返回值进行持久化存储
scrapy crawl qiubai -o ./qiubai.csv
【scrapy crawl spiderName -o filePpath
】- 注意:持久化存储的文件类型是有限制的
- 结论
- 优点: 简洁高效便捷
- 缺点:局限性比较强(数据只能存储到指定文件中)
3.2 基于管道⭐⭐
使用管道进行爬取xx百科中的段子
现在根据流程进行一步一步的操作
- 数据解析
在qiubai.py文件进行数据解析
- 代码
import scrapy
from qiubaipro.items import QiubaiproItem
class QiubaiSpider(scrapy.Spider):
name = 'qiubai'
# allowed_domains = ['www.xxx.com']
# 爬取糗事百科中段子的作者和内容
start_urls = ['https://www.qiushibaike.com/text/']
def parse(self, response):
# 解析:作者的名称+段子内容
div_list = response.xpath('//div[contains(@class,"col1")]/div')
for div in div_list:
# 所有的xpath返回的都是列表,但是列表元素一定是Selector类型的对象
# extract()可以将Selector类型的对象中的字符串提取出来
# author = div.xpath('./div[1]/a[2]/h2/text()')[0].extract()
# 如果保证返回的列表元素只有一个列表元素是可以使用extract_first()
author = div.xpath('./div[1]/a[2]/h2/text() ').extract_first()
# 列表调用extract()表示将列表中每一个Selector对象中的字符串提取出来,并返回列表
# 即列表调用extract()后返回还是列表
content = div.xpath('./a[1]/div/span//text()').extract() # 由于有的标签中含有<br>标签,取所有内容用//
content = ''.join(content)# 列表转换为字符串
# 3. 将解析的数据封装到item类型的对象
item = QiubaiproItem()
# 获取值用[]而不是. ⭐⭐
item['author'] = author
item['content'] = content
# 4. 将Item类型对象提交给管道进行持久化存储操作
yield item
- 在Item类中定义相关的属性
在items.py文件中操作
import scrapy
class QiubaiproItem(scrapy.Item):
# define the fields for your item here like:
author = scrapy.Field()
content = scrapy.Field()
pass
- 在管道类的process_item中呀将其接收到的item对象中存储的数据进行持久化存储操作
在pipelines.py文件中操作
from itemadapter import ItemAdapter
class QiubaiproPipeline:
fp = None
# 重写父类的一个方法:该方法只在开始爬虫的时候被调用一次⭐
def open_spider(self,spider):
print('开始爬虫。。。。。。')
self.fp = open('./qiubai.txt','w',encoding='utf-8')
# 专门用来处理Item类型对象的
# 该方法可以接收爬虫文件提交过来的item对象
# 该方法每接收一次item就会被调用一次⭐⭐⭐
def process_item(self, item, spider):
author = item['author']
content = item['content']
self.fp.write(author +":"+content+'\\n')
return item
# 只会调用一次⭐
def close_spider(self,spider):
print('结束爬虫!')
self.fp.close()
- 可能出现的问题
修改author的代码即可:author = div.xpath(’./div[1]/a[2]/h2/text()
| ./div[1]/span/h2/text()
').extract_first()
- 在配置文件中开启管道
在settings.py中操作
- 结论
- 好处:通用性强,可以存储任意的文件或者数据库
- 缺点:编码流程有些繁琐
3.3 面试题⭐⭐
将爬取到的数据一份存储到本地,一份存储到数据库,如何实现?
使用管道文件中的管道类:
- 一个管道类对应一组数据存储到一个平台或者载体中
- 爬虫文件中的item只会给管道文件中第一个被执行的管道类接收
- pipelines.py中的process_item中的return item表示传递给下一个即将被执行的管道类【优先级高的传递给优先级低的】
- 代码
同样根据编码流程进行编写,再次不再一一展示,只是展示核心代码
- 修改管道文件
对pipelines.py操作
from itemadapter import ItemAdapter
import pymysql
class QiubaiproPipeline:
fp = None
# 重写父类的一个方法:该方法只在开始爬虫的时候被调用一次
def open_spider(self,spider):
print('开始爬虫。。。。。。')
self.fp = open('./qiubai.txt','w',encoding='utf-8')
# 专门用来处理Item类型对象的
# 该方法可以接收爬虫文件提交过来的item对象
# 该方法每接收一次item就会被调用一次
def process_item(self, item, spider):
author = item['author']
content = item['content']
self.fp.write(author +":"+content+'\\n')
return item # 就会传递给下一个即将被执行的管道类
def close_spider(self,spider):
print('结束爬虫!')
self.fp.close()
# 管道文件中一个管道类对应一组数据存储到一个平台或者载体中
class mysqlPipeline(object):
conn = None
cursor = None # 游标对象
def open_spider(self,spider):
self.conn = pymysql.Connect(host='127.0.0.1',user='test',password='123456',db='testdb',charset='utf8')
def process_item(self, item, spider):
self.cursor = self.conn.cursor()
try:
self.cursor.execute('insert into qiubai values("%s","%s")'%(item["author"],item["content"]))
self.conn.commit()
except Exception as e:
print(e)
self.conn.rollback() # 出现错误,则回滚即这个事务从来没有执行过一样
# rollback()数据回滚的作用就是确保数据库操作的原子性问题,多次操作要么都执行,要么都不执行
return item
def close_spider(self,spider):
self.cursor.close() # 关闭游标
self.conn.close() # 关闭数据库
复习python对mysql数据库的操作:
- 修改配置文件
- 结果
四、scrapy爬取全栈数据⭐⭐
全栈数据:就是将网站中某板块下的全部页码对应的页面数据进行爬取
- 需求
爬取当前页的名称
- 实现方式
- 将所有页面的url添加到start_urls列表中,列表中的元素会被自动请求发送【不推荐】
- 自行手动编码进行请求发送【推荐】⭐
yield scrapy.Request(url,callback)
:callback专门用于数据解析
- 创建工程
- xiaohua.py
import scrapy
class XiaohuaSpider(scrapy.Spider):
name = 'xiaohua'
# allowed_domains = ['www.xxx.com']
start_urls = ['https://nice.ruyile.com/?f=5']
# 生成一个通用的模板(不可变)
url = 'https://nice.ruyile.com/?f=5&p=%d'
page_num =2
# 基于全栈数据的爬取
def parse(self, response):
div_list = response.xpath('/html/body/div[4]/div[1]/div[2]/div[@class="tp_list"]')
for div in div_list:
# scrapy中xpath表达式返回的是Selector
img_name = div.xpath('./div[2]/a[1]/text()').extract_first()
print(img_name)
# 一共117页 用5测试
if self.page_num <= 5:
new_url = format(self.url%self.page_num)# 不能使用self.new_url
self.page_num += 1
# 手动请求发送:callback回调函数是专门用于数据解析
yield scrapy.Request(url=new_url,callback=self.parse)
五、scrapy五大核心组件
scrapy的基本使用我们已经掌握,但是各位心中一定会有些许的疑问,我们在编写scrapy工程的时候,我们只是在定义相关类中的属性或者方法,但是我们并没有手动的对类进行实例化或者手动调用过相关的方法,那么这些操作都是谁做的呢?接下来我们就来看看scrapy的五大核心组件的工作流程,然后大家就会上述的疑问有基本了解了。
- 引擎(Scrapy)
用来处理整个系统的数据流处理, 触发事务(框架核心)
- 调度器(Scheduler)
用来接受引擎发过来的请求, 压入队列中, 并在引擎再次请求的时候返回. 可以想像成一个URL(抓取网页的网址或者说是链接)的优先队列, 由它来决定下一个要抓取的网址是什么, 同时去除重复的网址
- 下载器(Downloader)
用于下载网页内容, 并将网页内容返回给蜘蛛(Scrapy下载器是建立在twisted这个高效的异步模型上的)
- 爬虫(Spiders)
爬虫是主要干活的, 用于从特定的网页中提取自己需要的信息, 即所谓的实体(Item)。用户也可以从中提取出链接,让Scrapy继续抓取下一个页面
- 产生url,对url进行手动发送
- 进行数据解析
- 项目管道(Pipeline)
负责处理爬虫从网页中抽取的实体,主要的功能是持久化实体、验证实体的有效性、清除不需要的信息。当页面被爬虫解析后,将被发送到项目管道,并经过几个特定的次序处理数据。
六、请求传参⭐⭐
在某些情况下,我们爬取的数据不在同一个页面中,例如,我们爬取一个电影网站,电影的名称,评分在一级页面,而要爬取的其他电影详情在其二级子页面中。这时我们就需要用到请求传参。
- 使用场景
如果爬取解析的数据不在同一张页面中。【
深度爬取
】
- 需求
爬取该网页的岗位名称,岗位描述
要爬取的这两个信息并没有在同一页面中,因此要使用请求传参,即
深度爬取
- 问题说明
在进行本节练习的时候,可能由于反爬机制导致包含岗位信息和岗位描述的li标签列表无法获得。因此,最终没有结果显示,但还是将源码放在这里,供大家阅读和学习!
- 源码boss.py
import scrapy
from bossPro.items import BossproItem
class BossSpider(scrapy.Spider):
name = 'boss'
# allowed_domains = ['www.xxx.com']
# 首页放进去
start_urls = ['https://www.zhipin.com/job_detail/?query=python&city=101010100&industry=&position=']
url = 'https://www.zhipin.com/job_detail/?query=python&city=101010100&industry=&position=%d'
page_num = 2
# 用于解析详情页中的岗位描述
def parse_detail(self, response):
item = response.meta['item']
job_desc = response.xpath('//*[@id="main"]/div[3]/div/div[2]/div[2]/div[1]/div//text()').extract()
job_desc = ''.join(job_desc)
# print(job_desc)
item['job_desc'] = job_desc
yield item
# 用于解析首页的岗位名称
def parse(self, response):
# 查看响应状态码
print('code:',response.status)
li_list = response.xpath('//*[@id="main"]/div/div[3]/ul/li')
print('li_list:',li_list)
for li in li_list:
item = BossproItem()
job_name = li.xpath('.//div[@class="primary-box"]/div[1]/span[1]/a/text()').extract_first()
item['job_name'] = job_name
# print(job_name)
detail_url = 'https://www.zhipin.com/' + li.xpath('.//div[@class="primary-box"]/div[1]/span[1]/a/@href').extract_first()
# 对详情页发起请求获取详情页的页面源码数据
# 手动请求的发送
# 请求传参
yield scrapy.Request(detail_url,callback=self.parse_detail,meta={'items':item})
# 分页操作,对其他页面进行爬取
# 一定要设置条件和 page_num+=1
if self.page_num <= 3:
new_url = format(self.url%self.page_num)
self.page_num += 1
yield scrapy.Request(new_url,callback=self.parse)
- 代码解析
- 爬取更深度的信息
detail_url = 'https://www.zhipin.com/' + li.xpath('.//div[@class="primary-box"]/div[1]/span[1]/a/@href').extract_first()
# 请求传参
yield scrapy.Request(detail_url,callback=self.parse_detail,meta={'items':item})
----------------------------------------------------------------------------------------------------
# 用于解析详情页中的岗位描述
def parse_detail(self, response):
item = response.meta['item'<以上是关于python爬虫--scrapy框架的学习和使用⭐⭐⭐---第一部分的主要内容,如果未能解决你的问题,请参考以下文章
python爬虫--scrapy框架的学习和使用⭐⭐⭐---第一部分