Linux C语言实现简单爬虫

article/2025/11/8 4:56:15

文章目录

  • 代码案例
    • 源代码
  • 应用知识
    • hostent结构体
    • gethostbyname()函数
    • inet_pton和inet_ntop
      • inet_pton
      • inet_ntop
    • sockaddr_in
    • in_addr结构
    • htons 编辑
    • socket()
    • connect()
    • sprintf函数
      • format标签属性
    • setsockopt

代码案例

爬到的HTML文件
在这里插入图片描述
输入终端的参数
在这里插入图片描述

源代码

/*************************************************************************> File Name: myphp.c> Author: 杨永利> Mail: 1795018360@qq.com > Created Time: 2020年07月28日 星期二 11时50分24秒************************************************************************/#include <stdio.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <string.h>int main(int argc, char* argv[]){if(argc!=3){printf("请输入正确的参数个数(运行文件,源网址,保存文件名)!\n");return -1;}// 存放源网址的字符串char *Char_Yuan=argv[1];printf("你给的源网址是:%s\n",Char_Yuan);// 定义hostent结构体/*struct hostenth_name – 地址的正式名称。h_aliases – 空字节-地址的预备名称的指针。h_addrtype –地址类型; 通常是AF_INET。h_length – 地址的比特长度。h_addr_list – 零字节-主机网络地址指针。网络字节顺序。h_addr - h_addr_list中的第一地址。*/struct hostent *host;// gethostbyname()函数用于获取主机名的包含主机名字和地址信息的hostent结构的指针if((host=gethostbyname(Char_Yuan))== NULL){printf("获取ip地址失败!\n");return -1;}   // 定义字符串用于存放转换后的网络地址char buf[30];// inet_ntop是将网络二进制结构到ASCII类型的地址 /*第一个参数af是地址簇,第二个参数*src是来源地址,第三个参数* dst接收转换后的数据。第四个参数三接收数据的长度*/inet_ntop(host->h_addrtype,host->h_addr_list[0],buf,sizeof(buf));// sockaddr结构体/*参数说明:sin_family指代协议族,在socket编程中只能是AF_INETsin_port存储端口号(使用网络字节顺序),在linux下,端口号的范围0~65535,同时0~1024范围的端口号已经被系统使用或保留。sin_addr存储IP地址,使用in_addr这个数据结构sin_zero是为了让sockaddr与sockaddr_in两个数据结构保持大小相同而保留的空字节。*/struct sockaddr_in server;// 初始化结构体变量bzero(&server,sizeof(server));// 为结构体变量的每个成员赋值server.sin_family=AF_INET;server.sin_port=htons(80);server.sin_addr.s_addr=inet_addr(buf);// 根据目前数据,分配一个套接口的描述字及其所用的资源。int sockfd=socket(AF_INET,SOCK_STREAM,0);// connect函数进行连接套接字if(connect(sockfd, (struct sockaddr*)&server, sizeof(server))){printf("connect() error!\n");return -1;}// 定义套接字要进行发送的消息字符串char socket_send_message[1024];// 用sprintf函数将请求头写发送字符串sprintf(socket_send_message,"GET / HTTP/1.1\r\nHost:%s\r\nAccept:*/* \r\nUser-Agent:Mozilla/5.0 (Windows NT 10.0; WOW64)\r\nconnection:Keep-Alive\r\n\r\n", Char_Yuan);// 将缓冲区设置为两秒int timeout=2000;/*setsockopt函数参数:sockfd:标识一个套接口的描述字。level:选项定义的层次;支持SOL_SOCKET、IPPROTO_TCP、IPPROTO_IP和IPPROTO_IPV6。optname:需设置的选项。optval:指针,指向存放选项待设置的新值的缓冲区。optlen:optval缓冲区长度。 */setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, (char*)&timeout, sizeof(timeout));// 发送套接字if(send(sockfd, socket_send_message, strlen(socket_send_message), 0) <= 0){printf("send() error!\n");return -1;}setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, (char*)&timeout, sizeof(timeout));// 定义套接字要进行接收的消息字符串char socket_get_message[1024];// 用于进行数据的接收int str_len;// 将获取的消息写入html文件FILE* file = fopen(argv[2], "a+");if(file == NULL){printf("fopen() error!\n");return -1;}// 执行循环直到接收消息的最后do{//给定循环缓冲区一直接受,直到接受至最后bzero(socket_get_message, sizeof(socket_get_message));str_len = recv(sockfd, socket_get_message, sizeof(socket_get_message), 0);// 将每次接收的数据追加到文件中fwrite(socket_get_message, 1, 1024, file);}while(str_len);return 0;
}

应用知识

hostent结构体

定义

struct hostent{
char * h_name;
char ** h_aliases;
short h_addrtype;
short h_length;
char ** h_addr_list;
#define h_addr h_addr_list[0];
};

参数
struct hostent
h_name – 地址的正式名称。
h_aliases – 空字节-地址的预备名称的指针。
h_addrtype –地址类型; 通常是AF_INET。
h_length – 地址的比特长度。
h_addr_list – 零字节-主机网络地址指针。网络字节顺序。
h_addr - h_addr_list中的第一地址。

gethostbyname()成功时返回一个指向结构体 hostent 的指针,或者是个空(NULL)指针。(但是和以前不同,不设置errno,h_errno 设置错误信息。请看下面的herror()。

gethostbyname()函数

gethostbyname()返回对应于给定主机名的包含主机名字和地址信息的hostent结构的指针。结构的声明与gethostbyaddr()中一致。
返回对应于给定主机名的主机信息。

#include <winsock2.h>
struct hostent *gethostbyname(const char *name);

name:指向主机名的指针。
返回类型

struct hostent
{
char *h_name;
char ** h_aliases;
short h_addrtype;
short h_length;
char ** h_addr_list;
};

Linux版

#include <netdb.h>
struct hostent *gethostbyname(const char *hostname);

gethostbyname()返回对应于给定主机名的包含主机名字和地址信息的hostent结构指针。结构的声明与gethostbyaddr()中一致。

返回的指针指向一个由Windows Sockets实现分配的结构。应用程序不应该试图修改这个结构或者释放它的任何部分。此外,所有线程共用一份这个结构的拷贝(这里应该是每个线程一份拷贝,原文是这样的Furthermore, only one copy of this structure is allocated per thread, so the application should copy any information it needs before issuing any other Windows Sockets function calls.),所以应用程序应该在发出其他Windows Scokets API调用前,把自己所需的信息拷贝下来。

gethostbyname()实现没有必要识别传送给它的IP地址串。对于这样的请求,应该把IP地址串当作一个未知主机名同样处理。如果应用程序有IP地址串需要处理,它应该使用inet_addr()函数把地址串转换为IP地址,然后调用gethostbyaddr()来得到hostent结构。

返回:非空指针——成功,空指针——出错,同时设置h_errno

inet_pton和inet_ntop

函数作用

  1. inet_pton:转换字符串到网络地址
  2. inet_ntop:转换网络二进制结构到ASCII类型的地址

inet_pton

inet_pton:将“点分十进制” -> “二进制整数”

函数原型:

int inet_pton(int af, const char *src, void *dst);

参数:
这个函数转换字符串到网络地址,
第一个参数af是地址簇,
第二个参数src是来源地址,
第三个参数
dst接收转换后的数据。

inet_pton 是inet_addr的扩展,支持的多地址族有下列:
af = AF_INET
src为指向字符型的地址,即ASCII的地址的首地址(ddd.ddd.ddd.ddd格式的),函数将该地址转换为in_addr的结构体,并复制在dst中。
af = AF_INET6
src为指向IPV6的地址,函数将该地址转换为in6_addr的结构体,并复制在
dst中。

如果函数出错将返回一个负值,并将errno设置为EAFNOSUPPORT,如果参数af指定的地址族和src格式不对,函数将返回0。

inet_ntop

inet_ntop函数原型如下[将“二进制整数” -> “点分十进制”]

Linux下inet_pton和inet_ntop这2个IP地址转换函数,可以在将IP地址在“点分十进制”和“二进制整数”之间转换。而且,这2个函数能够处理ipv4和ipv6。算是比较新的函数了。

#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
const char *inet_ntop(int af, const void *src, char *dst, socklen_t cnt);

这个函数转换网络二进制结构到ASCII类型的地址,参数的作用和inet_pton相同,只是多了一个参数socklen_t cnt,他是所指向缓存区dst的大小,避免溢出,如果缓存区太小无法存储地址的值,则返回一个空指针,并将errno置为ENOSPC。

sockaddr_in

sockaddr_in(在netinet/in.h中定义):

struct sockaddr_in{short sin_family;/*Address family一般来说AF_INET(地址族)PF_INET(协议族)*/unsigned short sin_port;/*Port number(必须要采用网络数据格式,普通数字可以用htons()函数转换成网络数据格式的数字)*/struct in_addr sin_addr;/*IP address in network byte order(Internet address)*/unsigned char sin_zero[8];/*Same size as struct sockaddr没有实际意义,只是为了 跟SOCKADDR结构在内存中对齐*/};

参数说明:
sin_family指代协议族,在socket编程中只能是AF_INET
sin_port存储端口号(使用网络字节顺序),在linux下,端口号的范围065535,同时01024范围的端口号已经被系统使用或保留。
sin_addr存储IP地址,使用in_addr这个数据结构
sin_zero是为了让sockaddr与sockaddr_in两个数据结构保持大小相同而保留的空字节。
(s_addr按照网络字节顺序存储IP地址)

然后用memset函数初始化就可以了memset((char*)&mysock,0,sizeof(mysock));//初始化


sockaddr_in mysock;
memset((char*)&mysock,0,sizeof(mysock));
mysock.sin_family=AF_INET;
mysock.sin_port=htons(1234);//1234是端口号
mysock.sin_addr.s_addr=inet_addr("192.168.0.1");

in_addr结构

在linux下:

typedef uint32_t in_addr_t;
struct in_addr{in_addr_t s_addr;};

htons 编辑

htons是将整型变量从主机字节顺序转变成网络字节顺序, 就是整数在地址空间存储方式变为高位字节存放在内存的低地址处。

u_shorthtons(u_short hostshort);

socket()

socket()函数用于根据指定的地址族、数据类型和协议来分配一个套接口的描述字及其所用的资源。如果协议protocol未指定(等于0),则使用缺省的连接方式。

对于使用一给定地址族的某一特定套接口,只支持一种协议。但地址族可设为AF_UNSPEC(未指定),这样的话协议参数就要指定了。协议号特定于进行通讯的“通讯域”。

#include <sys/socket.h>
int socket( int af, int type, int protocol);

参数

  • af:一个地址描述。仅支持AF_INET格式,也就是说ARPA Internet地址格式。
  • type:指定socket类型。新套接口的类型描述类型,如TCP(SOCK_STREAM)和UDP(SOCK_DGRAM)。常用的socket类型有,SOCK_STREAM、SOCK_DGRAM、SOCK_RAW、SOCK_PACKET、SOCK_SEQPACKET等等。
  • protocol:顾名思义,就是指定协议。套接口所用的协议。如调用者不想指定,可用0。常用的协议有,IPPROTO_TCP、IPPROTO_UDP、IPPROTO_STCP、IPPROTO_TIPC等,它们分别对应TCP传输协议、UDP传输协议、STCP传输协议、TIPC传输协议。

返回值:
若无错误发生,socket()返回引用新套接口的描述字。否则的话,返回INVALID_SOCKET错误,应用程序可通过WSAGetLastError()获取相应错误代码。

connect()

connect()用于建立与指定socket的连接。

定义函数:int connect(int sockfd, struct sockaddr * serv_addr, int addrlen);

  • sockfd:标识一个套接字。
  • serv_addr:套接字s想要连接的主机地址和端口号。
  • addrlen:name缓冲区的长度。

返回值:成功则返回0, 失败返回-1, 错误原因存于errno 中.

sprintf函数

函数声明

int sprintf(char *string, char *format [,argument,...]);

参数列表

  • string-- 这是指向一个字符数组的指针,该数组存储了 C 字符串。
  • format-- 这是字符串,包含了要被写入到字符串 str 的文本。它可以包含嵌入的 format 标签,format 标签可被随后的附加参数中指定的值替换,并按需求进行格式化。format 标签属性是%[flags][width][.precision][length]specifier
  • [argument]…:根据不同的 format 字符串,函数可能需要一系列的附加参数,每个参数包含了一个要被插入的值,替换了 format 参数中指定的每个 % 标签。参数的个数应与 % 标签的个数相同。
    功能
    把格式化的数据写入某个字符串缓冲区。
    返回值
    如果成功,则返回写入的字符总数,不包括字符串追加在字符串末尾的空字符。如果失败,则返回一个负数。
    sprintf 返回以format为格式argument为内容组成的结果被写入string的字节数,结束字符‘\0’不计入内。即,如果“Hello”被写入空间足够大的string后,函数sprintf 返回5。

format标签属性

format 标签属性是%[flags][width][.precision][length]specifier,具体讲解如下: [3]
sprintf格式的规格如下所示。[]中的部分是可选的。
%[指定参数][标识符][宽度][.精度]指示符

  1. [指定参数] 处理字符方向。负号时表示从后向前处理。
  2. [标识符] 填空字元。 0 的话表示空格填 0;空格是内定值,表示空格就放着。
  3. [宽度]字符总宽度。为最小宽度。
  4. [精度] 精确度。指在小数点后的浮点数位数。
  5. 转换字符
    %% 印出百分比符号,不转换。
    %c 字符输出到缓冲区,不转换。
    %d 整数转成十进位。
    %f 倍精确度数字转成浮点数。
    %o 整数转成八进位。
    %s 字符串输出到缓冲区,不转换。
    %x 整数转成小写十六进位。
    %X 整数转成大写十六进位。

setsockopt

setsockopt()函数,用于任意类型、任意状态套接口的设置选项值。尽管在不同协议层上存在选项,但本函数仅定义了最高的“套接口”层次上的选项。

#include <sys/types.h>
#include <sys/socket.h>
int setsockopt(int sockfd, int level, int optname,const void *optval, socklen_t optlen);

参数说明

  • sockfd:标识一个套接口的描述字。
  • level:选项定义的层次;支持SOL_SOCKET、IPPROTO_TCP、IPPROTO_IP和IPPROTO_IPV6。
  • optname:需设置的选项。
  • optval:指针,指向存放选项待设置的新值的缓冲区。
  • optlen:optval缓冲区长度。

http://chatgpt.dhexx.cn/article/BHLkmMDG.shtml

相关文章

fcntl函数 和 ioctl函数

文章目录 一、fcntl 函数二、ioctl 函数 一、fcntl 函数 fcntl()函数可以对一个已经打开的文件描述符执行一系列控制操作&#xff0c;譬如复制一个文件描述符&#xff08;与dup、dup2 作用相同&#xff09;、获取/设置文件描述符标志、获取/设置文件状态标志等&#xff0c;类似…

linux ioctl 理解

背景 传统的操作系统可以分成两层&#xff0c;用户层和内核层。内核代码处理敏感资源同时在不同应用程序中间提供了安全且可信的隔离&#xff0c;出于此&#xff0c;操作系统要阻止用户态的程序直接访问内核资源。用户空间的程序通常发出一个给内核的请求&#xff0c;该请求称为…

linux ioctl函数介绍

1. 概念 ioctl 是设备驱动程序中设备控制接口函数&#xff0c;一个字符设备驱动通常会实现设备打开、关闭、读、写等功能&#xff0c;在一些需要细分的情境下&#xff0c;如果需要扩展新的功能&#xff0c;通常以增设 ioctl() 命令的方式实现。 在文件 I/O 中&#xff0c;ioctl…

ioctl函数详解(参数详解,驱动unlocked_ioctl使用、命令码如何封装)

ioctl函数详解 一、ioctl函数的原型 在用户空间的函数原型 #include <sys/ioctl.h> int ioctl(int d, int request, ...); //io的控制&#xff0c;设备的控制/***第一个参数d是打开的文件描述符***//***The second argument is a device-dependent request code&…

IOCTL函数用法

.ioctl 的实现 一、ioctl的简介&#xff1a; 虽然在文件操作结构体"struct file_operations"中有很多对应的设备操作函数&#xff0c;但是有些命令是实在找不到对应的操作函数。如CD-ROM的驱动&#xff0c;想要一个弹出光驱的操作&#xff0c;这种操作并不是所有的…

IOCTL函数用法详解

ioctl是设备驱动程序中对设备的I/O通道进行管理的函数 。所谓对I/O通道进行管理&#xff0c;就是对设备的一些特性进行控制&#xff0c;例如串口的传输波特率、马达的转速等等。它的调用个数如下&#xff1a; int ioctl(int fd, ind cmd, …)&#xff1b; 其中fd是用户程序打…

linux ioctl()详解

一、ioctl的简介&#xff1a; 虽然在文件操作结构体"struct file_operations"中有很多对应的设备操作函数&#xff0c;但是有些命令是实在找不到对应的操作函数。如CD-ROM的驱动&#xff0c;想要一个弹出光驱的操作&#xff0c;这种操作并不是所有的字符设备都需要的…

ioctl 详细介绍

ioctl 详细介绍 (一)ioctl 的作用: 通过设备驱动程序执行各种类型的硬件控制。除了简单数据传输外,大部分设备可以执行其他的一些操作,比如,用户空间经常请求设备锁门、弹出介质、报告错误信息、改变波特率或者执行自破坏等等。 Ioctl的操作通过流程图简言之: 从图…

ioctl函数

一、什么是ioctl   ioctl是设备驱动程序中对设备的I/O通道进行管理的函数。所谓对I/O通道进行管理&#xff0c;就是对设备的一些特性进行控制&#xff0c;例如串口的传输波特率、马达的转速等等。   ioctl函数是文件结构中的一个属性分量&#xff0c;就是说如果你的驱动程序…

linux驱动开发(四):ioctl()函数

前文中我们介绍了应用程序通过使用虚拟文件系统VFS提供的接口&#xff0c;来控制字符驱动程序&#xff0c;完成字符驱动设备的open、close、read、write操作。但是如果我们想进行除此以外的其他操作&#xff0c;拓展一些file_operations给出的接口中没有的自定义功能&#xff0…

linux 内核 - ioctl 函数详解

1. 概念 ioctl 是设备驱动程序中设备控制接口函数&#xff0c;一个字符设备驱动通常会实现设备打开、关闭、读、写等功能&#xff0c;在一些需要细分的情境下&#xff0c;如果需要扩展新的功能&#xff0c;通常以增设 ioctl() 命令的方式实现。 在文件 I/O 中&#xff0c;ioc…

第10章综合案例1广电大数据分析

第10章综合案例1广电大数据分析 实验目的及要求 &#xff08;1&#xff09;现有用户观看历史和用户信息两个广电大数据文件&#xff0c;将对用户数据进行大数据分析。 实验系统环境及版本 Linux Ubuntu 20.04 JDK1.8 Hadoop3.1.0 MySQL8.0.28 Hive3.1.2 实验任务 基本…

图解大数据 | 综合案例-使用Spark分析挖掘零售交易数据

作者&#xff1a;韩信子ShowMeAI 教程地址&#xff1a;http://www.showmeai.tech/tutorials/84 本文地址&#xff1a;http://www.showmeai.tech/article-detail/177 声明&#xff1a;版权所有&#xff0c;转载请联系平台与作者并注明出处 收藏ShowMeAI查看更多精彩内容 引言 …

大数据之实践案例分析

前言 公司由页游转手游&#xff0c;公司的数据分析需要针对手游进行设计&#xff0c;所以原来的那一套针对页游的数据分析框架就显得不是很合适了&#xff0c;一方面在于手游和页游一些业务逻辑上的不同&#xff0c;另外一方面是数据量级上的改变&#xff0c;以及渠道、区服之间…

大数据分析案例-基于朴素贝叶斯算法构建电信客户流失分析预警模型

&#x1f935;‍♂️ 个人主页&#xff1a;艾派森的个人主页 ✍&#x1f3fb;作者简介&#xff1a;Python学习者 &#x1f40b; 希望大家多多支持&#xff0c;我们一起进步&#xff01;&#x1f604; 如果文章对你有帮助的话&#xff0c; 欢迎评论 &#x1f4ac;点赞&#x1f4…

Python+大数据-数据分析与处理(六)-综合案例

Python大数据-数据分析与处理(六)-综合案例 案例一&#xff1a;Appstore数据分析 学习目标 掌握描述性数据分析流程 能够使用pandas、seaborn进行数据分析和可视化 1.案例介绍 案例背景&#xff1a; 对 App 下载和评分数据分析&#xff0c;帮助 App 开发者获取和留存用户…

大数据挖掘分析的经典案例,主要有哪几种?

大数据挖掘分析经典案例有以下几种&#xff1a; 1.预测产品未来一段时间用户是否会流失&#xff0c;流失情况怎么样&#xff1b; 2.公司做了某个促销活动&#xff0c;预估活动效果怎么样&#xff0c;用户接受度如何&#xff1b; 3.评估用户信用度好坏&#xff1b; 4.对现有…

第11章综合案例2影评大数据分析

第11章综合案例2影评大数据分析 实验目的及要求 &#xff08;1&#xff09;现有电影、影评和用户信息3个数据文件&#xff0c;将对其进行大数据分析。 实验系统环境及版本 Linux Ubuntu 20.04 JDK1.8 Hadoop3.1.0 MySQL8.0.28 Hive3.1.2 实验任务 评分次数最多的10部电…

淘宝大数据分析案例

项目介绍 本次结合的是一份淘宝大数据数据&#xff0c;数据集的大小共177MB&#xff0c;数据一共有3182261份&#xff08;三百多万份数据集&#xff09;&#xff0c;一般的软件是无法计算和分析的&#xff0c;比如Excel&#xff0c;MySQL&#xff0c;Python这些都无法较好的完…

数据分析综合案例

数据分析综合案例&#xff1a; 数据分析流程 什么是数据清洗&#xff1f; 简单来说&#xff0c;数据清洗就是把“脏数据”变为“干净的数据”。数据清洗虽然很繁琐&#xff0c;但也很重要。数据清洗流程&#xff1a; 数据的读写、数据的探索与描述、数据简单处理、重复值的处…