前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >音视频八股文(11)-- ffmpeg avio 内存输入和内存输出。内存输出有完整代码,网上很少有的。

音视频八股文(11)-- ffmpeg avio 内存输入和内存输出。内存输出有完整代码,网上很少有的。

原创
作者头像
福大大架构师每日一题
发布于 2023-05-09 14:41:19
发布于 2023-05-09 14:41:19
2.1K00
代码可运行
举报
运行总次数:0
代码可运行

1.avio介绍

avio是FFmpeg中的一个模块,用于实现多种输入输出方式的封装。

avio提供了一系列API,可以将数据从内存读取到缓冲区中,也可以将缓冲区中的数据写入到内存中。其实现依赖于IOContext结构体,该结构体定义了当前输入/输出事件的状态、数据、回调函数等信息,并支持通过自定义回调函数实现不同的输入/输出方式。

内存输入(Memory Input)是指将数据从内存中读取到缓冲区中,常见的应用场景包括:从内存中读取音视频数据进行解码或处理。在使用avio实现内存输入时,需要首先创建一个AVIOContext结构体,并将内存数据缓冲区作为参数传递给avio_open函数进行初始化。之后,可以使用avio_read函数从缓冲区中读取数据,直至读取完成。

内存输出(Memory Output)是指将数据从缓冲区中写入到内存中,常见的应用场景包括:将音视频数据编码并保存到内存中。在使用avio实现内存输出时,需要首先创建一个AVIOContext结构体,并将内存数据缓冲区和缓冲区大小作为参数传递给avio_open函数进行初始化。之后,可以使用avio_write函数将数据写入缓冲区中,并在完成输出后调用avio_close函数关闭AVIOContext结构体。

总的来说,内存输入和输出是指在使用FFmpeg进行音视频处理时,将数据从内存中读取或写入到内存中的一种方式。使用avio模块可以方便地实现这种输入输出方式,并支持自定义回调函数以满足不同的应用需求。

2.为什么要用avio?

使用FFmpeg的avio模块实现内存输入和输出有以下几个优点:

2.1.灵活性高

传统的音视频处理方式往往需要将音视频数据保存到文件中,然后再进行读取和处理。而使用avio模块可以将数据直接读取或写入到内存中,从而提高了音视频处理的灵活性。这种方式可以避免繁琐的文件IO操作,节省磁盘空间。

2.2.扩展性强

内存输入和输出功能可以方便地扩展为其他更高级的应用程序,例如:流媒体服务器实时音视频采集与处理等。这是因为内存输出能够较为轻松地将音视频数据编码并存储到内存缓冲区中,进而交由网络传输;内存输入则可直接从内存缓冲区获取音视频数据,快速响应用户请求。

2.3.可定制性好

FFmpeg的avio模块提供了一系列API,可以通过设置回调函数实现各种自定义功能。例如:自定义网络协议传输方式、增加错误重试机制、实现多路复用等。这使得处理器可以根据自己的需求对avio模块进行灵活配置,以最大限度地满足不同场景下的业务需求。

因此,使用FFmpeg的avio模块实现内存输入和输出可以提高音视频处理的效率,增加程序的灵活性和扩展性,同时还具有良好的可定制性。

3.内存区作为输入

3.1.回调函数何时被回调呢?

所有需要从输入源中读取数据的时刻,都将调用回调函数。和输入源是普通文件相比,只不过输入源变成了内存区,其他各种外在表现并无不同。

如下各函数在不同的阶段从输入源读数据,都会调用回调函数:

avformat_open_input() 从输入源读取封装格式文件头

avformat_find_stream_info() 从输入源读取一段数据,尝试解码,以获取流信息

av_read_frame() 从输入源读取数据包

3.2.该示例作用是统计mp4文件的视频帧数,代码如下:

代码语言:c
代码运行次数:0
运行
AI代码解释
复制
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libavformat/avio.h>
#include <libavutil/file.h>

#define INPUT_FILE "1.mp4"

struct buffer_data {
    uint8_t* ptr;
    size_t size; ///< size left in the buffer
};

static int read_packet(void* opaque, uint8_t* buf, int buf_size)
{
    struct buffer_data* bd = (struct buffer_data*)opaque;
    buf_size = FFMIN(buf_size, bd->size);

    if (!buf_size)
        return AVERROR_EOF;
    printf("ptr:%p size:%zu\n", bd->ptr, bd->size);

    /* copy internal buffer data to buf */
    memcpy(buf, bd->ptr, buf_size);
    bd->ptr += buf_size;
    bd->size -= buf_size;

    return buf_size;
}

int main(int argc, char* argv[])
{
    AVFormatContext* fmt_ctx = NULL;
    AVIOContext* avio_ctx = NULL;
    uint8_t* buffer = NULL, * avio_ctx_buffer = NULL;
    size_t buffer_size, avio_ctx_buffer_size = 4096;
    char* input_filename = NULL;
    int ret = 0;
    struct buffer_data bd = { 0 };
    int  videoStreamIndex = -1;
    AVCodecParameters* avCodecPara = NULL;
    const AVCodec* codec = NULL;
    AVCodecContext* codecCtx = NULL;
    AVPacket* pkt = NULL;

    input_filename = INPUT_FILE;

    /* slurp file content into buffer */
    ret = av_file_map(input_filename, &buffer, &buffer_size, 0, NULL);
    if (ret < 0)
        goto end;

    /* fill opaque structure used by the AVIOContext read callback */
    bd.ptr = buffer;
    bd.size = buffer_size;

    if (!(fmt_ctx = avformat_alloc_context())) {
        ret = AVERROR(ENOMEM);
        goto end;
    }

    avio_ctx_buffer = av_malloc(avio_ctx_buffer_size);
    if (!avio_ctx_buffer) {
        ret = AVERROR(ENOMEM);
        goto end;
    }
    avio_ctx = avio_alloc_context(avio_ctx_buffer, avio_ctx_buffer_size,
        0, &bd, &read_packet, NULL, NULL);
    if (!avio_ctx) {
        ret = AVERROR(ENOMEM);
        goto end;
    }
    fmt_ctx->pb = avio_ctx;

    ret = avformat_open_input(&fmt_ctx, NULL, NULL, NULL);
    if (ret < 0) {
        fprintf(stderr, "Could not open input\n");
        goto end;
    }

    ret = avformat_find_stream_info(fmt_ctx, NULL);
    if (ret < 0) {
        fprintf(stderr, "Could not find stream information\n");
        goto end;
    }

    //av_dump_format(fmt_ctx, 0, input_filename, 0);
    printf("完成\n");

    //循环查找视频中包含的流信息,直到找到视频类型的流
    //便将其记录下来 保存到videoStreamIndex变量中
    for (unsigned int i = 0; i < fmt_ctx->nb_streams; i++) {
        if (fmt_ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
            videoStreamIndex = i;
            break;//找到视频流就退出
        }
    }

    //如果videoStream为-1 说明没有找到视频流
    if (videoStreamIndex == -1) {
        printf("cannot find video stream\n");
        goto end;
    }

    //=================================  查找解码器 ===================================//
    avCodecPara = fmt_ctx->streams[videoStreamIndex]->codecpar;
    codec = avcodec_find_decoder(avCodecPara->codec_id);
    if (codec == NULL) {
        printf("cannot find decoder\n");
        goto end;
    }
    //根据解码器参数来创建解码器内容
    codecCtx = avcodec_alloc_context3(codec);
    avcodec_parameters_to_context(codecCtx, avCodecPara);
    if (codecCtx == NULL) {
        printf("Cannot alloc context.");
        goto end;
    }

    //================================  打开解码器 ===================================//
    if ((ret = avcodec_open2(codecCtx, codec, NULL)) < 0) { // 具体采用什么解码器ffmpeg经过封装 我们无须知道
        printf("cannot open decoder\n");
        goto end;
    }

    //=========================== 分配AVPacket结构体 ===============================//
    int       i = 0;//用于帧计数
    pkt = av_packet_alloc();                      //分配一个packet
    av_new_packet(pkt, codecCtx->width * codecCtx->height); //调整packet的数据

    //===========================  读取视频信息 ===============================//
    while (av_read_frame(fmt_ctx, pkt) >= 0) { //读取的是一帧视频  数据存入一个AVPacket的结构中
        if (pkt->stream_index == videoStreamIndex) {
            i++;//只计算视频帧
        }
        av_packet_unref(pkt);//重置pkt的内容
    }
    printf("There are %d frames int total.\n", i);

end:
    avformat_close_input(&fmt_ctx);

    /* note: the internal buffer could have changed, and be != avio_ctx_buffer */
    if (avio_ctx)
        av_freep(&avio_ctx->buffer);
    avio_context_free(&avio_ctx);
    av_packet_free(&pkt);
    avcodec_close(codecCtx);
    av_file_unmap(buffer, buffer_size);
    avformat_free_context(fmt_ctx);

    if (ret < 0) {
        fprintf(stderr, "Error occurred: %s\n", av_err2str(ret));
        return 1;
    }

    return 0;
}
在这里插入图片描述
在这里插入图片描述

4.内存区作为输出

4.1.回调函数何时被回调呢?

所有输出数据的时刻,都将调用回调函数。和输出是普通文件相比,只不过输出变成了内存区,其他各种外在表现并无不同。

如下各函数在不同的阶段会输出数据,都会调用回调函数:

avformat_write_header() 将流头部信息写入输出区

av_interleaved_write_frame() 将数据包写入输出区

av_write_trailer() 将流尾部信息写入输出区

4.2.该示例作用是提取mp4文件的视频帧为h264文件,输出采用write_packet回调,代码如下:

代码语言:c
代码运行次数:0
运行
AI代码解释
复制
//https://www.cnblogs.com/leisure_chn/p/10318145.html
#include <stdio.h>
#include <stdlib.h>

#include <libavformat/avformat.h>

#define INPUT_FILE "1.mp4"
#define OUTPUT_FILE "output.h264"

typedef struct {
    FILE* fp;
} OutputContext;

static int write_packet(void* opaque, uint8_t* buf, int buf_size)
{
    OutputContext* output_ctx = (OutputContext*)opaque;
    FILE* fp = output_ctx->fp;

    fwrite(buf, 1, buf_size, fp);

    return buf_size;
}

int main(int argc, char* argv[])
{
    AVFormatContext* input_ctx = NULL;
    AVOutputFormat* output_fmt = NULL;
    AVFormatContext* output_ctx = NULL;
    OutputContext* output_opaque = NULL;

    int ret = avformat_open_input(&input_ctx, INPUT_FILE, NULL, NULL);
    if (ret < 0) {
        fprintf(stderr, "Error: Could not open input file: %s.\n", av_err2str(ret));
        goto end;
    }

    ret = avformat_find_stream_info(input_ctx, NULL);
    if (ret < 0) {
        fprintf(stderr, "Error: Could not find stream information: %s.\n", av_err2str(ret));
        goto end;
    }

    output_fmt = av_guess_format("h264", NULL, NULL);
    if (!output_fmt) {
        fprintf(stderr, "Error: Could not guess output format.\n");
        ret = AVERROR_MUXER_NOT_FOUND;
        goto end;
    }

    ret = avformat_alloc_output_context2(&output_ctx, output_fmt, NULL, OUTPUT_FILE);
    if (ret < 0) {
        fprintf(stderr, "Error: Could not allocate output context: %s.\n", av_err2str(ret));
        goto end;
    }

    AVStream* in_video_stream = NULL;
    AVCodecParameters* in_codec_params = NULL;
    for (int i = 0; i < input_ctx->nb_streams; i++) {
        AVStream* stream = input_ctx->streams[i];
        if (stream->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
            in_video_stream = stream;
            in_codec_params = stream->codecpar;
            break;
        }
    }
    if (!in_video_stream) {
        fprintf(stderr, "Error: Could not find video stream.\n");
        ret = AVERROR(ENOSYS);
        goto end;
    }

    AVStream* out_video_stream = avformat_new_stream(output_ctx, NULL);
    if (!out_video_stream) {
        fprintf(stderr, "Error: Could not create new stream.\n");
        ret = AVERROR(ENOMEM);
        goto end;
    }

    ret = avcodec_parameters_copy(out_video_stream->codecpar, in_codec_params);
    if (ret < 0) {
        fprintf(stderr, "Error: Could not copy codec parameters: %s.\n", av_err2str(ret));
        goto end;
    }

    out_video_stream->codecpar->codec_tag = 0;

    output_opaque = av_malloc(sizeof(OutputContext));
    if (!output_opaque) {
        fprintf(stderr, "Error: Could not allocate output context.\n");
        ret = AVERROR(ENOMEM);
        goto end;
    }

    fopen_s(&output_opaque->fp, OUTPUT_FILE, "wb");
    if (!output_opaque->fp) {
        fprintf(stderr, "Error: Could not open output file.\n");
        ret = AVERROR(ENOENT);
        goto end;
    }

    AVIOContext* pb = NULL;
    pb = avio_alloc_context((unsigned char*)av_malloc(32768), 32768, 1, output_opaque, NULL, &write_packet, NULL);
    if (!pb) {
        fprintf(stderr, "Error: Could not allocate output IO context.\n");
        ret = AVERROR(ENOMEM);
        goto end;
    }
    output_ctx->pb = pb;

    ret = avformat_write_header(output_ctx, NULL);
    if (ret < 0) {
        fprintf(stderr, "Error: Could not write header: %s.\n", av_err2str(ret));
        goto end;
    }

    AVPacket packet = { 0 };
    while (av_read_frame(input_ctx, &packet) >= 0) {
        if (packet.stream_index == in_video_stream->index) {
            av_packet_rescale_ts(&packet, in_video_stream->time_base, out_video_stream->time_base);
            packet.stream_index = out_video_stream->index;

            ret = av_interleaved_write_frame(output_ctx, &packet);
            if (ret < 0) {
                fprintf(stderr, "Error: Could not write frame: %s.\n", av_err2str(ret));
                goto end;
            }
        }

        av_packet_unref(&packet);
    }

    ret = av_write_trailer(output_ctx);
    if (ret < 0) {
        fprintf(stderr, "Error: Could not write trailer: %s.\n", av_err2str(ret));
        goto end;
    }

    printf("Conversion complete!\n");
end:
    if (input_ctx) {
        avformat_close_input(&input_ctx);
    }
    if (output_ctx) {
        if (output_ctx->pb) {
            av_freep(&output_ctx->pb->buffer);
            avio_context_free(&output_ctx->pb);
        }
        if (output_opaque->fp) {
            fclose(output_opaque->fp);
        }
        avformat_free_context(output_ctx);
        av_free(output_opaque);
    }
    return ret;
}
在这里插入图片描述
在这里插入图片描述

5.内存IO模式非常重要的一个函数:avio_alloc_context()

代码语言:c
代码运行次数:0
运行
AI代码解释
复制
/**
 * Allocate and initialize an AVIOContext for buffered I/O. It must be later
 * freed with avio_context_free().
 *
 * @param buffer Memory block for input/output operations via AVIOContext.
 *        The buffer must be allocated with av_malloc() and friends.
 *        It may be freed and replaced with a new buffer by libavformat.
 *        AVIOContext.buffer holds the buffer currently in use,
 *        which must be later freed with av_free().
 * @param buffer_size The buffer size is very important for performance.
 *        For protocols with fixed blocksize it should be set to this blocksize.
 *        For others a typical size is a cache page, e.g. 4kb.
 * @param write_flag Set to 1 if the buffer should be writable, 0 otherwise.
 * @param opaque An opaque pointer to user-specific data.
 * @param read_packet  A function for refilling the buffer, may be NULL.
 *                     For stream protocols, must never return 0 but rather
 *                     a proper AVERROR code.
 * @param write_packet A function for writing the buffer contents, may be NULL.
 *        The function may not change the input buffers content.
 * @param seek A function for seeking to specified byte position, may be NULL.
 *
 * @return Allocated AVIOContext or NULL on failure.
 */
AVIOContext *avio_alloc_context(
                  unsigned char *buffer,
                  int buffer_size,
                  int write_flag,
                  void *opaque,
                  int (*read_packet)(void *opaque, uint8_t *buf, int buf_size),
                  int (*write_packet)(void *opaque, uint8_t *buf, int buf_size),
                  int64_t (*seek)(void *opaque, int64_t offset, int whence));

这是FFmpeg中用于创建AVIOContext结构体的函数 avio_alloc_context 的代码注释。

该函数具有以下参数:

  • buffer:存储音视频数据的内存缓冲区指针,必须通过 av_malloc() 等函数分配。该内存块会被 AVIOContext 结构体引用,不能在生命周期内被释放。
  • buffer_size:缓冲区大小,对于固定块大小的协议需要设置为固定块大小,对于其他协议可以设置为典型缓存页大小,例如 4KB。
  • write_flag:标记是否可写,1 表示可写,0 表示只读。
  • opaque:用户指定的不透明指针,用于在回调函数中携带自定义数据。
  • read_packet:read_packet 回调函数,用于本地文件或网络流传输时从输入源中读取数据。当 buffer 中的数据被消耗完后,调用此函数填充缓冲区。
  • write_packet:write_packet 回调函数,在可写模式下用于将缓冲区中的数据写入输出源,例如本地文件或网络流。
  • seek:seek 回调函数,用于跳转到指定字节位置。

该函数主要用于在 FFmpeg 内部创建一个 AVIOContext 结构体,该结构体用于管理读取或写入内存缓冲区的音视频数据,并提供了一些 API 函数用于处理缓冲区数据。一旦创建了 AVIOContext 结构体,就可以通过调用 avio_open2() 函数来打开对应的输入或输出资源,然后即可开始读写数据。

在使用完毕后,需要通过调用 avio_context_free() 函数来释放 AVIOContext 结构体占用的内存空间。

6.两个示例的环境

操作系统:win10 64位

开发环境:VS2022

vcpkg命令:

代码语言:shell
AI代码解释
复制
vcpkg install ffmpeg:x64-windows
在这里插入图片描述
在这里插入图片描述

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
暂无评论
推荐阅读
利用Python爬取淘宝商品信息分析设计程序结构核心代码完整代码
爬取这个商品名称,比如“手机”搜索结果下的每个商品的信息,存储到数据结构中,并能将其输出显示。
desperate633
2018/08/27
8440
利用Python爬取淘宝商品信息分析设计程序结构核心代码完整代码
用Python爬取分析【某东618】畅销商品销量数据,带你看看大家都喜欢买什么!
今天是618购物节,辰哥准备分析一波购物节大家都喜欢买什么?本文以某东为例,Python爬取618活动的畅销商品数据,并进行数据清洗,最后以可视化的方式从不同角度去了解畅销商品中,名列前茅的商品是哪些?销售数据如何?用户好评如何?等等
Python研究者
2021/06/22
7340
用Python爬取分析【某东618】畅销商品销量数据,带你看看大家都喜欢买什么!
用Python爬取分析【某东618】畅销商品销量数据,带你看看大家都喜欢买什么!
618购物节,辰哥准备分析一波购物节大家都喜欢买什么?本文以某东为例,Python爬取618活动的畅销商品数据,并进行数据清洗,最后以可视化的方式从不同角度去了解畅销商品中,名列前茅的商品是哪些?销售数据如何?用户好评如何?等等
Python研究者
2021/06/19
1.3K1
独家揭秘:小程序如何借势热门营销玩法,流量订单双丰收
嘿,各位小伙伴们!在如今这个互联网飞速发展的时代,小程序就像是一颗璀璨的新星,在商业的天空中闪耀着独特的光芒。它轻巧便捷,无需下载安装,用户轻轻一点就能快速使用,简直就是 “互联网界的小精灵”。但是呢,在这竞争激烈的小程序世界里,想要脱颖而出,实现流量订单双丰收,可不是一件容易的事儿哦。别着急,今天小编就来给大家独家揭秘,小程序如何巧妙借势热门营销玩法,走上人生巅峰!
小白的大数据之旅
2025/03/19
1560
这个横行霸道的美食,也是中秋节的一大特色,我用Python爬取1546条数据带大家看看
中秋节除了月饼之外,还有一道美食也开始闯入人们的餐桌,这边是横行"霸道"的大闸蟹!
可以叫我才哥
2021/09/24
3040
营销模块数据库表解析:优惠券功能
本文主要对优惠券功能相关表进行解析,采用数据库表与功能对照的形式。 相关表结构 优惠券表 用于存储优惠券信息,需要注意的是优惠券的使用类型:0->全场通用;1->指定分类;2->指定商品,不同使用类型的优惠券使用范围不一样。 create table sms_coupon ( id bigint not null auto_increment, type int(1) comment '优惠卷类型;0->全场赠券;1->会员赠券
macrozheng
2019/08/13
3K0
营销模块数据库表解析:优惠券功能
订单模块数据库表解析(二)
本文主要对购物车功能相关表进行解析,介绍从商品加入购物车到下单的整个流程,涉及购物车优惠计算流程、确认单生成流程、下单流程及取消订单流程。 购物车表 用于存储购物车中每个商品信息,可用于计算商品优惠金额。 create table oms_cart_item ( id bigint not null auto_increment, product_id bigint comment '商品的id', product_sku_id
macrozheng
2019/07/24
9220
订单模块数据库表解析(二)
python爬取『大年初一』热映电影,以『可视化及词云秀』方式带你了解热映电影
今年给大家爬取『大年初一』上映的几部热门数据(评分、时长、类型)以及相关网友评论等数据
Python研究者
2021/03/05
4630
python爬取『大年初一』热映电影,以『可视化及词云秀』方式带你了解热映电影
用python爬取4332条粽子数据进行分析
爬取淘宝数据,本次采用的方法是:Selenium控制Chrome浏览器自动化操作[1]。其实我们还可以利用Ajax接口来构造链接,但是非常繁琐(包含加密秘钥等),直接使用Selenium来模拟浏览器会省去很多事情;
朱小五
2020/06/28
5800
用python爬取4332条粽子数据进行分析
淘宝商品比价爬虫
一、前提准备 1、功能描述 获取淘宝搜索页面的信息,提取其中的商品名称和价格。 2、分析页面 ①先确定搜索url 根据上图我们可以看到url为:https://s.taobao.com/sea
不温卜火
2020/10/28
1.2K0
淘宝商品比价爬虫
用python爬取4332条粽子数据进行分析,看哪个最受大家欢迎
爬取淘宝数据,本次采用的方法是:Selenium控制Chrome浏览器自动化操作[1]。其实我们还可以利用Ajax接口来构造链接,但是非常繁琐(包含加密秘钥等),直接使用Selenium来模拟浏览器会省去很多事情;
朱小五
2020/06/18
7593
用python爬取4332条粽子数据进行分析,揭秘甜咸肉粽的江湖!
端午节快要到了,甜咸粽子之争也快要拉开帷幕。 小五准备用Python爬取淘宝上的粽子数据并进行分析,看看有什么发现。 爬虫 爬取淘宝数据,本次采用的方法是:Selenium控制Chrome浏览器自动化操作[1]。其实我们还可以利用Ajax接口来构造链接,但是非常繁琐(包含加密秘钥等),直接使用Selenium来模拟浏览器会省去很多事情; 之前的文章我们也用过相同的方法,比如:爬电脑、爬电脑、爬完电脑买不起 最常见的问题是chromedriver驱动与谷歌浏览器的版本不匹配,很容易就可以解决。接下来,我
腾讯大讲堂
2020/06/28
5170
python爬取淘宝全部『螺蛳粉』数据,看看你真的了解螺蛳粉吗?
上一篇文章(爬取淘宝热卖商品并可视化分析,看看大家都喜欢买什么!)爬取分析了淘宝的热卖商品,从分析来看『螺蛳粉』的销量巨高。因此这篇文章将爬取淘宝全部『螺蛳粉』商品数据,通过可视化分析淘宝螺蛳粉的一些秘密!
Python研究者
2021/03/05
8391
python爬取淘宝全部『螺蛳粉』数据,看看你真的了解螺蛳粉吗?
多线程爬虫优化:快速爬取并写入CSV
在数据驱动的时代,爬虫技术已成为获取网络数据的重要手段。然而,随着数据量的不断增加,单线程爬虫的效率逐渐难以满足需求。多线程爬虫通过并行处理,能够显著提升爬取速度,同时将数据快速写入CSV文件,为后续的数据分析和应用提供支持。本文将详细介绍多线程爬虫的优化策略,并通过一个完整的实战案例展示如何实现高效的数据爬取和存储。
小白学大数据
2025/06/16
1640
我fork的110+star的newbee-mall商城V2.2.0发布啦
本项目是在newbee-mall项目的基础上改造而来, 使用mybatis-plus,集成RedisSearch作为商城搜索中间件,商城首页集成tianai-captcha作为滑块验证码,还添加了高级秒杀、优惠劵以及完善可用的后台全部功能,喜欢的话麻烦给我个star
wayn
2023/02/01
3930
电商数据分析-02-电商业务介绍及表结构
电商业务简介 大数据项目之电商数仓、电商业务简介、电商业务流程、电商常识、业务数据介绍、电商业务表、后台管理系统
IT从业者张某某
2023/12/27
6030
电商数据分析-02-电商业务介绍及表结构
用Python+PPT对某宝月饼数据进行可视化分析~
过几天就中秋了,小刀还没收到公司送的月饼 ,只能用羡慕的眼神看着女朋友她们公司早在半个月前就送的广州酒家月饼,所以为了不羡慕,今天用python+ppt带大家看看淘宝的月饼怎么样,呃呃,只看不买...顺便想想怎么提醒公司送!
Python与Excel之交
2021/09/28
1K0
我用Python分析1585家电商车厘子销售数据,发现这些秘密
智利驻华大使馆商务参赞娜塔曾表示:“2020-2021产季车厘子实现了丰收,预计今年有50万吨左右的车厘子进入中国市场。”自2020年12月中旬开始,智利海运车厘子陆续到达中国,运输成本较此前空运方式大幅下滑。这意味着,国内消费者将能以更低的价格买到车厘子。然而,近日国内已有多地进口车厘子核酸检测结果为阳性,在这种情况下,你还敢大呼“车厘子自由”吗?
Python进击者
2021/03/09
5550
我用Python分析1585家电商车厘子销售数据,发现这些秘密
用Python爬取淘宝4403条大裤衩数据进行分析,终于找到可以入手的那一条
炎炎夏日,长裤已难以满足广大男生的需求,为了在搬砖和摆摊的过程中增添一丝舒适感,他们开始寻找一种神奇的存在——大裤衩。J哥在种菜的这些日子里也日益感受到大裤衩的重要性,于是,默默打开了淘宝并搜索了大裤衩,但翻了半天也不知道买啥。
Python进阶者
2020/09/22
8320
用Python爬取淘宝4403条大裤衩数据进行分析,终于找到可以入手的那一条
python爬虫-唯品会商品信息实战步骤详解
下拉右侧滚动条可以发现,滑动到下面的时候页面会自动刷新出商品的数据,这里就体现了ajax交互,说明商品的信息是存放在json接口中,接着拉到底就可以发现翻页的按钮了,如下
Python研究者
2020/10/22
1.7K0
python爬虫-唯品会商品信息实战步骤详解
推荐阅读
相关推荐
利用Python爬取淘宝商品信息分析设计程序结构核心代码完整代码
更多 >
LV.0
这个人很懒,什么都没有留下~
目录
  • 1.avio介绍
  • 2.为什么要用avio?
    • 2.1.灵活性高
    • 2.2.扩展性强
    • 2.3.可定制性好
  • 3.内存区作为输入
    • 3.1.回调函数何时被回调呢?
    • 3.2.该示例作用是统计mp4文件的视频帧数,代码如下:
  • 4.内存区作为输出
    • 4.1.回调函数何时被回调呢?
    • 4.2.该示例作用是提取mp4文件的视频帧为h264文件,输出采用write_packet回调,代码如下:
  • 5.内存IO模式非常重要的一个函数:avio_alloc_context()
  • 6.两个示例的环境
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档