Scrapy网络爬虫基础

使用Spider提取数据

Scarpy网络爬虫编程的核心就是爬虫Spider组件,它其实是一个继承与Spider的类,主要功能设计封装一个发送给网站服务器的HTTP请求,解析网站返回的网页及提取数据

执行步骤

1、Spider生成初始页面请求(封装于Request对象中),提交给引擎
2、引擎通知下载按照Request的要求,下载网页文档,再将文档封装成Response对象作为参数传回给Spider
3、Spider解析Response中的网页内容,生成结构化数据Item,或者产生新的请求(比如爬取下一页),再次发送给引擎
4、如果发送给引擎的是新的Request,就继续第2步。如果发送的是结构化数据Item,则引擎通知其他组件处理该数据(保存的文件或数据库中)

class DingdianXuanhuanSpider(scrapy.Spider):# 爬虫名称name = "dingdian_xuanhuan"# 允许的域名allowed_domains = ["www.xiaoshuopu.com"]# 起始URL列表start_urls = ["https://www.xiaoshuopu.com/class_1/"]def parse(self, response):# 小说列表novel_list = response.xpath("//table/tr[@bgcolor='#FFFFFF']")print("小说数量是:", len(novel_list))# 循环获取小说名称、最新章节、作者、字数、更新、状态for novel in novel_list:# 小说名称name = novel.xpath("./td[1]/a[2]/text()").extract_first()# 最新章节new_chapter = novel.xpath("./td[2]/a/text()").extract_first()# 作者author = novel.xpath("./td[3]/text()").extract_first()# 字数word_count = novel.xpath("./td[4]/text()").extract_first()# 更新update_time = novel.xpath("./td[5]/text()").extract_first()# 状态status = novel.xpath("./td[6]/text()").extract_first()# 将小说内容保存到字典中novel_info = {"name": name,"new_chapter": new_chapter,"author": author,"word_count": word_count,"update_time": update_time,"status": status}print("小说信息:",novel_info)# 使用yield返回数据yield novel_info
  • name:必填项,用于区分不同的爬虫。一个Scrapy项目中可以有多个爬虫。不同的爬虫,name值不能相同
  • start_urls:存放要爬取的模板网页地址的列表
  • start_request():爬虫启动时,引擎自动调用该方法,并且只会被调用一次,用于生成初始的请求对象,代码中没有是因为直接使用了基类的功能
  • parse():Spider类的核心方法。引擎将下载好的页面作为参数传递给parse方法,parse方法执行从页面中解析数据的功能

重写start_request方法

如何避免爬虫被网站识别出来导致被禁用呢?
通过重写start_request方法,手动生成一个功能更强大的Request对象。伪装浏览器、自动登录等功能都是在Request对象中设置的

  • 将爬虫伪装成浏览器
  • 设置新的解析数据的回调函数,不使用默认的parse()
class QdYuepiaoSpider(scrapy.Spider):name = "qd_yuepiao"allowed_domains = ["www.qidian.com"]start_urls = ["https://www.qidian.com/rank/yuepiao/"]# 设置代理headers = {"user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36 Edg/127.0.0.0"}# 重写请求def start_requests(self):for url in self.start_urls:yield scrapy.Request(url=url, headers=self.headers, callback=self.parse)def parse(self, response):print("数据:", response.xpath("//div"))

注:上面简单设置headers还是会被一些反爬的网站给识别出来。

更好的方式是在settings中启用并设置user-agent,这样项目下的所用爬虫都能使用到该设置
在这里插入图片描述

USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36 Edg/127.0.0.0"

Request对象

request对象用来描述一个HTTP请求,它通常在Spider中生成并由下载器执行

class Request(url: str,callback: ((...) -> Any) | None = None,method: str = "GET",headers: dict | None = None,body: bytes | str | None = None,cookies: dict | List[dict] | None = None,meta: dict | None = None,encoding: str = "utf-8",priority: int = 0,dont_filter: bool = False,errback: ((...) -> Any) | None = None,flags: List[str] | None = None,cb_kwargs: dict | None = None
)
  • url :HTTP请求的网址
  • callback:指定回调函数,即确定页面的解析函数,默认为parse。在解析期间如果发生异常会调用errback
  • method:请求方式。默认为GET,必须大写英文字母
  • headers:HTTP请求头
  • body:HTTP请求体
  • cookies:请求的Cookie值,可以实现自动登录的效果
  • meta:字典类型,用于数据的传递,可以将数据传递给其他组件,也可以传递给Response对象
  • encoding:请求的编码方式。默认UTF-8
  • priority:请求的优先级,优先级高的优先下载
  • dont_filter:默认值为False,避免对同一个url的重复请求。设置True,即使是重复的请求也会强制下载
  • errback:在处理请求时引发任何异常时调用的函数

多页数据爬取
大多数网站都会存在分页条,进行多个页面数据爬取需要:

在解析函数中,提取完本页数据并提交给引擎后,设法提取到下一页的URL地址,使用这个地址生成新的请求对象,再提交给引擎。

import scrapyclass DangaoSpider(scrapy.Spider):name = "dangao"allowed_domains = ["sc.chinaz.com"]start_urls = ["https://sc.chinaz.com/tupian/dangaotupian.html"]def parse(self, response):# 定位到图片的元素,并保存到列表中img_list = response.xpath("//div[@class='item']/img")for img in img_list:name = img.xpath("./@alt").extract_first()src = img.xpath("./@data-original").extract_first()img_info = {"name": name, "src": src}yield img_info# 获取下一页的urlnext_url = response.xpath("//a[@class='nextpage']/@href").extract_first()if next_url != None:next_url = "https://sc.chinaz.com/tupian/" + next_urlprint("下一页地址是:", next_url)# 生成新的请求对象,并交给引擎执行yield scrapy.Request(url=next_url, callback=self.parse)

在这里插入图片描述

使用Item封装数据

Item对象是一个简单的容器,用于收集抓取到的数据,其提供了类似于字典的API,并具有用于声明可用字段的简单语法

定义Item 和 Field
items.py中创建对应的类

class DingdianItem(scrapy.Item):# 小说名称、作者、最新、字数、更新时间、状态name = scrapy.Field()author = scrapy.Field()new_chapter = scrapy.Field()word_count = scrapy.Field()update_time = scrapy.Field()status = scrapy.Field()

在相应爬虫中使用

import scrapy
from qidian_yuepiao.items import DingdianItemclass DingdianXuanhuanSpider(scrapy.Spider):# 爬虫名称name = "dingdian_xuanhuan"# 允许的域名allowed_domains = ["www.xiaoshuopu.com"]# 起始URL列表start_urls = ["https://www.xiaoshuopu.com/class_1/"]def parse(self, response):# 小说列表novel_list = response.xpath("//table/tr[@bgcolor='#FFFFFF']")print("小说数量是:", len(novel_list))# 循环获取小说名称、最新章节、作者、字数、更新、状态for novel in novel_list:# 小说名称name = novel.xpath("./td[1]/a[2]/text()").extract_first()# 最新章节new_chapter = novel.xpath("./td[2]/a/text()").extract_first()# 作者author = novel.xpath("./td[3]/text()").extract_first()# 字数word_count = novel.xpath("./td[4]/text()").extract_first()# 更新update_time = novel.xpath("./td[5]/text()").extract_first()# 状态status = novel.xpath("./td[6]/text()").extract_first()# 将小说内容保存到Item中novel_info = DingdianItem()novel_info["name"] = namenovel_info["new_chapter"] = new_chapternovel_info["author"] = authornovel_info["word_count"] = word_countnovel_info["update_time"] = update_timenovel_info["status"] = statusprint("小说信息:", novel_info)# 使用yield返回数据yield novel_info

在这里插入图片描述

使用ItemLoader填充容器

在项目很大、提取的字段数很多时,数据提取规则也会越来越多,再加上还要对提取到的数据做转换处理,代码就会变得臃肿,维护起来困难。

为了解决这个问题,Scrapy提供了项目加载器(ItemLoader)这样一个填充容器。通过填充容器,可以配置Item中各个字段的提取规则,并通过函数分析原始数据,最后进行赋值

ItemItemLoader 的区别在于:

Item提供了保存数据的容器,需要手动将数据保存于容器中
ItemLoader提供的是填充容器的机制,提供了3种方法

  • add_xpath:使用xpath选择器提取数据
  • add_css:使用css选择器提取数据
  • add_value:直接传值
import scrapy
from qidian_yuepiao.items import DingdianItem
from scrapy.loader import ItemLoaderclass DingdianXuanhuanSpider(scrapy.Spider):# 爬虫名称name = "dingdian_xuanhuan"# 允许的域名allowed_domains = ["www.xiaoshuopu.com"]# 起始URL列表start_urls = ["https://www.xiaoshuopu.com/class_1/"]def parse(self, response):# 小说列表novel_list = response.xpath("//table/tr[@bgcolor='#FFFFFF']")print("小说数量是:", len(novel_list))# 循环获取小说名称、最新章节、作者、字数、更新、状态for novel in novel_list:# 生成ItemLoader对象novel_info = ItemLoader(item=DingdianItem(),selector=novel)# 小说名称novel_info.add_xpath("name","./td[1]/a[2]/text()")# 最新章节novel_info.add_xpath("author","./td[2]/a/text()")# 作者novel_info.add_xpath("new_chapter","./td[3]/text()")# 字数novel_info.add_xpath("word_count","./td[4]/text()")# 更新novel_info.add_xpath("update_time","./td[5]/text()")# 状态novel_info.add_xpath("status","./td[6]/text()")print("小说信息:", novel_info)

处理数据
使用ItemLoader提取出的数据也是保存于列表中,以前可以通过extract_first()获取列表数据,现在呢?需要使用输入处理器input_processor和输出处理器out_processor

import scrapy
from scrapy.loader.processors import TakeFirstclass DingdianItem(scrapy.Item):# 定义一个转换函数def change_status(status):if status[0] == "连载中":return 1else:return 2# 小说名称、作者、最新、字数、更新时间、状态# 使用内置函数,获取列表中第一个非空数据name = scrapy.Field(output_processor=TakeFirst)author = scrapy.Field(output_processor=TakeFirst)new_chapter = scrapy.Field(output_processor=TakeFirst)word_count = scrapy.Field(output_processor=TakeFirst)update_time = scrapy.Field(output_processor=TakeFirst)status = scrapy.Field(input_processor=change_status, output_processor=TakeFirst)

使用Pipeline封装数据

Spider将收集的数据封装成Item后,将会被传递到Item Pipeline 项目管道组件中等待进一步处理。Scrapy犹如一个爬虫流水线,Item Pipeline是流水线的最后一道工序,它是可选的,默认关闭,使用时需要将它激活。如果需要,也可以定义多个 Item Pipeline组件,数据会依次访问每个组件,执行相应的数据处理功能

典型应用

  • 清理数据
  • 验证数据的有效性
  • 查重并丢弃
  • 将数据按照自定义的格式存储到文件中
  • 将数据保存的数据库中

当创建项目后,会字段生成一个pipelines.py文件,在里面编写自己的Item Pipeline

# 默认生成的
class QidianYuepiaoPipeline:# process_item 是必须实现的,用于处理每一条数据Item# item 是待处理的Item对象,spider是爬取此数据的spider对象def process_item(self, item, spider):# 编写相应的处理逻辑if item["status"] == 1:item["status"] = "连载"else:item["status"] = "完结"return item# 自定义的
class DingdianPipeline:def __init__(self):# 类初始化函数passdef process_item(self, item, spider):# 编写相应的处理逻辑item["status"] = item["status"].replace("连载", "1").replace("完结", "2")return item

启用 Item Pipeline
在配置文件settings.py中启用被注释掉的代码

ITEM_PIPELINES = {"qidian_yuepiao.pipelines.DingdianItemPipeline": 100,"qidian_yuepiao.pipelines.QidianYuepiaoPipeline": 300,
}

格式为项目名.pipelines.对应的类:优先级,数值越小优先级越高。在settings.py中设置后会对所有爬虫都生效。如果想针对每一个爬虫使用某一个,可以在爬虫内部进行指定,例如

class ScrapyASpider(scrapy.Spider):name = 'scrapyA'custom_settings = {'ITEM_PIPELINES': {'myproject.pipelines.MyCustomPipelineForScrapyA': 300,# 其他可能需要的Pipelines...},}# 爬虫的具体逻辑...

保存为其他文件

# 默认生成的
class QidianYuepiaoPipeline:# 文件名称file_name = datetime.now().strftime("%Y%m%d%H%M%S") + ".txt"# 文件对象file = None# Spider开启时,执行打开文件操作def open_spider(self, spider):# 以追加形式打开文件self.file = open(self.file_name, "a", encoding="utf-8")# process_item 是必须实现的,用于处理每一条数据Item# item 是待处理的Item对象,spider是爬取此数据的spider对象def process_item(self, item, spider):# 写入文件self.file.write("名称:"+item["name"] + "\n")return item# 爬虫关闭时,执行关闭文件操作def close_spider(self, spider):self.file.close()

在这里插入图片描述

案例

还是以获取上面获取蛋糕的案例为基础,上面我们获取了蛋糕图片的名称和地址,我们再次基础上获取图片的简介内容

import scrapy
from scarpy_study.items import DanGaoItemclass DangaoSpider(scrapy.Spider):name = "dangao"allowed_domains = ["sc.chinaz.com"]start_urls = ["https://sc.chinaz.com/tupian/dangaotupian.html"]def parse(self, response):# 定位到图片的元素,并保存到列表中img_list = response.xpath("//div[@class='item']")for img_item in img_list:# 获取图片名称和图片地址name = img_item.xpath("./img/@alt").extract_first()src = img_item.xpath("./img/@data-original").extract_first()img_info = {"name": name,"url": src}# 获取图片详情地址detail_url = img_item.xpath("./div[@class='bot-div']/a/@href").extract_first()# print("详情地址:", detail_url)if detail_url != None:detail_url = 'https://sc.chinaz.com' + detail_url# 生成新的请求,并使用meta传递信息yield scrapy.Request(url=detail_url, callback=self.parse_detail,meta={"img_info": img_info})# 获取下一页的urlnext_url = response.xpath("//a[@class='nextpage']/@href").extract_first()if next_url != None:next_url = "https://sc.chinaz.com/tupian/" + next_urlprint("下一页地址是:", next_url)# 生成新的请求对象,并交给引擎执行yield scrapy.Request(url=next_url, callback=self.parse)# 用来解析详情页def parse_detail(self, response):img_info = response.meta["img_info"]print("img_info:", img_info)# 记录图片的名称、地址dangaoItem = DanGaoItem()dangaoItem["name"] = img_info["name"]dangaoItem["url"] = img_info["url"]# 获取描述desc = response.xpath("//p[@class='all-c']/text()").extract_first()dangaoItem["desc"] = descprint("蛋糕图片信息:", dangaoItem)yield dangaoItem

这里获取详情的核心是,在生成新的请求对象时使用callback指定详情信息的解析函数,使用meta来传递之前获取到的图片名称和图片地址

# 生成新的请求,并使用meta传递信息yield scrapy.Request(url=detail_url, callback=self.parse_detail,meta={"img_info": img_info})

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/447197.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【未公开0day】9.9付费进群系统 wxselect SQL注入漏洞【附poc下载】

免责声明:本文仅用于技术学习和讨论。请勿使用本文所提供的内容及相关技术从事非法活动,若利用本文提供的内容或工具造成任何直接或间接的后果及损失,均由使用者本人负责,所产生的一切不良后果均与文章作者及本账号无关。 fofa语…

Java Maven day1014

ok了家人们,今天学习了如何安装和配置Maven项目,我们一起去看看吧 一.Maven概述 1.1 Maven作用 Maven 是专门用于管理和构建 Java 项目的工具,它的主要功能有: 提供了一套标准化的项目结构 提供了一套标准化的构建流程&#x…

【2D/3D-Lidar-SLAM】 2D/3D激光SLAM以及GMapping 与 Cartographer

这里写自定义目录标题 1. 激光SLAM分类2. 2D Lidar SLAM3. 3D Lidar SLAM4. GMapping**1. GMapping 系统架构**1.1 **粒子滤波器Particle Filter**1.2 **运动模型Motion Model**1.3 **传感器模型Sensor Model**1.4 **地图更新Map Update**1.5 **重采样Resampling**1.6 **闭环检…

nbsaas vue3管理后台框架

nbsaas vue3管理后台框架 一、项目概述 Nbsaas Admin Vue 是一个基于 Vue.js 3.0 构建的轻量级后台管理系统,结合了现代前端技术栈的最佳实践,旨在帮助开发者快速构建具有高可扩展性和良好用户体验的后台管理系统。该项目拥有简洁的 UI 设计&#xff0…

【Linux快速入门(二)】Linux与ROS学习之编译基础(make编译)

目录 零.前置篇章 一.make的由来 二.安装make 三.编写Makefile 四.编译运行 五.删除可执行文件 零.前置篇章 第一篇【Linux快速入门】Linux与ROS学习之编译基础(gcc编译)_linuxros-CSDN博客 一.make的由来 "make"是一个用于自…

STL.string(中)

string 迭代器findswapsubstrrfindfind_first_of(用的很少)find_last_of(用的很少)find_first_not_of(用的很少) 迭代器 int main() {//正向迭代器string s1("hello world!");string::iterator i…

力扣 237. 删除链表中的节点【狸猫换太子】

题目 解题 该题中链表节点的值都是唯一的,且只给出待删除的节点 node,而没有给出 head,显然是不可以遍历链表找到相应值来进行删除节点的。注意到题目只要求给定节点的值不在链表中,且链表节点个数减少一个即可,并非严…

起吊机革新:协议转换器解锁安全与效率

重工起吊机设备在工业生产中扮演着至关重要的角色,但其在实际应用中面临着一系列痛点问题。这些问题不仅影响了起吊机的性能和安全性,还限制了生产效率的提升。我们自主研发的MG协议转换器能够高效解决这些痛点,同时MG协议转换器作为一种关键…

第十五届蓝桥杯C/C++学B组(解)

1.握手问题 解题思路一 数学方法 50个人互相握手 (491)*49/2 ,减去7个人没有互相握手(61)*6/2 答案:1024 解题思路二 思路: 模拟 将50个人从1到50标号,对于每两个人之间只握一…

[Linux] 逐层深入理解文件系统 (2)—— 文件重定向

标题:[Linux] 逐层深入理解文件系统 (2)—— 文件重定向 个人主页水墨不写bug (图片来源于网络) 目录 一、文件的读取和写入 二、文件重定向的本质 1.手动模拟重定向的过程——把标准输出重定向到redir.txt 2.重定向…

分享两种安装windows系统教程,学会后再也不需要花钱装系统了。

前期准备工作: 需要一个8G或16G的空U盘需要你安装的系统的镜像文件 一般是一个以 .iso 后缀结尾的文件 2.1 镜像文件获取方式 1) 去windows 官网获取 2)去 我告诉你 网址下载所需要的镜像文件 这个网址 分享了很多 我们常用的系统 大家可以按…

C++面向对象--------继承篇

目录 一.继承(重点) 1.1 概念 1.2 构造函数 1.2.1 派生类与基类的构造函数关系 1.2.2 解决方案 1.2.2.1 补充基类的无参构造函数 1.2.2.2 手动在派生类中调用基类构造函数 1.2.2.2.1 透传构造 1.2.2.2.2 委托构造 1.2.2.2.3 继承构造 1.3 对象…

中标麒麟v5安装qt512.12开发软件

注意 需要联网操作 遇到问题1:yum提示没有可用软件包问题 终端执行如下命令 CentOS7将yum源更换为国内源保姆级教程 中标麒麟V7-yum源的更换(阿里云源) wget -O /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Cento…

mysql 慢查询日志slowlog

慢查询参数 slow log 输出示例 # Time: 2024-08-08T22:39:12.80425308:00 #查询结束时间戳 # UserHost: root[root] localhost [] Id: 83 # Query_time: 2.331306 Lock_time: 0.000003 Rows_sent: 9762500 Rows_examined: 6250 SET timestamp1723127950; select *…

PS证件照换底色

ps工具:Adobe Photoshop 2021 文章目录 1. 扣取人物2. 更换底色 1. 扣取人物 2. 更换底色

SwiftUI 6.0(iOS 18)自定义容器值(Container Values)让容器布局渐入佳境(上)

概述 我们在之前多篇博文中已经介绍过 SwiftUI 6.0(iOS 18)新增的自定义容器布局机制。现在,如何利用它们对容器内容进行“探囊取物”和“聚沙成塔”,我们已然胸有成竹了。 然而,除了上述鬼工雷斧般的新技巧之外&…

10月15日 -- 11月15日 ,参与《人工智能导论》学习打卡赢B站大会员

一、活动参与地址 点击链接进行活动报名>>>https://momodel.cn/classroom/course/detail?id6173911eab37f12b14daf4a8&activeKeyinfo&srcbef3adb478 二、活动详情 进入链接点击报名,仅需每天参与吴超老师的《人工智能导论》打卡活动&#xff0…

NPCAP和WPCAP

NPCAP是专为Windows开发的一款网络抓包SDK,该SDK提供了被应用程序调用的库文件和系统驱动程序。通过Npcap,我们可以得到原始网络数据,即未经过TCP/IP协议栈的数据,也就是网卡收到的数据,同时呢,我们也可以通过NPCAP设置接收过滤器,这样收到的数据就是我们感兴趣的数据,…

[C++ 核心编程]笔记 4.1.4 类和对象 - 案例1

类和对象: 案例1: 设计立方体类(Cube) 求出立方体的面积和体积分别用全局函数和成员函数判断两个立方体是否相等。 设计方法: 创建立方体类设计属性设计行为 求立方体面积和体积分别用全局和成员函数 判断立方体是否相等 #include<iostream> using namespace std;clas…

正则表达式-“三剑客”(grep、sed、awk)

1.3正则表达式 正则表达式描述了一种字符串匹配的模式&#xff0c;可以用来检查一个串是否含有某种子串&#xff0c;将匹配的子串替换或者从某个串中取出符号某个条件的子串等&#xff0c;在linux中代表自定义的模式模版&#xff0c;linux工具可以用正则表达式过滤文本。Linux…