当前位置: 首页 > news >正文

初步搭建并使用Scrapy框架

目录

目标

版本

实战

搭建框架

获取图片链接、书名、价格

通过管道下载数据

通过多条管道下载数据

下载多页数据


目标

        掌握Scrapy框架的搭建及使用,本文以爬取当当网魔幻小说为案例做演示。


版本

        Scrapy 2.12.0


实战

搭建框架

第一步:在D:\pytharm_workspace位置创建爬虫Scrapy项目。通过cmd在该目录执行Scrapy创建项目命令。dangdang是我的项目名称。

scrapy startproject dangdang

第二步:进入项目目录,并创建爬虫类。其中magic_novels是我自定义的爬虫程序名称,permit.mee.gov.cn表示要爬取的网站域名。

第三步:注释在settings文件中掉OBOTSTXT_OBEY协议。

#ROBOTSTXT_OBEY = True

第四步:打开Pycharm控制台,进入项目目录。设置start_urls为我们要爬取的首页。parse表示项目启动后会自动请求start_urls中的URL。所以我们在parse方法中调试输出,并运行项目。

import scrapyclass MagicNovelsSpider(scrapy.Spider):name = "magic_novels"allowed_domains = ["category.dangdang.com"]start_urls = ["https://category.dangdang.com/cp01.03.40.00.00.00.html"]def parse(self, response):print(response.url)print(response.text)

 scrapy crawl magic_novels

第五步:此时会打印很多的无用信息,我们可以在settings.py文件中设置日志级别。再次启动项目后会发现页面干净了很多。

LOG_LEVEL = "WARNING"
scrapy crawl magic_novels
注意:如果多次请求导致可能会导致缓存出现,请使用以下命令:
scrapy crawl magic_novels --set HTTPCACHE_ENABLED=False

获取图片链接、书名、价格

第一步:通过xpath爬取价格、图片、书名,我们先来打印调试。此时发现图片的链接不对,思考是否是懒加载的一个反扒策略。

    def parse(self, response):'''图片的链接:src=//ul[@id='component_59']/li//img/@src图片的名称:alt=//ul[@id='component_59']/li//img/@alt图书的价格:price=//ul[@id='component_59']/li//p[@class='price']/span考虑到所有的数据都来源于//ul[@id='component_59']/li,所以我们可以复用li对象。'''li_list = response.xpath("//ul[@id='component_59']/li")for li in li_list:print(f'图片的链接:src={li.xpath(".//img/@src").extract_first()}')print(f'图片的名称:alt={li.xpath(".//img/@alt").extract_first()}')print(f'图书的价格:price={li.xpath(".//p[@class='price']/span[1]/text()").extract_first()}')print("\n")

第二步: 刷新页面,在浏览器检查中查看第一个和最后一个,发现图片链接的初始接收属性并不是src,而是data-original,src是加载以后才代替data-original的。

第三步:修改src获取的方法,并再次运行项目。发现除了第一个图书的src为None,其他src都正常获取了。猜测:是不是第一个图书打开时没有使用懒加载。

第四步: 通过调试发现,确实如刚才的猜想一般,第一个图书的src没有使用懒加载。修改代码后再次调试,发现可以获取到第一个图书的链接。

    def parse(self, response):'''图片的链接:src=//ul[@id='component_59']/li//img/@src图片的名称:alt=//ul[@id='component_59']/li//img/@alt图书的价格:price=//ul[@id='component_59']/li//p[@class='price']/span考虑到所有的数据都来源于//ul[@id='component_59']/li,所以我们可以复用li对象。'''li_list = response.xpath("//ul[@id='component_59']/li")for i , li in enumerate(li_list):print(f'第{i+1}本书。')src = li.xpath(".//img/@data-original").get()if src is None:src = li.xpath(".//img/@src").get()alt = li.xpath(".//img/@alt").get()price = li.xpath(".//p[@class='price']/span[1]/text()").get()print(f'图片的链接:src={src}')print(f'图片的名称:alt={alt}')print(f'图书的价格:price={price}')print("\n")


通过管道下载数据

第一步:打开items.py文件,配置字段。

# Define here the models for your scraped items
#
# See documentation in:
# https://docs.scrapy.org/en/latest/topics/items.htmlimport scrapyclass DangdangItem(scrapy.Item):# 图片src = scrapy.Field()# 书名name = scrapy.Field()# 价格price = scrapy.Field()

第二步:将item类导入到爬虫程序。

import scrapyfrom dangdang.items import DangdangItemclass MagicNovelsSpider(scrapy.Spider):name = "magic_novels"allowed_domains = ["category.dangdang.com"]start_urls = ["https://category.dangdang.com/cp01.03.40.00.00.00.html"]def parse(self, response):'''图片的链接:src=//ul[@id='component_59']/li//img/@src图书的名称:alt=//ul[@id='component_59']/li//img/@alt图书的价格:price=//ul[@id='component_59']/li//p[@class='price']/span考虑到所有的数据都来源于//ul[@id='component_59']/li,所以我们可以复用li对象。'''li_list = response.xpath("//ul[@id='component_59']/li")for i , li in enumerate(li_list):print(f'第{i+1}本书。')src = li.xpath(".//img/@data-original").get()if src is None:src = li.xpath(".//img/@src").get()alt = li.xpath(".//img/@alt").get()price = li.xpath(".//p[@class='price']/span[1]/text()").get()print(f'图片的链接:src={src}')print(f'图书的名称:alt={alt}')print(f'图书的价格:price={price}')print("\n")#该对象要通过管道去下载,通过yield可以在每次获得book后立刻返回book给管道。book=DangdangItem(src=src, alt=alt, price=price);yield book

第三步:在settings.py中开启管道配置。管道可以有很多个并且有优先级,值越大优先级越小。

ITEM_PIPELINES = {"dangdang.pipelines.DangdangPipeline": 300,
}

第四步:来到pipelines.py文件,其中process_item方法中的item就是我们刚才在爬虫程序配置的boot对象。我们可以打印测试效果。

class DangdangPipeline:def process_item(self, item, spider):print(type(item))print(str(item))return item
scrapy crawl magic_novels

思考:我们通过process_item可以获取到数据,但是每次循环获取数据再重新打开文件、写入数据,关闭文件明显不符合开发规范。

第五步:在pipelines.py文件中配置open_spider和close_spider方法,分别表示在爬虫程序执行前执行的方法和在爬虫程序执行之后执行的方法。我们可以打印日志测试。

class DangdangPipeline:#在爬虫文件开始之前就执行的方法def open_spider(self, spider):print("++++")def process_item(self, item, spider):print(type(item))print(str(item))return item#在爬虫文件执行之后再执行的方法def close_spider(self, spider):print("----")
scrapy crawl magic_novels

第六步: 下载JSON数据。

# 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 json# useful for handling different item types with a single interface
from itemadapter import ItemAdapterclass DangdangPipeline:#在爬虫文件开始之前就执行的方法def open_spider(self, spider):self.fp=open("book.json","w",encoding="utf-8")self.fp.write("[")def process_item(self, item, spider):line = json.dumps(dict(item), ensure_ascii=False) + ",\n"self.fp.write(line)return item#在爬虫文件执行之后再执行的方法def close_spider(self, spider):# 删除最后一个多余的逗号,并关闭 JSON 数组self.fp.seek(self.fp.tell() - 3, 0)  self.fp.write("\n]")self.fp.close()
scrapy crawl magic_novels

 


通过多条管道下载数据

第一步:在pipelines.py文件中定义新的管道类。

#下载图片
class DangdangDownloadImgPipeline:# 在爬虫文件开始之前就执行的方法def open_spider(self, spider):passdef process_item(self, item, spider):print(item.get('src'))url="http:"+item.get('src')filename='C:/Users/Administrator/Desktop/test/'+sanitize_filename(item.get("alt"))+'.jpg'urllib.request.urlretrieve(url=url,filename=filename)return item# 在爬虫文件执行之后再执行的方法def close_spider(self, spider):passdef sanitize_filename(filename):"""替换 Windows 文件名中不合法的字符为下划线。"""# 定义 Windows 文件名不允许的字符invalid_chars = r'[\\/:*?"<>|]'# 使用正则表达式将非法字符替换为下划线return re.sub(invalid_chars, '_', filename)

第二步:在settings.py中定义该管道类的优先级。

ITEM_PIPELINES = {"dangdang.pipelines.DangdangPipeline": 300,"dangdang.pipelines.DangdangDownloadImgPipeline": 300,
}

第三步:执行下载操作,可以看到JSON数据和图片都下载成功了。

scrapy crawl magic_novels


下载多页数据

思考:目前我们只是下载了第一页的数据,能否通过配置页码下载多个页面的数据呢?

第一步:去页面点击下一页,发现链接都差不多,区别在于pg后面的跟的页码。

https://category.dangdang.com/pg2-cp01.03.40.00.00.00.html
https://category.dangdang.com/pg3-cp01.03.40.00.00.00.html

第二步:在爬虫程序中,设置基础的url和页码,页码初始化为第一页。

class MagicNovelsSpider(scrapy.Spider):name = "magic_novels"allowed_domains = ["category.dangdang.com"]start_urls = ["https://category.dangdang.com/cp01.03.40.00.00.00.html"]base_url="https://category.dangdang.com/pg"page_num=1;

第三步:在parse方法中递归请求当当网,每次请求都将url的页码改变。注意:递归逻辑写在循环之外。

import scrapyfrom dangdang.items import DangdangItemclass MagicNovelsSpider(scrapy.Spider):name = "magic_novels"allowed_domains = ["category.dangdang.com"]start_urls = ["https://category.dangdang.com/cp01.03.40.00.00.00.html"]base_url="https://category.dangdang.com/pg"page_num=1;def parse(self, response):'''图片的链接:src=//ul[@id='component_59']/li//img/@src图书的名称:alt=//ul[@id='component_59']/li//img/@alt图书的价格:price=//ul[@id='component_59']/li//p[@class='price']/span考虑到所有的数据都来源于//ul[@id='component_59']/li,所以我们可以复用li对象。'''li_list = response.xpath("//ul[@id='component_59']/li")for i , li in enumerate(li_list):print(f'第{i+1}本书。')src = li.xpath(".//img/@data-original").get()if src is None:src = li.xpath(".//img/@src").get()alt = li.xpath(".//img/@alt").get()price = li.xpath(".//p[@class='price']/span[1]/text()").get()print(f'图片的链接:src={src}')print(f'图书的名称:alt={alt}')print(f'图书的价格:price={price}')print("\n")#该对象要通过管道去下载,通过yield可以在每次获得book后立刻返回book给管道。book=DangdangItem(src=src, alt=alt, price=price);yield bookif self.page_num<3:self.page_num+=1url=self.base_url+str(self.page_num)+"-cp01.03.40.00.00.00.html";#GET请求yield scrapy.Request(url=url, callback=self.parse)

第四步:运行项目。发现可以正常下载前三页的数据。

http://www.lryc.cn/news/526812.html

相关文章:

  • 基于SpringBoot的软件产品展示销售系统
  • pycharm 运行远程环境问题 Error:Failed to prepare environment.
  • Redis vs. 其他数据库:深度解析,如何选择最适合的数据库?
  • HTB:Support[WriteUP]
  • 导出地图为pdf文件
  • Linux中关于glibc包编译升级导致服务器死机或者linux命令无法使用的情况
  • Golang Gin系列-8:单元测试与调试技术
  • linux如何修改密码,要在CentOS 7系统中修改密码
  • Kafka后台启动命令
  • 使用Cline+deepseek实现VsCode自动化编程
  • 【redis初阶】redis客户端
  • 【ComfyUI专栏】ComfyUI 部署Kolors
  • 深入了解 HTTP 头部中的 Accept-Encoding:gzip、deflate、br、zstd
  • 【含代码】逆向获取 webpack chunk 下的__webpack_require__ 函数,获悉所有的模块以及模块下的函数
  • 2025牛客寒假算法基础集训营2
  • 落地 ORB角点检测与sift检测
  • 16 分布式session和无状态的会话
  • SpringBoot整合Swagger UI 用于提供接口可视化界面
  • 如何实现滑动开关功能
  • 数仓的数据加工过程-ETL
  • 自动驾驶中的多传感器时间同步
  • CYT3BB_4BB:Clock system
  • C# OpenCV机器视觉:利用CNN实现快速模板匹配
  • 消息队列篇--通信协议扩展篇--二进制编码(ASCII,UTF-8,UTF-16,Unicode等)
  • Direct Preference Optimization (DPO): 一种无需强化学习的语言模型偏好优化方法
  • 跟我学C++中级篇——容器的连接
  • java求职学习day15
  • 【脚本】如何禁用谷歌浏览器自动更新
  • 【Linux】华为服务器使用U盘安装统信操作系统
  • WPF3-在xaml中引用其他程序集的名称空间