【Python】Scrapy整合FastAPI实现爬虫API 附大量示例

news2024/11/15 8:41:26

文章目录

  • 前言
  • 1. 网页分析入门
    • 1.1 基本原理
    • 1.2 Scrapy 原理
  • 2. 创建项目
    • 2.1 创建Scrapy项目
      • 2.2.1 创建Scrapy项目
      • 2.2.2 创建Spider
      • 2.2.3 执行Demo
    • 2.2 引入FastAPI
  • 2. 获取Cookie
  • 3. 数据建模
    • 3.1 Scrapy 数据建模
    • 3.2 SQLAlchemy 创建实体类
  • 3. 分析网页
    • 3.1 xpath 分析
    • 3.2 css 分析
    • 3.3 分页/多级页面策略
  • 4. 管道持久化
    • 4.1 创建管道
  • 5. 补充与总结
    • 5.1 如何在FastAPI触发scrapy?
    • 5.2 总结
  • 参考资料

Python Scarpy 整合 FastAPI

前言

刑啊,我们开始Python爬虫网页分析啦。由于某虫的叫法较为敏感(求生欲拉满),以下均将其称为网页分析吧!

说在前面
网页分析技术,是我们日常学习与工作中常见的需求之一,因此,多少了解甚至会一点网页分析技术,是当今程序猿的基本要求。基本功了属于是。本文基于我实际的工作记录,分享一下Scrapy整合FastAPI个人解决方案

注意,使用网页分析技术,请严格遵守相关法律法规,本文仅供技术学习与交流,代码例子均为虚拟的脱敏示例。

学习目标

  • 会用 Python 进行简单的网页分析实战,将分析到的数据存储到mysql数据表

需求描述
基于某合法业务,分析某网页,并将数据插入到表里。

既然我们要网页分析,首先要选择框架;其次是由于需要将数据保存到表里,为了避免异构造成耦合的麻烦,需要在python代码里直接连接数据库并将数据插入到表里;最后,由于最终是以api的形式给其它服务调用,则需要选择web框架,将分析逻辑做成接口供其它服务调用。

准备工作
基于上面需求描述,本次我们选用

  • fastapi[all] :无脑选择 fastapi 所有依赖,构建fastapi项目,供封装接口使用
  • mysqlclient 与 SQLAlchemy:数据库相关依赖
  • 网页分析依赖:Scrapy

示例:

pip install fastapi[all]
pip install mysqlclient==2.1.1
pip install SQLAlchemy==2.0.23
pip install scrapy

对应 requirements.txt:

fastapi[all]
mysqlclient==2.1.1
SQLAlchemy==2.0.23
scrapy~=2.11.1

1. 网页分析入门

1.1 基本原理

网页分析的原理是模拟浏览器对网页进行访问,并获取访问的信息。这就意味着一般情况下,网页分析只能获取我们平常能在浏览器获取的信息。与浏览网页 + f12查看部分信息并无多大差别。

一般网页分析原理
以 Java 的 Jsoup为例,一般传统的网页分析流程是:发送Http请求获取网页信息 -> 接收网页信息,分析里面的字段 -> 再根据需求与获取的信息,若需要二次请求则进行二次请求,再重复上述步骤,不需要则存储数据。

与攻击的区别
与网络攻击不同的是,攻击是试图破坏系统或获取后台权限以达到不法目的。而网页分析则是在遵守相应法律法规的前提下,合法获取目标服务的信息,以支持实际的运营。

注意
使用网络分析技术,务必要遵循对应的法律法规!

1.2 Scrapy 原理

Scrapy 是一个支持分布式的Python爬虫框架。它在基于传统的网页分析技术原理上进行了优化,如下图所示:
1

图片来源于网络,侵删

Scrapy的核心是Scrapy引擎,通过调度器的队列,调度下载器及爬虫文件的中间结果给引擎,再通过引擎将分析结果给管道做结果的处理。

Scrapy的基本原理如上,而实际操作中,若我们不需要对底层进行细致的研究,整体爬取的流程及代码相对简单。下面我们开始学习如何使用Scrapy并整合FastAPI,让我们的网页分析程序可通过API访问。

2. 创建项目

此小节介绍创建 FastAPI + Scrapy 项目的流程。

2.1 创建Scrapy项目

2.2.1 创建Scrapy项目

进入我们目标项目目录,执行

scrapy startproject <prject_name>

执行上述命令后,生成基础项目,里面包含了Scrapy项目的基本文件。

接下来,我们用pycharm等工具打开,然后需要注意一下是否设置了 python解释器。若没有设置,需要参考如下截图设置并生成.venv文件:
02

然后,在虚拟环境下再执行一遍:

pip install scrapy

这是为了在虚拟环境中也有scrapy相关依赖

2.2.2 创建Spider

terminal 执行 命令,参考如下:

scrapy genspider itcast "itcast.cn"

稍微解释一下,该命令的结构为:

scrapy genspider <spider_name> <domain>

domain为网站域名,意思为要分析的网站范围。后续的url都基于这个域名。要在实际业务使用的同学请将spider_name 和 domain 改成实际的。

那么此处我们就参照参考教程,爬取某客首页吧!

2.2.3 执行Demo

在上一小节执行了genspider命令后,项目里又增加了一些py文件。有点像脚手架。

我们稍作修改,将response保存为文件:

 def parse(self, response):
        filename = "test.html"
        open(filename, 'w').write(response.body)
        pass

执行一下:

scrapy crawl itcast

若成功在项目内生成test.html文件,至此scrapy项目创建与spider创建成功!

2.2 引入FastAPI

引入FastAPI很简单,只需要新建一个main.py,然后参考如下代码:

import uvicorn

from fastapi import FastAPI, applications
from fastapi.openapi.docs import get_swagger_ui_html


# 解决国内Swagger无法正常显示问题
def swagger_monkey_patch(*args, **kwargs):
    """
    fastapi的swagger ui默认使用国外cdn, 所以导致文档打不开, 需要对相应方法做替换
    在应用生效前, 对swagger ui html做替换
    :param args:
    :param kwargs:
    :return:
    """
    return get_swagger_ui_html(
        *args, **kwargs,
        swagger_js_url='https://cdn.staticfile.org/swagger-ui/4.15.5/swagger-ui-bundle.min.js',  # 改用国内cdn
        swagger_css_url='https://cdn.staticfile.org/swagger-ui/4.15.5/swagger-ui.min.css'
    )


applications.get_swagger_ui_html = swagger_monkey_patch

app = FastAPI()
# 指定Swagger 版本为3.0.0
app.openapi_version = "3.0.0"


@app.get("/")
async def root():
    return {"message": "Hello, this is an fastapi project to analyse the website!"}


if __name__ == "__main__":
    uvicorn.run("main:app", host="0.0.0.0", port=8080)

接下来,就可以根据上述代码示例的main方法启动项目,并在swagger查看api接口。FastAPI 默认是swagger路径在 ip:端口号/docs

2. 获取Cookie

网页分析第一步,就是要确保我们所访问的各页面携带登录认证信息。那就是Cookie。

本次我采取的模拟登录策略是采用urllib的方式post请求并获取Cookie,然后将这个模拟登录的方法单独写成一个py文件,供scrapy爬虫调用,示例代码如下:

def get_cookie():
    cookie_list = []
    try:
        url = "http://demo/xxx"
        data = {
            "param1": "1",
            "param2": "sfavxv",
            "username": "username",
            "password": "password"
        }
        data = urllib.parse.urlencode(data).encode('utf-8')
        req = urllib.request.Request(url, data=data, method='POST')
        with urllib.request.urlopen(req) as response:
            # 获取响应头中的 Cookie
            cookies = response.headers.get('Set-Cookie')
            if cookies:
                cookie_list = cookies.split(', ')
            else:
                print("No cookies found in response headers")
    except Exception as e:
        print(e)
    result = get_result(cookie_list)
    cookies_dict = cookie_str_to_dict(result)
    print(cookies_dict)
    return cookies_dict

def get_result(cookie_list):
    result = ""
    if cookie_list:
        # 拼接结果
        result = "; ".join(cookie.split(';')[0] for cookie in cookie_list)
    return result

def cookie_str_to_dict(cookie_str):
    cookie_dict = {}
    cookies = cookie_str.split('; ')
    for cookie in cookies:
        key, value = cookie.split('=')
        # path 不需要
        if key != "Path":
            cookie_dict[key] = value
    # 现阶段手动补充cookie, 后续找到方法再重写
    cookie_dict['cd_1'] = ''
    cookie_dict['iswbzh'] = 1
    return cookie_dict

3. 数据建模

数据建模是开始爬取前的固定步骤之一。数据建模实际上是在Scrapy项目里的item.py ,创建一个继承 scrapy.Item 的 类。我们需要在爬取逻辑中返回这个类的对象,并可将该对象当作字典使用。具体接收这一步,框架内部已经帮我们封装好了。一般开发时我们不必过多关注此步。

3.1 Scrapy 数据建模

数据建模demo代码如下:

class DemoSpiderItem(scrapy.Item):
    # 代码
    code = scrapy.Field()
    # 使用类型
    use_type = scrapy.Field()
    # 使用编号
    use_num = scrapy.Field()

注意,这个类首先要继承scrapy.Item类,然后每个字段都要调用scrapy.Field()。语义上,这表示这个类的字段值都来自爬取的字段。

3.2 SQLAlchemy 创建实体类

在数据获取完毕后,我们需要对数据进行存储。我选择的是使用SQLAlchemy进行存储。因此,在数据建模的同时,我们需要创建SQLAlchemy实体类。这是为了在下一步获取到数据后,可以通过ORM直接执行持久化操作。示例如下:

from sqlalchemy import create_engine, Column, Integer, String, DateTime
from sqlalchemy.ext.declarative import declarative_base

# 创建引擎
engine = create_engine('mysql://root:root@localhost:3306/kcl2024?charset=utf8', echo=True)
Base = declarative_base()

# 定义映射关系类

class Subject(Base):
    __tablename__ = 'subject'
    id = Column(Integer, primary_key=True)
    name = Column(String(255), unique=True, nullable=True)
    score = Column(Integer, unique=False)

3. 分析网页

常见的网页分析获取数据方法有xpath和css,基本上这两种就够用了。前提其它方式,请读者自行了解。

3.1 xpath 分析

调用 response 的 xpath,以下是parse方法里的示例:

	def parse(self, response, *args, **kwargs):
        # 二级页面表格
        result_table = response.xpath('//*[@id="dataForm"]/div/div/table')
        # xpath示例
        company_name = result_table.xpath('//*[@id="SYDWNAME"]/@value').get()
        # 获取其它信息列表
        other_list = response.xpath('//*[@id="tr001"]')
        for result in other_list:
            item['device_code'] = result.xpath('//*[@id="tr001"]/td[4]/text()').get().replace(" ", '')
            item['company_name'] = company_name
            yield item

3.2 css 分析

scrapy 对css分析前,需要先创建Select对象,接下来的操作就很好理解了:

    def parse(self, response, *args, **kwargs):
        selector = Selector(response)
        # 获取css xpath
        maintain_contract_num = selector.xpath('//input[@name="HTBH"]/@value').get()
        # 获取css xpath
        effective_time = selector.xpath('//input[@name="EXPDATE"]/@value').get()
    # 其它逻辑......

3.3 分页/多级页面策略

当我们要分析的是分页/多级页面时,可以在start_requests方法里先进行页面的预处理,也就是说,先手动分析分页的参数,然后在start_requests方法里事先拼接好二级页面的url参数,然后再通过循环等方式请求,将具体的response交给parse方法解析。

当然,也可以再创建几个中间方法执行循环处理,只需要请求后的response 都 yield parse 方法即可。

如下示例:

    def start_requests(self):
        cookies_dict = target_website_cookie.get_cookie()
        # 二级页面初始url
        secondary_start_url = ("http://demo/url")

        # 定义 二级页面 url 所需参数 字典 数组
        secondary_params = []

        # 访问一级页面获取关键信息
        start_response = scrapy.Request(self.start_urls[0], cookies=cookies_dict)

        for element in start_response.xpath('//tr[onclick^=ECSideUtil.selectRow]'):
            tds = element.xpath('td')
            # 提取do_edit的参数
            secondary_id = tds[2].xpath('./input[@onclick]/@onclick').get().split("'")[1]
            # 提取其它信息
            review_status = tds[9].xpath('text()').get()
            # 将id和其它信息存入数组
            secondary_params.append({"secondary_id": secondary_id, "review_status": review_status})

        for secondary_param in secondary_params:
            # 拼接二级页面url
            secondary_url = secondary_start_url + secondary_param["secondary_id"]
            # 携带 cookie 直接访问目标页面
            yield scrapy.Request(secondary_url, cookies=cookies_dict, callback=self.parse, meta=secondary_param)

4. 管道持久化

分析到的数据总得存起来呗。本文我们采用Mysql数据库存储数据,采用Scrapy管道+SQLAlchemy的策略做数据持久化。

在上面数据建模的小节,我们就已经介绍过SQLAlchemy。这一小节,我们需要在管道里进行持久化操作。

4.1 创建管道

class DemoSpiderPipeline:
    # 定义用于接收 item 字典列表
    def __init__(self):
        self.item_list = []

    @staticmethod
    def open_spider(spider):
        if spider.name == "TestSpider":

    def process_item(self, item, spider):
        if spider.name == "TestSpider":
            # 将item转换为字典
            data = dict(item)
            # 将 data 加入 item_list
            self.item_list.append(data)
        return item
    def close_spider(self, spider):
        # 数据存储逻辑
        .....

创建管道主要通过spider名来判断具体执行哪个管道。其中,创建的管道主要有三部分:open_spider、process_item以及close_spider。

其中,open_spider 里 执行的是开启管道时的操作,process_item 通常用于对爬取到的数据进行数据处理。注意,process_item 必须 return item。最后是close_spider方法,我们可以在管道结束时做数据进一步处理以及持久化,然后关闭所需资源。

SqlAlchemy存储示例
上面的示例代码存储逻辑用注释表示。若读者也是使用的SqlAlchemy,可以参考如下代码进行数据存储。

            for temp_dict in self.item_list:
                # 将主键的值设置为空
                temp_dict['id'] = None
                demo_data = DemoClass(temp_dict)
                spider_list.append(demo_data)
            if spider_list:
                with Session(db_init.engine) as session:
                    # 1.数据准备:要增加的列表,要删除的列表,要更新的列表、现有数据
                    insert_list = spider_list
                    delete_list = []

                    # 查询现有的数据
                    try:
                        exist_list = session.query(DemoClass).all()
                        delete_list = exist_list
                    except Exception as e:
                        print(e)

                    # 2.执行删除
                    if delete_list:
                        for delete_obj in delete_list:
                            session.delete(delete_obj)
                        session.commit()

                    # 3.执行新增
                    if insert_list:
                        session.add_all(insert_list)
                        session.commit()

5. 补充与总结

5.1 如何在FastAPI触发scrapy?

已知我们通常在本地执行scrapy crawl spider_name来启动本地爬虫。

若没有特殊需求,我们可以用Python来帮我们执行这个命令,示例FastAPI代码如下:

@app.get("/test")
def galaxy_csel():
    result = ''
    command = 'scrapy crawl testSpider --nolog'
    try:
        result = subprocess.run(command, shell=True, check=True, cwd=".")
    except subprocess.CalledProcessError as e:
        print(f"Error executing command: {e}")
    return {f'execute message:{result}'}

5.2 总结

本文主要基于个人的实际开发,总结了Scrapy的基本用法,并且整合了FastAPI:

  • 项目配置、创建
  • Spider 网页分析的使用
  • 数据建模
  • 管道的使用
  • Alchemy 数据库存储

结语
感谢大家阅读此文,希望我的内容能够给您带来启发和帮助。如果您喜欢我的文章,欢迎点赞、评论和分享,让更多人看到。期待与您在下一篇文章再相会!

参考资料

  • bilibili-Scrapy优秀教程
  • Scrapy- 菜鸟教程

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

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

相关文章

计算机进制转换:二进制、八进制、十进制、十六进制。原码、补码、反码。

一、什么是进制 在生活中&#xff0c;我们通常都是使用阿拉伯数字计数的&#xff0c;也就是10进制&#xff0c;以10为单位&#xff0c;遇10进一&#xff0c;所以是由0&#xff0c;1&#xff0c;2、3、4、5、6、7、8、9组成的&#xff1b;而在计算机中&#xff0c;计算机是无法…

AI工具排行榜:最全工具汇总

如今,人工智能技术正在快速崛起,AI助手、语音识别、机器翻译等工具深深渗透到我们的工作和生活中。这些智能工具极大地提高了我们的工作效率,使我们能更加专注于创造性的任务。 本文将为读者推荐一些实用的AI神器,只要掌握其中一个,就能极大地提升你的工作能力,事半功倍。这些…

YOLOv9代码解读[01] readme解读

文章目录 YOLOv9COCO数据集上指标&#xff1a;环境安装训练验证重参数化 Re-parameterization推断相关链接 YOLOv9 paper: YOLOv9: Learning What You Want to Learn Using Programmable Gradient Information github: https://github.com/WongKinYiu/yolov9 COCO数据集上指…

如何使用Python进行网络安全与密码学【第149篇—密码学】

&#x1f47d;发现宝藏 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。【点击进入巨牛的人工智能学习网站】。 用Python进行网络安全与密码学&#xff1a;技术实践指南 随着互联网的普及&#xff0c;网络…

CSS(四)

一、CSS浮动 1.1 传统网页布局的三种方式 网页布局的本质——用 CSS 来摆放盒子。 把盒子摆放到相应位置. CSS 提供了三种传统布局方式(简单说,就是盒子如何进行排列顺序)&#xff1a; 普通流&#xff08;标准流&#xff09; 浮动 定位 1.2 标准流&#xff08;普通流/文档…

【C++】哈希应用之位图

&#x1f440;樊梓慕&#xff1a;个人主页 &#x1f3a5;个人专栏&#xff1a;《C语言》《数据结构》《蓝桥杯试题》《LeetCode刷题笔记》《实训项目》《C》《Linux》《算法》 &#x1f31d;每一个不曾起舞的日子&#xff0c;都是对生命的辜负 目录 前言 1.位图的概念 2.位…

基于React的低代码平台开发实践

&#x1f482; 个人网站:【 摸鱼游戏】【神级代码资源网站】【工具大全】&#x1f91f; 一站式轻松构建小程序、Web网站、移动应用&#xff1a;&#x1f449;在线地址&#x1f91f; 基于Web端打造的&#xff1a;&#x1f449;轻量化工具创作平台&#x1f485; 想寻找共同学习交…

day07-缓存商品、购物车

1. 缓存菜品 1.1 问题说明 用户端小程序展示的菜品数据都是通过查询数据库获得&#xff0c;如果用户端访问量比较大&#xff0c;数据库访问压力随之增大。 结果&#xff1a; 系统响应慢、用户体验差 1.2 实现思路 通过Redis来缓存菜品数据&#xff0c;减少数据库查询操作。 缓…

Java 在PDF中插入页眉、页脚

在处理PDF文档时&#xff0c;有时需要为文档中的每一页添加页眉和页脚&#xff0c;以包含一些有用的信息&#xff0c;如文档标题、章节名称、日期、页码等。对于需要自动化处理的场景&#xff0c;或者需要在大量文档中添加一致的页眉和页脚&#xff0c;可以通过编程的方式来实现…

java Web餐馆订单管理系统用eclipse定制开发mysql数据库BS模式java编程jdbc

一、源码特点 JSP 餐馆订单管理系统是一套完善的web设计系统&#xff0c;对理解JSP java 编程开发语言有帮助&#xff0c;系统具有完整的源代码和数据库&#xff0c;系统主要采用B/S模式开发。开发环境为TOMCAT7.0,eclipse开发&#xff0c;数据库为Mysql5.0&#xff0c;使…

【超图 SuperMap3D】【基础API使用示例】51、超图SuperMap3D - 绘制圆|椭圆形面标注并将视角定位过去

前言 引擎下载地址&#xff1a;[添加链接描述](http://support.supermap.com.cn/DownloadCenter/DownloadPage.aspx?id2524) 绘制圆形或者椭圆形效果 核心代码 entity viewer.entities.add({// 圆中心点position: { x: -1405746.5243351874, y: 4988274.8462937465, z: 370…

git reset版本回退后悔药(图文例子)

目录 版本回退前期测试样例准备git reset --soft 不撤销add,撤销commit,保留修改git reset --mixed 或 git reset () 撤销add,撤销commit,保存修改git reset --hard 撤销add,撤销commit,不保存修改git reset --merge 取消合并git reset --keep 不撤销add,撤销commit,根据情况判…

数据可视化-ECharts Html项目实战(7)

在之前的文章中&#xff0c;我们学习了如何设置漏斗图、仪表盘。想了解的朋友可以查看这篇文章。同时&#xff0c;希望我的文章能帮助到你&#xff0c;如果觉得我的文章写的不错&#xff0c;请留下你宝贵的点赞&#xff0c;谢谢 数据可视化-ECharts Html项目实战&#xff08;6…

Spring Boot 3核心技术介紹集应用实例

文章目录 核心技术1. **配置管理**&#xff1a;2. **Starter依赖**&#xff1a;3. **自动配置**&#xff1a;4. **启动过程与扩展应用**&#xff1a;5. **日志管理**&#xff1a;6. **数据访问**&#xff1a;7. **计划任务**&#xff1a;8. **缓存**&#xff1a;9. **消息队列*…

RSTP环路避免实验(思科)

华为设备参考&#xff1a;RSTP环路避免实验&#xff08;华为&#xff09; 一&#xff0c;技术简介 RSTP (Rapid Spanning Tree Protocol) 是从STP发展而来 • RSTP标准版本为IEEE802.1w • RSTP具备STP的所有功能&#xff0c;可以兼容STP运行 • RSTP和STP有所不同 减少了…

拥抱C++的深度和复杂性,挖掘更多可能 !——《C++20高级编程(第5版)》

&#xff0c;C难以掌握&#xff0c;但其广泛的功能使其成为游戏和商业软件应用程序中最常用的语言。即使是有经验的用户通常也不熟悉许多高级特性&#xff0c;但C20的发布提供了探索该语言全部功能的绝佳机会。《C20高级编程(第5版)》为C的必要内容提供了一个代码密集型、面向解…

蓝桥杯学习笔记(贪心)

在很久很久以前&#xff0c;有几个部落居住在平原上&#xff0c;依次编号为1到n。第之个部落的人数为 t 有一年发生了灾荒&#xff0c;年轻的政治家小蓝想要说服所有部落一同应对灾荒&#xff0c;他能通过谈判来说服部落进行联台。 每次谈判&#xff0c;小蓝只能邀请两个部落参…

【软考高项】十九、项目管理概论之价值驱动的项目管理知识体系

1、项目管理知识体系概述 重点记忆项目管理涉及内容&#xff1a;12项项目管理原则-10大知识领域&#xff08;10大管理&#xff09;-8大绩效域-5类过程组-4项生命周期-价值交付系统-组织战略和目标实现 2、项目管理原则 勤勉、尊重和关心他人 ①关注组织内部和外部的职责; ②坚…

[深度学习]yolov8+pyqt5搭建精美界面GUI设计源码实现二

【简单介绍】 基于目标检测算法YOLOv8和灵活的PyQt5界面开发框架&#xff0c;我们精心打造了一款集直观性、易用性和功能性于一体的目标检测GUI界面。通过深度整合YOLOv8在目标识别上的卓越能力与PyQt5的精致界面设计&#xff0c;我们成功研发出一款既高效又稳定的软件GUI。 …

Unity 布局控制器Content Size Fitter

Content Size Fitter是Unity中的一种布局控制器组件&#xff0c;用于根据其内容的大小来调整包含它的UI元素的大小。换句话来说就是&#xff0c;Content Size Fitter可以根据UI元素内部内容的大小&#xff0c;自动调整UI元素的大小&#xff0c;以确保内容能够正确显示。 如下图…