Scrapy网络爬虫基础

news2024/10/11 5:53:21

使用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 scrapy


class 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

        # 获取下一页的url
        next_url = response.xpath("//a[@class='nextpage']/@href").extract_first()
        if next_url != None:
            next_url = "https://sc.chinaz.com/tupian/" + next_url
            print("下一页地址是:", 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 DingdianItem


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()

            # 将小说内容保存到Item中
            novel_info = DingdianItem()
            novel_info["name"] = name
            novel_info["new_chapter"] = new_chapter
            novel_info["author"] = author
            novel_info["word_count"] = word_count
            novel_info["update_time"] = update_time
            novel_info["status"] = status
            print("小说信息:", 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 ItemLoader

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:
            # 生成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 TakeFirst


class DingdianItem(scrapy.Item):
    # 定义一个转换函数
    def change_status(status):
        if status[0] == "连载中":
            return 1
        else:
            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):
        # 类初始化函数
        pass
    def 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 DanGaoItem


class 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})

        # 获取下一页的url
        next_url = response.xpath(
            "//a[@class='nextpage']/@href").extract_first()
        if next_url != None:
            next_url = "https://sc.chinaz.com/tupian/" + next_url
            print("下一页地址是:", 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"] = desc
        print("蛋糕图片信息:", dangaoItem)
        yield dangaoItem

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

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

在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

基于SpringBoot智能垃圾分类系统【附源码】

基于SpringBoot智能垃圾分类系统 效果如下: 系统首页界面 用户注册界面 垃圾站点页面 商品兑换页面 管理员登录界面 垃圾投放界面 物业登录界面 物业功能界图 研究背景 随着城市化进程的加速,生活垃圾的产量急剧增加,传统的垃圾分类方式已…

Java 集合 Collection常考面试题

理解集合体系图 collection中 list 是有序的,set 是无序的 什么是迭代器 主要遍历 Collection 集合中的元素,所有实现了 Collection 的集合类都有一个iterator()方法,可以返回一个 iterator 的迭代器。 ArrayList 和 Vector 的区别? ArrayList 可以存放 null,底层是由数…

Oracle RAC IPC Send timeout detected问题分析处理

一、报错信息 今天在进行数据库巡检时,在集群节点1发现了IPC相关报错信息: 2024-10-10T10:22:06.84631708:00 IPC Receiver dump detected. Sender instance 2 Receiver pnum 277 ospid 377527 [oraclezxsszpt-sjkfwq1 (PPA6)], pser 124403 2024-10-1…

飞行机器人专栏(十六)-- 双臂机器人体感交互式控制

目录 1. 概要 2. 整体架构流程 3. 控制系统设计 3.1 Vision-based Human-Robot Interaction Control 3.2 Human Motion Estimation Approach 4. 实现方法及实验验证 4.1 System Implementation 4.2 Experimental Setup 4.3 Experimental Results 5. 小结 ​​​​​​​ 1. 概…

Qt Creator 通过python解释器调用*.py

全是看了大佬们的帖子,结合chatGPT才揉出来。在此做个记录。 安装python在Qt Creator *.pro 文件中配置好环境来个简单的example.py调用代码安装pip添加opencv等库调用包含了opencv库的py代码成功 *.pro配置: INCLUDEPATH C:\Users\xuanm\AppData\Lo…

接口测试-day3-jmeter-2组件和元件

组件和元件: 组件:组件指的是jmeter里面任意一个可以使用的功能。比如说查看结果树或者是http请求 元件:元件指是提对组件的分类 组件的作用域:组件放的位置不一样生效也不一样。 作用域取决于组件的的层级结构并不取决于组件的…

论文阅读:OpenSTL: A Comprehensive Benchmark of Spatio-Temporal Predictive Learning

论文地址:arxiv 摘要 由于时空预测没有标准化的比较,所以为了解决这个问题,作者提出了 OpenSTL,这是一个全面的时空预测学习基准。它将流行的方法分为基于循环和非循环模型两类。OpenSTL提供了一个模块化且可扩展的框架&#xff…

算法: 前缀和题目练习

文章目录 前缀和题目练习前缀和二维前缀和寻找数组的中心下标除自身以外数组的乘积和为 K 的子数组和可被 K 整除的子数组连续数组矩阵区域和 前缀和题目练习 前缀和 自己写出来了~ 坑: 数据太大,要用long. import java.util.Scanner;public class Main {public static voi…

“国货户外TOP1”凯乐石签约实在智能,RPA助力全域电商运营自动化提效

近日,国货第一户外品牌KAILAS凯乐石与实在智能携手合作,基于实在智能“取数宝”自动化能力,打通运营数据获取全链路,全面提升淘宝、天猫、抖音等平台的运营效率与消费者体验,以自动化能力驱动企业增长。 KAILAS凯乐石…

雨晨 24H2 正式版 Windows 11 iot ltsc 2024 适度 26100.2033 VIP2IN1

雨晨 24H2 正式版 Windows 11 iot ltsc 2024 适度 26100.2033 VIP2IN1 install.wim 索引: 1 名称: Windows 11 IoT 企业版 LTSC 2024 x64 适度 (生产力环境推荐) 描述: Windows 11 IoT 企业版 LTSC 2024 x64 适度 By YCDISM 2024-10-09 大小: 15,699,006,618 个字节 索引: 2 …

Jenkins常见问题处理

Jenkins操作手册 读者对象:生产环境管理及运维人员 Jenkins作用:项目自动化构建部署。 一、登陆 二、新增用户及设置权限 2.1:新增用户 点击Manager Jenkins → Manager Users → Create User 2.2:权限 点击Manager Jenkins…

互联网线上融合上门洗衣洗鞋小程序,让洗衣洗鞋像点外卖一样简单

随着服务创新的风潮,众多商家已巧妙融入预约上门洗鞋新风尚,并携手洗鞋小程序,开辟线上蓝海。那么,这不仅仅是一个小程序,它究竟蕴含着哪些诱人好处呢? 1. 无缝融合,双线共赢:小程序…

Corel VideoStudio Ultimate 会声会影2025旗舰版震憾来袭,会声会影2025旗舰版最低系统要求

软件介绍 会声会影2025旗舰版全名:Corel VideoStudio Ultimate 2025,相信做视频剪辑的朋友都认识它,会声会影是一款强大的视频剪辑编辑软件,运用数百种拖放滤镜、效果、图形、标题和过渡,探索新奇好玩的新增面部追踪贴…

彩族相机内存卡恢复多种攻略:告别数据丢失

在数字时代,相机内存卡作为我们存储珍贵照片和视频的重要媒介,其数据安全性显得尤为重要。然而,意外删除、错误格式化、存储卡损坏等情况时有发生,导致数据丢失,给用户带来不小的困扰。本文将详细介绍彩族相机内存卡数…

【万字长文】Word2Vec计算详解(三)分层Softmax与负采样

【万字长文】Word2Vec计算详解(三)分层Softmax与负采样 写在前面 第三部分介绍Word2Vec模型的两种优化方案。 【万字长文】Word2Vec计算详解(一)CBOW模型 markdown行 9000 【万字长文】Word2Vec计算详解(二&#xff0…

初级网络工程师之从入门到入狱(五)

本文是我在学习过程中记录学习的点点滴滴,目的是为了学完之后巩固一下顺便也和大家分享一下,日后忘记了也可以方便快速的复习。 网络工程师从入门到入狱 前言一、链路聚合1.1、手动进行链路聚合1.1.1、 拓扑图:1.1.2、 LSW11.1.3、 LSW2 1.2、…

5.C语言基础入门:数据类型、变量声明与创建详解

C语言基础入门:数据类型、变量声明与创建详解 C语言往期系列文章目录 往期回顾: C语言是什么?编程界的‘常青树’,它的辉煌你不可不知VS 2022 社区版C语言的安装教程,不要再卡在下载0B/s啦C语言入门:解锁…

Elasticsearch 索引数据预处理

pipeline 在文档写入 ES 之前,对数据进行预处理(ingest)工作通过定义 pipeline 和 processors 实现。 注意:数据预处理必须在 Ingest node 节点处理,ES 默认所有节点都是 Ingest node。 如果需要禁用 Ingest &#x…

Java中的拦截器、过滤器及监听器

过滤器(Filter)监听器(Listener)拦截器(Interceptor)关注点web请求系统级别参数、对象Action(部分web请求)如何实现函数回调事件Java反射机制(动态代理)应用场…

《大道平渊》· 廿贰 —— 杀心篇:独立人格的形成

《大道平渊》 独立人格的形成,在杀心的过程中会越来越完备。 在这个漫长的过程中,你会一次次击碎自己固有的三观,慢慢再修复你的三观。 . 不要认为一个人的明白,都是恍然大悟,都是碰到了高人指点。 并不是这样的&a…