掌握Scrapy数据建模与请求技巧
scrapy数据建模与请求
学习目标:
应用scrapy项目中建模
应用构造Request对象,并发送请求
应用meta参数在不同解析函数中传递数据
数据建模
通常在做项目的过程中,在items.py中进行数据建模
为什么建模
定义item即提前规划好那些字段要抓取,防止手误,因为定义好之后,在运行过程中,系统会自动检查
配合注释一起可以清晰的知道要抓取那些字段,没有定义的字段不能抓取,在目标字段少的时候可以使用字典代替
使用scrapy的一些特定组件需要item做支持,如scrapy的ImagesPipeline管道类
如何建模
在items.py文件中定义要提取的字段:
import scrapy
class Day01Item(scrapy.Item):# define the fields for your item here like:# 老师的名字name = scrapy.Field()# 老师的级别title = scrapy.Field()# 老师的介绍desc = scrapy.Field()
然后下面进行讲解怎么使用,但是我们建模完后通常不会在这个文件中使用,回去项目文件中使用,这里解决是为了讲解
class Day01Item(scrapy.Item):# define the fields for your item here like:# 老师的名字name = scrapy.Field()# 老师的级别title = scrapy.Field()# 老师的介绍desc = scrapy.Field()
if __name__ == '__main__':# 实例化对象item = Day01Item()item['name'] = 'hui函数'item['title'] = '高手'item['desc'] = '一个真正学习的程序员'print(item)print(type(item))# 打印结果# {'desc': '一个真正学习的程序员', 'name': 'hui函数', 'title': '高手'}# <class '__main__.Day01Item'>
这里可以由代码看出来,我们的item打印出来的数据和字典一模一样,但是它不属于字典,由type可以看出
前面不是说了,如果手误打错了,系统会自动进行检查吗?那么,下面就可以试一下,将item的name打成nama看看会发生什么
# Define here the models for your scraped items
#
# See documentation in:
# https://docs.scrapy.org/en/latest/topics/items.html
import scrapy
class Day01Item(scrapy.Item):# define the fields for your item here like:# 老师的名字name = scrapy.Field()# 老师的级别title = scrapy.Field()# 老师的介绍desc = scrapy.Field()
if __name__ == '__main__':# 实例化对象item = Day01Item()item['nama'] = 'hui函数'item['title'] = '高手'item['desc'] = '一个真正学习的程序员'print(item)print(type(item))"""Traceback (most recent call last):File "C:\Users\Desktop\python爬虫\scrapy_day01\day01\day01\items.py", line 21, in <module>item['nama'] = 'hui函数'~~~~^^^^^^^^File "C:\Users\AppData\Local\Programs\Python\Python312\Lib\site-packages\scrapy\item.py", line 98, in __setitem__raise KeyError(f"{self.__class__.__name__} does not support field: {key}")
KeyError: 'Day01Item does not support field: nama'
"""
由上面的报错结果可以看出,系统会提示我们name写错了
如何使用模板类
模板类定义好后需要在爬虫中导入并且实例化,之后使用的方法和使用字典相同
示例:
首先,我们来到上一次写的抓取某网站老师信息的案例,将我们的模板类导入到spider的爬虫文件里面,然后实例化对象
import scrapy # 导入模型类 from ..items import Day01Item class ItcastSpider(scrapy.Spider):name = "itcast"allowed_domains = ["itcast.cn"]start_urls = ["https://www.itheima.com/teacher.html"] def parse(self, response):# 实例化模板对象item_data_dict = Day01Item()# 获取数据data_list = response.xpath("//*/div[@class='li_txt']")print('获取到数量为:',len(data_list))for data_item in data_list:name = data_item.xpath("./h3/text()").extract_first()title = data_item.xpath("./h4/text()").extract_first()desc = data_item.xpath("./p/text()").extract_first()print(f'老师的名字:{name},级别:{title},介绍:{desc}')item_data_dict['name'] = nameitem_data_dict['title'] = titleitem_data_dict['desc'] = desc# 返回到pipeline.py文件yield item_data_dict
由于这里的item_data_dict不属于字典,所以我们要进入管道文件,将我们返回的数据转换为字典数据进行保存
from itemadapter import ItemAdapter import json class Day01Pipeline:def __init__(self):self.file = open('itcast.txt','w') def process_item(self, item, spider):# 将数据转换为字典类型item = dict(item)# 序列化为json格式json_data = json.dumps(item,ensure_ascii=False)# 写入文件self.file.write(json_data)# 返回引擎return item def __del__(self):self.file.close()
开发流程总结
创建项目
scrapy startproject 项目名
明确目标
在items.py文件中进行建模
创建爬虫
创建爬虫
scrapy genspider 爬虫名 允许的域名
完成爬虫
修改start_urls 检查修改allowed_domains 编写解析方法
保存数据
在pipeline.py文件中对数据进行处理的管道
在settings.py文件中注册启用管道
翻页请求的思路
我们爬取数据的时候不可能只爬一页数据,那么对于一些要进行翻页请求的数据我们应该怎么办呢?
回顾requests模块是如何实现翻页请求的:
找到下一页的url地址
调用requests.get(url)
scrapy实现翻页的思路:
找到下一页的url地址
构造url地址的请求对象,传递给引擎
构造Request对象,并发送请求
实现方法
确定url地址
构造请求,scrapy.Request(url,callback)
callback:指定解析函数名称,表示该请求返回的响应使用哪一个函数进行解析
把请求交给引擎:yield scrapy.Request(url,callback)
scrapy.Request的更多参数
scrapy.Request(url[,callback,method="GET",headers,body,cookies,meta,dont_filter=False])
参数解释
中括号里面的参数为可选参数
callback:表示当前的url的响应交给哪个函数去处理
meta:实现数据在不同的解析函数中传递,meta默认带有部分数据,比如下载延迟,深度请求等
dont_filter:默认为False,表示开启过滤,会过滤请求过的url,如果一个页面要重复请求,那么就要可以改为True
method:指定POST或者GET请求
headers:接收一个字典,其中不包含cookies
cookies:接收一个字典,专门放置cookies
body:接收json字符串,为POST的数据,发送payload_post请求时使用
meta参数的使用
meta的作用:meta可以实现数据在不同的解析函数中的传递
在爬虫文件的parse方法中,提取详情页增加之前callback指定的parse_detail函数:
def parse(self,response):……yield scrapye.Request(detail_url,callback=self.parse_detail,meta={"item":item})……
def parse_detail(self,response):# 获取之前传入的itemitem = response.meta["item]
特别注意
meta参数是一个字典
meta字典中有一个固定的键proxy,表示代理ip,后面在scrapy的中间件中进行介绍
scrapy模拟登录
学习目标:
应用 请求对象cookies参数的使用
了解 start_requests函数的作用
应用 构造并发送post请求
回顾之前的模拟登陆的方法
requests模块是如何实现模拟登陆的?
直接携带cookies请求页面
找url地址,发送post请求存储cookie
selenium是如何模拟登陆的?
找到对应的input标签,输入文本点击登陆
scrapy的模拟登陆
直接携带cookies
找url地址,发送post请求存储cookie
scrapy携带cookies直接获取需要登陆后的页面
应用场景
cookie过期时间很长,常见于一些不规范的网站
能在cookie过期之前把所有的数据拿到
配合其他程序使用,比如其使用selenium把登陆之后的cookie获取到保存到本地,scrapy发送请求之前先读取本地cookie
实现:重构scrapy的starte_rquests方法
scrapy中start_url是通过start_requests来进行处理的,其实现代码如下
# 这是源代码def start_requests(self) -> Iterable[Any]:warnings.warn(("The Spider.start_requests() method is deprecated, use ""Spider.start() instead. If you are calling ""super().start_requests() from a Spider.start() override, ""iterate super().start() instead."),ScrapyDeprecationWarning,stacklevel=2,)if not self.start_urls and hasattr(self, "start_url"):raise AttributeError("Crawling could not start: 'start_urls' not found ""or empty (but found 'start_url' attribute instead, ""did you miss an 's'?)")for url in self.start_urls:yield Request(url, dont_filter=True)
所以对应的,如果start_url地址中的url是需要登录后才能访问的url地址,则需要重写start_request方法并在其中手动添加上cookie
携带cookies登陆豆瓣
import scrapy
class DouSpider(scrapy.Spider):name = "dou"allowed_domains = ["douban.com"]start_urls = ["https://www.douban.com/people/290131564/?_i=3446982r7DGOBi"]
def start_requests(self):url = self.start_urls[0]
temp = '...' # 抓取cookie值cookies = {data.split('=')[0]: data.split('=')[-1] for data in temp.split('; ')}
yield scrapy.Request(url=url,callback=self.parse,cookies=cookies)
def parse(self, response):print(response.xpath('/html/head/title/text()').extract_first())
注意:
scrapy中cookie不能够放在headers中,在构造请求的时候有专门的cookies参数,能够接受字典形式的coookie
在setting中设置
USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.0.0 Safari/537.36" ROBOTSTXT_OBEY = False //协议 DOWNLOAD_DELAY = 3 # 防止请求过快 COOKIES_ENABLED = True # 允许cookies