linux获取网页源码

本文介绍了在Linux环境下通过C语言获取网页源码的多种方式,包括使用socket编程和libcurl库。同时,展示了如何使用libcurl库保存网页内容到本地,以及利用系统调用和select函数实现连接超时的处理。这些方法对于网络编程和自动化任务非常有用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

#include <stdio.h> 
#include <winsock.h> 
#include <string.h> 
#pragma comment(lib, "ws2_32.lib") 


void geturl(char *url) 
{ 
	WSADATA WSAData={0}; 
	SOCKET sockfd; 
	struct sockaddr_in addr; 
	struct hostent *pURL; 
	char myurl[BUFSIZ]; 
	char *pHost = 0, *pGET = 0; 
	char host[BUFSIZ], GET[BUFSIZ]; 
	char header[BUFSIZ] = ""; 
	static char text[BUFSIZ]; 
	int i; 
	
	/* 
	* windows下使用socket必须用WSAStartup初始化,否则不能调用 
	*/ 
	if(WSAStartup(MAKEWORD(2,2), &WSAData)) 
	{ 
		printf("WSA failed\n"); 
		return; 
	} 
	
	/* 
	* 分离url中的主机地址和相对路径 
	*/ 
	strcpy(myurl, url); 
	for (pHost = myurl; *pHost != '/' && *pHost != '\0'; ++pHost); 
	if ( (int)(pHost - myurl) == strlen(myurl) ) 
		strcpy(GET, "/"); 
	else 
		strcpy(GET, pHost); 
	*pHost = '\0'; 
	strcpy(host, myurl); 
	printf("%s\n%s\n", host, GET); 
	
	/* 
	* 设定socket参数,并未真正初始化 
	*/ 
	sockfd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); 
	pURL = gethostbyname(host); 
	addr.sin_family = AF_INET; 
	addr.sin_addr.s_addr = *((unsigned long*)pURL->h_addr); 
	addr.sin_port = htons(80); 
	
	/* 
	* 组织发送到web服务器的信息 
	* 为何要发送下面的信息请参考HTTP协议的约定 
	*/ 
	strcat(header, "GET "); 
	strcat(header, GET); 
	strcat(header, " HTTP/1.1\r\n"); 
	strcat(header, "HOST: "); 
	strcat(header, host); 
	strcat(header, "\r\nConnection: Close\r\n\r\n"); 
	//printf(header);
	/* 
	* 连接到服务器,发送请求header,并接受反馈(即网页源代码) 
	*/ 
	connect(sockfd,(SOCKADDR *)&addr,sizeof(addr)); 
	
	send(sockfd, header, strlen(header), 0); 
	
	while ( recv(sockfd, text, BUFSIZ, 0) > 0) 
	{ 
		printf("%s", text); 
		strnset(text, '\0', BUFSIZ); 
	} 
	
	closesocket(sockfd); 
	
	WSACleanup(); 
} 


int main() 
{ 
	while (1)
	{
		char url[256]; 
		printf("http://"); 
		scanf("%s", url); 
		geturl(url);
	}
	return 0; 
}




网页下载保存实例


// 采用CURLOPT_WRITEFUNCTION 实现网页下载保存功能


#include <stdio.h>;


#include <stdlib.h>;


#include <unistd.h>;


 


#include <curl/curl.h>;


#include <curl/types.h>;


#include <curl/easy.h>;


 


FILE *fp; //定义FILE类型指针


//这个函数是为了符合CURLOPT_WRITEFUNCTION而构造的


//完成数据保存功能


size_t write_data(void *ptr, size_t size, size_t nmemb, void *stream) 


{


 int written = fwrite(ptr, size, nmemb, (FILE *)fp);


 return written;


}


 


int main(int argc, char *argv[])


{


 CURL *curl;


 


 curl_global_init(CURL_GLOBAL_ALL); 


 curl=curl_easy_init();


 curl_easy_setopt(curl, CURLOPT_URL, argv[1]); 


 


 if((fp=fopen(argv[2],"w"))==NULL)


 {


 curl_easy_cleanup(curl);


 exit(1);


 }


////CURLOPT_WRITEFUNCTION 将后继的动作交给write_data函数处理


 curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_data); 


 curl_easy_perform(curl);


 curl_easy_cleanup(curl);


 exit(0);


}


编译gcc save_http.c -o save_http –lcurl


./ save_http www.baidu.com /tmp/baidu






做实验用脚本最快, 你要用C就用libcurl。
#include <stdio.h>
#include <stdlib.h>


int main(void)
{
	FILE *fp;
	char buf[BUFSIZ];
	if (NULL != (fp = popen("curl http://www.baidu.com", "r")))
		fread(buf, BUFSIZ, 1, fp);
	else {
		fprintf(stderr, "popen error...\n");
		exit(1);
	}
	printf("%s\n", buf);
	pclose(fp);
	return 0;
}




#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <errno.h>
#include <time.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <string.h>
#include <stdarg.h>
#include <netdb.h>
#include <setjmp.h>
#include <signal.h>


/*gethostbyname 超时返回
这里使用的办法是设置一个时钟,如果gethostbyname在指定的时间内尚未返回,
时钟会强制其返回,得到的返回值显然是空指针,等价于告诉用户主机未连如互联网或者该域名无法解析。*/
static sigjmp_buf jmpbuf;
static void alarm_func() //该函数执行之后会执行跳转
{
	siglongjmp(jmpbuf, 1);
}




static struct hostent *gngethostbyname(char *HostName, int timeout)
{
	struct hostent *lpHostEnt;
	
	signal(SIGALRM, alarm_func);  //接受alarm信号,然后调用函数
	if(sigsetjmp(jmpbuf, 1) != 0)//跳转目的地
	{
		alarm(0);//timout
		signal(SIGALRM, SIG_IGN);
		return NULL;
	}
	alarm(timeout);//setting alarm
	printf("\nwill gethost!\n");
	lpHostEnt = gethostbyname(HostName);
	signal(SIGALRM, SIG_IGN);
	
	return lpHostEnt;
}






/*(linux socket编程实现connect超时的一种方法
创建套接字,将其设置成非阻塞状态。 
调用connect连接对端主机,如果失败,判断当时的errno是否为EINPROGRESS,也就是说是不是连接正在进行中,如果是,转到步骤3,如果不是,返回错误。 
用select在指定的超时时间内监听套接字的写就绪事件,如果select有监听到,证明连接成功,否则连接失败。*/ 




int main(int argc, char *argv[])
{
	//最好检查一下参数,要求传入3个参数 URL PORT TIMEOUT(connect && send && recv 3个参数的超时)
	int fd, retval,res,error;
	struct sockaddr_in addr;
	struct timeval timeo = {15, 0}; //time ou struct
	struct hostent *site;
	socklen_t len = sizeof(timeo);
	fd_set set;
	fd = socket(AF_INET, SOCK_STREAM, 0);
	if (argc == 4)
		timeo.tv_sec = atoi(argv[3]);
	site=gngethostbyname(argv[1],3);  //解析域名的超时设置,测试域名超时,可以写一个可以ping的通但是没有办法解析域名
	//的IP地址到resolv.conf里面,然后加上一个默认路由,直接PING一个百度,就能发现如果不加超时机制就会一直卡在那里
	if(NULL == site)
	{
		printf("\ncan not find the site!\n");
		return -2;
	}
	fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK); //设置为非阻塞模式
	addr.sin_family = AF_INET;
	//addr.sin_addr.s_addr = inet_addr(argv[1]);
	memcpy(&addr.sin_addr, site->h_addr_list[0], site->h_length);
	addr.sin_port = htons(atoi(argv[2]));
	printf("%d\n", time(NULL));
	/*if (connect(fd, (struct sockaddr*)&addr, sizeof(addr)) == 0) {
	printf("connected1\n");
	// return 0;
}*/
	//res=connect(fd, (struct sockaddr*)&addr, sizeof(addr));
	//printf("\nconnect result:[%d]\n",res);
	if (connect(fd, (struct sockaddr*)&addr, sizeof(addr)) != 0)
	{
		//调用一次系统函数失败后直接看errno,确定是什么问题,下面的代码可以实现在没有默认路由的情况下直接返回失败.
		if (errno != EINPROGRESS) { 
			printf("connect:normal network unreach!!");
			return -1;
		}
		printf("\nwill select\n");
        FD_ZERO(&set);/*将set清零使集合中不含任何fd*/ 
        FD_SET(fd,&set); /*将一个给定的文件描述符加入集合之中*/ 
        retval = select(fd + 1, NULL, &set, NULL, &timeo);
        if (retval == -1) {
			printf("select");
			return -1;
        } else if(retval == 0) {
			printf("timeout\n"); //这样的select等于是变成了再timeout时间内是阻塞模式,超过timeout就直接返回
			printf("%d\n", time(NULL));
			return 0;
        }
        else
        {
			printf("connected--->:[%d]\n",retval);
			getsockopt(fd, SOL_SOCKET, SO_ERROR, &error, (socklen_t *)&len); //判断在connected成功之后,获取套接口目前的一些信息来判断是否真的是连接上了,返回0表示真的连上了
			printf("error--->:[%d]\n",error);
			
			if(0!=error)
				return -1;
        }
	}
	
	int ul = 0;
	ioctl(fd, FIONBIO, &ul); //设置为阻塞模式
	//return 0;
	setsockopt(fd,SOL_SOCKET,SO_SNDTIMEO,(char *)&timeo,sizeof(timeo));
	setsockopt(fd,SOL_SOCKET,SO_RCVTIMEO,(char *)&timeo,sizeof(timeo));
	printf("\nbefore\n");
	sleep(5); //在sleep 5的时候,拔掉网线就可以测试出recv超时的功能,如果不加recv 的超时功能,拔掉网线后就会一直卡在那里,当然你在实际应用的时候没必要加这个
	printf("\nafter\n");
	char *msg="GET / HTTP/1.0\r\n\r\n";
	if( send(fd, msg, strlen(msg), 0)<0 )
	{
		printf("error in send msg\n");
		exit(1);
	}
	int i=0;
	char buf[1000];
	
	while((recv(fd,buf,1000,MSG_WAITALL))>0)
	{
		printf("[%d]:[%s]",i,buf);
		i++;
	}
	printf("\n------end---------\n");
	close(fd);
	return;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值