专栏首页magicsoarC++ socket网络爬虫(1)

C++ socket网络爬虫(1)

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

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

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

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

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函数原型如下

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中的所有/替换为_

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函数

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结构体的定义如下

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函数

函数原型如下

void *memcpy(void *dest, const void *src, size_t n);

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

程序的下载地址

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

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • C++内存布局(1)-让new出的两个变量在堆上的地址连续

    大家都知道栈的地址按照从高到低的顺序增长的, 而堆的地址是按照从底到高的顺序增长的。 int *n1 = new int(1); int *n2 = new i...

    magicsoar
  • windows下的C++ socket服务器(4)

    void handleAccept(int socket_fd) { char buf[1024] = { '\0' }; string cmd...

    magicsoar
  • windows下的C++ socket服务器(3)

    int make_server_socket(int port) { WSADATA inet_WsaData;//1 WSAStar...

    magicsoar
  • 1076: 输入入门(1)

    描述:计算A+B 输入:输入数据有多组。每组一行,为整数A, B。以EOF做结束。 输出:输出A+B的值。 样例输入: 1 23 4 样例输出: 3...

    bboysoul
  • TCPServer TCPClient三次握手模拟编程

    套接字创建和关闭: SOCKET socket(int af,int type,int protocal); 套接字类型: SOCK_STREAM 流套接字...

    用户1154259
  • P1508 Likecloud-吃、吃、吃

    题目背景 问世间,青春期为何物? 答曰:“甲亢,甲亢,再甲亢;挨饿,挨饿,再挨饿!” 题目描述 正处在某一特定时期之中的李大水牛由于消化系统比较发达,最近一直处...

    attack
  • win2d 画出好看的图形

    本文告诉大家,win2d 不需要从零开始做,以前做出来的很多库其实只需要做很小修改就可以做出好看的效果,而且用在 UWP 上。本文修改原先 大神写的 GDI 图...

    林德熙
  • 浙大版《C语言程序设计(第3版)》题目集 习题8-2 在数组中查找指定元素

    其中list[]是用户传入的数组;n(≥0)是list[]中元素的个数;x是待查找的元素。如果找到

    C you again 的博客
  • cf1043D. Mysterious Crime(二分 前缀和)

    因此我们按照\(x - y\)排序,对于每个位置,肯定是某一个前缀全选\(x+b\),除此之外都是\(y+a\)

    attack
  • window系统安装msysgit(Git客户端软件)教程

    Git是一种很常用的版本控制系统,当然还有SVN,不过本文只简述msysgit的安装过程

    SmileNicky

扫码关注云+社区

领取腾讯云代金券