以编程方式读取网页

33

我想用C/C++编写一个程序,可以动态地读取网页并从中提取信息。比如,如果你想写一个应用程序来跟踪和记录 eBay 的拍卖活动,有没有一种简单的方法来抓取网页?是否有提供这种功能的库?还有获取特定数据的简单方法吗?


6
在C/C++中非常困难,即使在具有正则表达式、XML解析和HTTP方法等广泛支持的语言(如Java)中也很恼人。至于eBay,它有一个应该被使用的API。 - cletus
7个回答

44

看看cURL库

 #include <stdio.h>
 #include <curl/curl.h>

 int main(void)
 {
   CURL *curl;
   CURLcode res;

   curl = curl_easy_init();
   if(curl) {
     curl_easy_setopt(curl, CURLOPT_URL, "curl.haxx.se");
     res = curl_easy_perform(curl);
      /* always cleanup */
    curl_easy_cleanup(curl);
   }
   return 0;
 }

顺便说一句,如果不一定要用C++的话,我鼓励你尝试使用C#或Java。它们更容易且有内置方法。


7
支持 cURL!我在我的 C++ 应用程序中使用了 cURL,它非常好用,即使遇到代理和其他障碍也没有问题。 - BlaM
2
如果curl为空(在上面的示例中),最好返回一个错误。 - Matthew Flaschen
请查看curlpp - cURL库的C ++封装程序。 - Piotr Dobrogost
赞成建议使用C#或Java。如果您安装了Beautiful Soup包来帮助解析,Python甚至更容易。 - Mike Housky
1
为什么这个回答会被+1并选为最佳答案?实际文档在哪里?代码是做什么的?明显的复制粘贴。 - Chloe Dev
显示剩余2条评论

16

Windows 代码:

#include <winsock2.h>
#include <windows.h>
#include <iostream>
#pragma comment(lib,"ws2_32.lib")
using namespace std;
int main (){
    WSADATA wsaData;
    if (WSAStartup(MAKEWORD(2,2), &wsaData) != 0) {
        cout << "WSAStartup failed.\n";
        system("pause");
        return 1;
    }
    SOCKET Socket=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
    struct hostent *host;
    host = gethostbyname("www.google.com");
    SOCKADDR_IN SockAddr;
    SockAddr.sin_port=htons(80);
    SockAddr.sin_family=AF_INET;
    SockAddr.sin_addr.s_addr = *((unsigned long*)host->h_addr);
    cout << "Connecting...\n";
    if(connect(Socket,(SOCKADDR*)(&SockAddr),sizeof(SockAddr)) != 0){
        cout << "Could not connect";
        system("pause");
        return 1;
    }
    cout << "Connected.\n";
    send(Socket,"GET / HTTP/1.1\r\nHost: www.google.com\r\nConnection: close\r\n\r\n", strlen("GET / HTTP/1.1\r\nHost: www.google.com\r\nConnection: close\r\n\r\n"),0);
    char buffer[10000];
    int nDataLength;
    while ((nDataLength = recv(Socket,buffer,10000,0)) > 0){        
        int i = 0;
        while (buffer[i] >= 32 || buffer[i] == '\n' || buffer[i] == '\r') {
            cout << buffer[i];
            i += 1;
        }
    }
    closesocket(Socket);
        WSACleanup();
    system("pause");
    return 0;
}

1
在发布复制和粘贴的样板/逐字回答多个问题时要小心,社区往往会将其标记为“垃圾邮件”。如果您这样做,通常意味着这些问题是重复的,请将它们标记为重复:https://dev59.com/1HNA5IYBdhLWcg3wUMDn#12374407 - Kev
这段代码存在严重的缺陷:1)如果页面超过10,000个字节且没有不可打印字符,它将读取缓冲区末尾并导致段错误。2)如果网页中有TAB字符(或其他不可打印字符),此代码将向前跳过多达10,000个字节。3)新代码不应使用gethostbyname()。它应该使用getaddrinfo()并支持IPv4和IPv6。 - Imbue
内部的while循环可以被替换为 printf("%.*s", nDataLength, buffer); ,这更加简单、快速、安全。 - Imbue

4

有一个免费的TCP/IP库可用于Windows,支持HTTP和HTTPS - 使用它非常简单。

终极TCP/IP

CUT_HTTPClient http;
http.GET("http://folder/file.htm", "c:/tmp/process_me.htm");    

您还可以使用CUT_DataSource派生类将文件获取并存储在内存缓冲区中。所有常规的HTTP支持都已经准备好了,包括PUT、HEAD等。代理服务器的支持非常简单,安全套接字也是如此。


3

尝试使用像Qt这样的库,它可以从网络中读取数据并从XML文档中获取数据。 此链接展示了如何读取XML源。您可以使用 eBay 源作为参考。


2
你可以使用套接字编程来实现,但是实现协议的部分以可靠地获取页面会比较棘手。最好使用像neon这样的库。这个库很可能已经安装在大多数Linux发行版中。在FreeBSD中使用fetch库。

对于解析数据,由于许多页面不使用有效的XML,您需要使用启发式方法而不是真正的基于yacc的解析器。您可以使用正则表达式或状态转换机来实现这些。因为您正在尝试进行许多试错操作,所以最好使用脚本语言,如Perl。由于网络延迟很高,您不会看到任何性能差异。


虽然它们不是有效的XML,但许多编程语言都有库具备HTML解析器,这将使您能够使用DOM接口来解析HTML文档。 - Daniel Papasian
是的,Neon 也很好(但我大部分的经验都是使用 curl,正如 m3rLinEz 的回答中所提到的。有什么比较吗? - bortzmeyer

2

你没有提及任何平台,所以我给你一个Win32的答案。

从互联网上下载任何东西的一种简单方法是使用URLDownloadToFile函数,并将IBindStatusCallback参数设置为NULL。为了使该函数更加有用,需要实现回调接口。


2
可以使用多平台QT库完成:
QByteArray WebpageDownloader::downloadFromUrl(const std::string& url)
{
    QNetworkAccessManager manager;
    QNetworkReply *response = manager.get(QNetworkRequest(QUrl(url.c_str())));
    QEventLoop event;
    QObject::connect(response, &QNetworkReply::finished, &event, &QEventLoop::quit);
    event.exec();
    return response->readAll();
}

那些数据可以被保存到文件中,或者转换为 std::string:
const string webpageText = downloadFromUrl(url).toStdString();

记住你需要添加。
QT       += network

将QT项目配置以编译代码。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接