C ++获取HTML源代码

时间:2013-11-26 22:37:05

标签: c++ winsock

我想知道如何在不使用LibCurl的情况下将网站的HTML源代码下载到字符串中。我在网上搜索了使用Wininet的例子。

下面是我用于Wininet的示例代码。我如何使用Winsock做同样的事情?

    #include "stdafx.h"
#include <windows.h>
#include <wininet.h>
#include <iostream>
#include <string>
#include <stdio.h>
#include <stdlib.h>
using namespace std;

#pragma comment ( lib, "Wininet.lib" )

int main()
{
    HINTERNET hInternet = InternetOpenA("InetURL/1.0", INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0);

    HINTERNET hConnection = InternetConnectA(hInternet, "google.com", 80, " ", " ", INTERNET_SERVICE_HTTP, 0, 0);

    HINTERNET hData = HttpOpenRequestA(hConnection, "GET", "/", NULL, NULL, NULL, INTERNET_FLAG_KEEP_CONNECTION, 0);

    char buf[2048];
    string lol;
    HttpSendRequestA(hData, NULL, 0, NULL, 0);

    DWORD bytesRead = 0;
    DWORD totalBytesRead = 0;
    // http://msdn.microsoft.com/en-us/library/aa385103(VS.85).aspx
    // To ensure all data is retrieved, an application must continue to call the
    // InternetReadFile function until the function returns TRUE and the
    // lpdwNumberOfBytesRead parameter equals zero. 
    while (InternetReadFile(hData, buf, 2000, &bytesRead) && bytesRead != 0)
    {
        buf[bytesRead] = 0; // insert the null terminator.

        puts(buf);          // print it to the screen.
        lol = lol + buf;

        printf("%d bytes read\n", bytesRead);

        totalBytesRead += bytesRead;
    }

    printf("\n\n END -- %d bytes read\n", bytesRead);
    printf("\n\n END -- %d TOTAL bytes read\n", totalBytesRead);

    InternetCloseHandle(hData);
    InternetCloseHandle(hConnection);
    InternetCloseHandle(hInternet);

    cout << "\nThe beginning." << endl << endl << endl;

    cout << lol << endl;


    system("PAUSE");
}

此WinSock示例适用于没有其他路径的网站。我如何获取这样的页面的HTML:(www.website.com/page)

    #include "stdafx.h"
#include <iostream>
#include <winsock2.h>
#include <string>
#include <fstream>
using namespace std;


string get_source()
{
    WSADATA WSAData;
    WSAStartup(MAKEWORD(2, 0), &WSAData);

    SOCKET sock;
    SOCKADDR_IN sin;

    char buffer[1024];

    ////////////////This is portion that is confusing me//////////////////////////////////////////////////
    string srequete = "GET /id/AeroNX/ HTTP/1.1\r\n";
    srequete += "Host: steamcommunity.com\r\n";
    srequete += "Connection: close\r\n";
    srequete += "Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5\r\n";
    srequete += "Accept-Language: fr,fr-fr;q=0.8,en-us;q=0.5,en;q=0.3\r\n";
    srequete += "Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7\r\n";
    srequete += "User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; fr; rv:1.8.1.3) Gecko/20070309 Firefox/2.0.0.3\r\n";
    srequete += "Referer: http://pozzyx.net/\r\n";
    srequete += "\r\n";
    ///////////////////////////////////////////////////////////////////////////////////////////////////////

    size_t requete_taille = srequete.size() + 1;

    char crequete[5000];
    strncpy(crequete, srequete.c_str(), requete_taille);

    int i = 0;
    string source = "";

    sock = socket(AF_INET, SOCK_STREAM, 0);

    sin.sin_addr.s_addr = inet_addr("63.228.223.103"); // epguides.com //why wont it work for 72.233.89.200 (whatismyip.com)
    sin.sin_family = AF_INET;
    sin.sin_port = htons(80); // port HTTP.

    connect(sock, (SOCKADDR *)&sin, sizeof(sin)); // on se connecte sur le site web.
    send(sock, crequete, strlen(crequete), 0); // why do we send the string??


    do
    {
        i = recv(sock, buffer, sizeof(buffer), 0); // le buffer récupère les données reçues.
        source += buffer;
    } while (i != 0);


    closesocket(sock); // on ferme le socket.
    WSACleanup();

    return source;
}

void main()
{
    ofstream fout;
    fout.open("Buffer.txt");
    fout << get_source(); // the string url doesnt matter
    fout.close();
    system("PAUSE");
}

1 个答案:

答案 0 :(得分:9)

好的,我看到你只需要一点点HTTP的帮助,而不是整个事情的细分。不过,在我给你简短的答案之后,我将为未来的读者留下我的完整描述。

简短回答:

在第一行中,您说GET /foo/bar.html HTTP/1.1,中间部分(/foo/bar.html)是资源的路径。因此,例如,如果您想获得http://www.myserver.com/foo/bar.html,那么您可以将/foo/bar.html放在那里。如果您想获得http://www.myserver.com/get/my/file.html,那么您的请求的第一行将是GET /get/my/file.html HTTP/1.1。您的请求的其余行不需要更改以获取不同的资源(尽管如果您完全从其他服务器获取某些内容,例如Host:),则需要更改Host: www.myserver.com

HTTP的完整描述:

您是否尝试在不使用任何库的情况下获取它,只是原始套接字?如果是这样,你将不得不实现HTTP协议(无论如何都是客户端),但好消息是HTTP 非常容易学习并且几乎同样容易实现。 :)

要发送页面请求,请打开Web服务器上端口80的连接。然后发送它:

GET <resource> HTTP/1.1\r\n
Host: <web_server_name>\r\n
Connection: close\r\n
\r\n

请注意,我已明确加入\r\n谎言来向您展示。关于它们有两个重要的事项:1)您必须在协议中使用\r\n而不仅仅是\n,以及2)HTTP标头的末尾必须具有双\r\n\r\n。 (对于您的请求,没有数据部分,因此标题的末尾也是整个请求消息的结尾。)

<resource>替换为您要获取的文件的路径,将<web_server_name>替换为Web服务器的DNS名称。例如,如果您想要检索http://www.cc.gatech.edu/~davel/classes/cs3251/summer2011/test/hypertext.html,则<web_server_name>(主机字段)为www.cc.gatech.edu<resource>/~davel/classes/cs3251/summer2011/test/hypertext.html

Web服务器将在同一套接字上发回HTTP响应消息。如果一切顺利,您将收到一条消息,如下所示:

HTTP/1.1 200 OK\r\n
Date: Mon, 23 May 2005 22:38:34 GMT\r\n
Server: Apache/1.3.3.7 (Unix) (Red-Hat/Linux)\r\n
Last-Modified: Wed, 08 Jan 2003 23:11:55 GMT\r\n
ETag: "3f80f-1b6-3e1cb03b"\r\n
Content-Type: text/html; charset=UTF-8\r\n
Content-Length: 131\r\n
Connection: close\r\n
\r\n
<html>
<head>
  <title>An Example Page</title>
</head>
<body>
  Hello World, this is a very simple HTML document.
</body>
</html>

再次注意双\r\n\r\n,表示HTTP标头的结尾。之后是数据部分,其中包含页面的HTML源代码。我没有明确地显示数据部分的换行符,因为它们是数据本身的一部分,而不是HTTP协议(因此它们不必是\r\n)。另请注意Content-Length字段。它告诉您数据部分的长度是多少字节(在这种情况下是HTML源),因此您可以从套接字读取正确的长度。数据部分末尾没有\r\n。 (数据本身可能包含也可能不包括末尾的换行符。如果是,则它将包含在Content-Length字节中。)

唯一有点困难的部分是接收和解析HTTP消息。我发现接收HTTP的最简单方法是从套接字一次读取一行,解析每个头字段(你不必处理每个字段;你可以忽略其中许多字段)。获得空行后,您就知道标题已完成。然后从Content-Length指定的数据负载的套接字中读取正确的字节数。 (通过验证1)在读取数据部分之前进行错误检查可能是一个好主意,你在响应的第一行得到200 OK - 其他东西表示某种错误,2)你实际得到了标题中某处的Content-Length字段。)

此外,请求中的Connection: close字段在响应中回显,表示服务器在向您发送响应后可以关闭TCP连接。如果您想要发出很多请求,可以改用Connection: keep-alive,但它会变得更复杂,因为您必须注意响应中的Connection字段。从技术上讲,即使您请求保持活动状态,服务器也可以发送回Connection: close并关闭套接字。因此,只需使用Connection: close就可以生成更简单的代码,如果您只想要一个页面,那么它就足够了。

HTTP的维基百科页面有一些帮助,但缺乏细节。 (尽管我从那里无耻地撕掉了我的HTTP响应示例。) https://en.wikipedia.org/wiki/Http

如果某人有一个更好的在线HTTP参考链接(比阅读标准文档更容易理解),请随时添加/编辑此帖子,或将其置于评论中。