Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >专栏 >C++ socket网络爬虫(1)

C++ socket网络爬虫(1)

作者头像
magicsoar
发布于 2018-02-06 02:44:27
发布于 2018-02-06 02:44:27
2.7K00
代码可运行
举报
文章被收录于专栏:magicsoarmagicsoar
运行总次数:0
代码可运行

C++写的socket网络爬虫,代码会在最后一次讲解中提供给大家,同时我也会在写的同时不断的对代码进行完善与修改

我首先向大家讲解如何将网页中的内容,文本,图片等下载到电脑中。

我会教大家如何将百度首页上的这个百度标志图片(http://www.baidu.com/img/bdlogo.gif)抓取下载到电脑中。

程序的部分代码如下,讲解在代码的下面,下载链接在最后给出,

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
int main()
{

    string url = "www.baidu.com";
    string name = "/img/bdlogo.gif";
    int port = 80;
    int client_socket = makeSocket(url,port);//1
    string request = "GET " + name + " HTTP/1.1\r\nHost:" + url + "\r\nConnection:Close\r\n\r\n";//2
    
    if (send(client_socket, request.c_str(), request.size(), 0) == SOCKET_ERROR)//3
    {
        cout << "send error" << endl;
    }
    
    fstream file;
    string fileName = FileName(name);//4
    file.open(fileName, ios::out | ios::binary);//5
    
    char buf[1024];
    ::memset(buf, 0, sizeof(buf));//6
    int n = 0;
    n = recv(client_socket, buf, sizeof(buf)-sizeof(char), 0);//3
    char* cpos = strstr(buf, "\r\n\r\n");//7
    file.write(cpos + strlen("\r\n\r\n"), n - (cpos - buf) - strlen("\r\n\r\n"));//7
    while ((n = recv(client_socket, buf, sizeof(buf)-sizeof(char), 0)) > 0)//7
    {
        try
        {
            file.write(buf, n);
        }
        catch (...)
        {
            cerr << "ERROR" << endl;
        }
    }
    file.close();
    closesocket(client_socket);
    system("pause");
    return 0;
}

一、main函数

1、makeSocket(url,port)

int makeSocket(string host,int port)函数是我自己编写的,接受两个参数,一个是域名或主机名,第二个是所使用的端口号,返回一个用于创建socket的int型数据,将在这一页的二.makeSocket中进行讲解

2、string request = "GET " + name + " HTTP/1.1\r\nHost:" + url + "\r\nConnection:Close\r\n\r\n";

这个是http的请求报头,有很多的信息,这里只对这句话中使用到的进行讲解

GET 请求获取Request-URI所标识的资源;

name 所标识的资源;

HTTP/1.1 表示请求的HTTP协议版本;

Host:url  指定被请求资源的Internet主机和端口号,通常从HTTP URL中提取出来的,

比如 我们在浏览器中输入http://baidu.com/index.html浏览器发送的请求消息中,就会包含Host请求报头域,如下: Host:www.baidu.com

此处使用缺省端口号80,若指定了端口号,则变成:Host:www.baidu.com:port

Connection:Close Connection字段用于设定是否使用长连接,在http1.1中默认是使用长连接的,即Connection的值为Keep-alive,如果不想使用长连接则需要明确指出connection的值为close

Connection:Close表明当前正在使用的tcp链接在请求处理完毕后会被断掉。以后client再进行新的请求时就必须创建新的tcp链接了,即必须从新创建socket

更多关于http协议的内容可以查考http://blog.csdn.net/gueter/article/details/1524447 HTTP协议详解

注意最后一定要以一个单独的\r\n作为结束标志

3.send/recv

send用于向服务端发送消息

recv/send函数原型如下

代码语言:js
AI代码解释
复制
int recv(SOCKET s,char FAR * buf,int len,int flags)/int send(SOCKET s,const char FAR * buf,int len,int flags);

第一个参数表示代表对方的socket,

第二个参数为接收读取的信息的字符串

第三个参数为该字符串的大小

第四个参数可以用来控制读写操作

详情可以参照http://www.cnblogs.com/magicsoar/p/3587351.html 中的讲解1

4 FileName(name)

自己编写的string FileName(string dir)函数,由于windows中文件的名字中不允许含有/

所以FileName函数用于将dir中的所有/替换为_

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
string FileName(string dir)
{
    string search = "/";
    int pos = 0;
    while ((pos = dir.find(search, pos)) != string::npos) {
        dir.replace(pos, search.size(), "_");
        pos++;
    }
    return dir;
}

如string FileName(“img/bdlogo.gif”)返回_img_bdlogo.gif

5 file.open(fileName, ios::out | ios::binary)r45

ios::out以输出方式打开文件,如果文件不存在这创建新的文件

ios::binary以二进制模式进行I/O操作,这里使用二进制模式是为了正确的处理图片的下载

6 ::memset(buf, 0, sizeof(buf));

函数原型为void *memset(void *s, int ch, size n);

函数解释:将s所指的内存中前n个字节 (typedef unsigned int size_t)用 ch 替换并返回 s 。

memset:作用是在一段内存块中填充某个给定的值,它是对较大的结构体和数组进行清零操作的一种较快方法

7 在接收和解释请求消息后,服务器返回一个HTTP响应消息。

HTTP响应也是由三个部分组成,分别是:状态行、消息报头、响应正文

响应正文就是服务器返回的资源的内容,所以我们需要跳过状态行与消息报头部分。

消息报头与相应正文之间可以用\r\n\r\n进行区分,当第一次发现接收到的字符串数组中含有\r\n\r\n时,则将\r\n\r\n前的内容全部忽略,将剩下的内容写到文件中去

strstr(*str1, *str2)实现从字符串str1中查找是否有字符串str2,如果有,从str1中的str2位置起,返回str1中str2起始位置的指针,如果没有,返回null。

由于一次最多可以接受1024个字符,而\r\n极有可能位于中间位置,所有我们要将1024个char中位于\r\n之后的数据写到文件中。

二.makeSocket函数

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
int makeSocket(string host,int port)
{
    WSADATA inet_WsaData;//1
    WSAStartup(MAKEWORD(2, 0), &inet_WsaData);//1
    if (LOBYTE(inet_WsaData.wVersion) != 2 || HIBYTE(inet_WsaData.wVersion) != 0)//1
    {
        WSACleanup();
        return -1;
    }
    int tcp_socket = socket(AF_INET, SOCK_STREAM, 0);//1
    struct hostent * hp = ::gethostbyname(host.c_str());//2
    struct sockaddr_in saddr;
    saddr.sin_family = AF_INET;
    saddr.sin_port = htons(port);
    memcpy(&saddr.sin_addr, hp->h_addr, 4);//3
    if (connect(tcp_socket, (const struct sockaddr *)&saddr, sizeof(saddr)) == -1)//1
    {
        cerr << "error in connect" << endl;
    }
    return tcp_socket;
}

1 见http://www.cnblogs.com/magicsoar/p/3585129.html windows下的C++ socket服务器(3)中讲解

2 struct hostent * hp = ::gethostbyname(host.c_str());

gethostbyname()返回对应于给定主机名的包含主机名字和地址信息的hostent结构指针

hostent结构体的定义如下

代码语言:js
AI代码解释
复制
struct  hostent { 
        char    FAR * h_name;           /* official name of host */     
        char    FAR * FAR * h_aliases;  /* alias list */     
        short   h_addrtype;             /* host address type */     
        short   h_length;               /* length of address */     
        char    FAR * FAR * h_addr_list; /* list of addresses */     
#define h_addr  h_addr_list[0]          /* address, for backward compat */     
};

hostent->h_name表示的是主机的规范名。例如www.baidu.com的规范名其实是www.a.shifen.com。(关于www.a.shifen.com还有一段故事http://www.zhihu.com/question/20100901) hostent->h_aliases表示的是主机的别名.www.google.com就是google他自己的别名。有的时候,有的主机可能有好几个别名,这些,其实都是为了易于用户记忆而为自己的网站多取的名字。 hostent->h_addrtype表示的是主机ip地址的类型,到底是ipv4(AF_INET),还是pv6(AF_INET6) hostent->h_length表示的是主机ip地址的长度

hostent->h_addr_list表示的是主机的ip地址

#define h_addr h_addr_list[0]

3 memcpy(&saddr.sin_addr, hp->h_addr, 4);

由于 hp->h_addr是char*类型,不能直接赋值给saddr.sin_addr

所以我们使用了memcpy函数

函数原型如下

代码语言:js
AI代码解释
复制
void *memcpy(void *dest, const void *src, size_t n);

从源src所指的内存地址的起始位置开始拷贝n个字节到目标dest所指的内存地址的起始位置中。

程序的下载地址

http://files.cnblogs.com/magicsoar/Webcrawler1.rar

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

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

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
Unix-Linux编程实践教程-chapter12-web-server
基于socket的客户/服务器程序遵循一个标准架构.服务器接收和处理请求,客户 发出请求
零式的天空
2022/03/02
2780
linux网络编程之socket(七):一个进程发起多个连接和gethostbyname等函数
s1mba
2017/12/28
1.7K0
linux网络编程之socket(七):一个进程发起多个连接和gethostbyname等函数
Unix-Linux编程实践教程-chapter11-socket
一些程序被作为单独的进程建立起来来接受和发送数据.在客户/服务器模型中, 服务器进程为客户进程提供处理或数据服务
零式的天空
2022/03/02
5690
socket 请求接收完整的一个http响应(设置recv 接收超时选项SO_RCVTIMEO)
本文讲述通过socket发送HTTP请求,可以获取到完整的一个HTTP响应,包括响应头、响应体和响应码。通过设置socket接收超时时间,可以避免接收过长的HTTP响应,造成内存溢出或者导致程序崩溃。
s1mba
2017/12/28
3.9K0
socket 请求接收完整的一个http响应(设置recv 接收超时选项SO_RCVTIMEO)
Unix-Linux编程实践教程-chapter13-udp
数据报是从一个socket发送到另一个socket的短消息.数据报socket是不连接的, 每个消息包含有目的地址.数据报(UDP)socket更加简单,快速,给系统增加的负荷 更小.
零式的天空
2022/03/02
3680
计网 | C语言Socket编程获取本机IP及指定域名IP
需要注意的是引用winsock2.h头文件后,还需要链接该头文件的实现文件,在本机上使用宏#pragma comment (lib, "ws2_32.lib")无法成功添加该实现文件ws2_32。手动编译时会出现如下报错信息:
Ranlychan
2023/03/05
3.7K0
18.1 Socket 原生套接字抓包
原生套接字抓包的实现原理依赖于Windows系统中提供的ioctlsocket函数,该函数可将指定的网卡设置为混杂模式,网卡混杂模式(Promiscuous Mode)是常用于计算机网络抓包的一种模式,也称为监听模式。在混杂模式下,网卡可以收到经过主机的所有数据包,而非只接收它所对应的MAC地址的数据包。
王瑞MVP
2023/10/25
4880
18.1 Socket 原生套接字抓包
webbench源码阅读
Webbench是一个在Linux下使用的非常简单的网站侧压工具。它使用fork()模拟多个客户端同时访问url,测试网站在压力下工作的性能。 只有socket.c和webbench.c两个文件.
yifei_
2022/11/14
3810
c语言socket通信
1. 前 言 网络的Socket数据传输是一种特殊的I/O,Socket也是一种文件描述符。Socket也具有一个类似于打开文件的函数调用Socket(),该函数返回一个整型的Socket描述符,随后的连接建立、数据传输等操作都是通过该Socket实现的。
全栈程序员站长
2022/09/14
1.3K0
C/C++ 实现URL路径拆分
URL路径拆分: 例如我们传入 http://www.baidu.com/index.php 拆分为 www.baidu.com 和 /index.php
王瑞MVP
2023/02/25
5570
Linux下Socket编程入门
网络字节序是TCP/IP中规定好的一种数据表示格式,它与具体的CPU类型、操作系统等无关,从而可以保证数据在不同主机之间传输时能够被正确解释。网络字节序采用big endian排序方式。
_咯噔_
2022/02/23
3.7K0
关于 getsockname、getpeername和gethostname、gethostbyname
一、gethostname,gethostbyname的用法 这两个函数可以用来获取主机的信息。 gethostname:获取主机的名字 gethostbyname:通过名字获取其他的信息(比如ip) 1.gethostname: man手册里面的解释(部分):        #include <unistd.h>        int gethostname(char *name, size_t len);        int sethostname(const char *name, size_t
xcywt
2018/01/11
1.4K0
14.10 Socket 套接字选择通信
对于网络通信中的服务端来说,显然不可能是一对一的,我们所希望的是服务端启用一份则可以选择性的与特定一个客户端通信,而当不需要与客户端通信时,则只需要将该套接字挂到链表中存储并等待后续操作,套接字服务端通过多线程实现存储套接字和选择通信,可以提高服务端的并发性能,使其能够同时处理多个客户端的请求。在实际应用场景中,这种技术被广泛应用于网络编程、互联网应用等领域。
王瑞MVP
2023/10/18
2520
14.10 Socket 套接字选择通信
windows下的C++ socket服务器(3)
int make_server_socket(int port) { WSADATA inet_WsaData;//1 WSAStartup(MAKEWORD(2, 0), &inet_WsaData);//1 1 WSADATA inet_WsaData;SAStartup(MAKEWORD(1, 1), &inet_WsaData); 在windows下使用socket的相关函数前,必须通过WSAStartup函数完成对Winsock服务的初始化。 int  WSAStartu
magicsoar
2018/02/06
2.1K0
跨平台 C++ 纯socket 访问webapi json
webapi.h #pragma once #include <string> #include <list> #include <map> class webapi { class urlitem { public: urlitem() { } //std::string url; std::string fullpath; // http://www.pic98.com:8080/file/beautileg/samansh
xiny120
2019/06/11
1.9K0
C++写Socket——TCP篇(0)建立连接及双方传输数据
这里主要是关于TCP的,TCP的特点什么的相关介绍在我另一篇博文里,所以这里直接动手吧。
太阳影的社区
2021/10/15
2.1K0
域名转化到IP地址的实现
在linux中,有一些函数可以实现主机名和地址的转化,最常见的有gethostbyname()、gethostbyaddr()等,它们都可以实现IPv4和IPv6的地址和主机名之间的转化。其中gethostbyname()是将主机名转化为IP地址,gethostbyaddr()则是逆操作,是将IP地址转化为主机名。
小爷毛毛_卓寿杰
2019/02/13
6.6K0
C/C++ 实现正反向端口映射
通常服务器是放在内网中,内部局域网中,并且分配了私有IP地址,而如果想要在外部直接访问内部服务器则需要端口映射,端口映射也叫做端口转发,或者叫做虚拟服务器,在渗透时当我们拿到目标机器权限以后,目标机器实在内网环境中,如果想要外部直接访问内部的特定机器的特定端口,则需要使用映射来实现,常用的映射工具是LCX,或者自己实现。
王瑞MVP
2022/12/28
6510
Linux下编程获取本地IP地址的常见方法
代码编译运行平台:Linux 64bits+g++(-m64),-m64表示生成64bits的程序。
恋喵大鲤鱼
2018/08/03
8.1K0
Linux下编程获取本地IP地址的常见方法
windows环境下用c++实现socket编程
socket即套接字,用于描述地址和端口,是一个通信链的句柄。应用程序通过socket向网络发出请求或者回应。
全栈程序员站长
2022/09/14
5.2K0
windows环境下用c++实现socket编程
相关推荐
Unix-Linux编程实践教程-chapter12-web-server
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验