首页 > 代码库 > C++ 实现网络爬虫
C++ 实现网络爬虫
吐槽
前天心血来潮, 把自己面试经历下了下来.
我觉得自己求职一路来比较心酸, 也付出了比一般人更多的汗水.
本以为写出来, 好歹可以作为一篇励志故事.
得到的评论却是,
语言只是一门工具. ||| 这句话我已经听了4年了!
我以前也是XX, 现在XXX. ||| 直到你膝盖中了一箭?
我也是用c的...只能说,重要的是一种学习的能力,会用即可. ||| 呵呵.
可能能对于你的同学,你是优秀的,但是可能对于你想工作的方向的公司来说,你会的太少,不值得一些公司去培养,所以还是建议你把基础再好好踏实一下更有利。
最后一句话是让我最不爽的.
现在的人啊, 看什么就是什么, 以偏概全,
上的大学不好, 从入大学到大学毕业, 这经过了多少年, 从这多少年前就注定的事能认定这个人现在的水平么?
网投了几百份简历, 没有收到一个面试通知,
直接去公司霸面, 面了2个, 过了2个.
难道这个问题不是出在简历上?
突然就想起了找工作那段时间, 我在某群发了一条挂广告.
立马就有人出来扮演一位阅人无数的高人.
直言说, 你要是很优秀早就有人抢去了, 还是报个培训机构吧.
C++程序员都明白, C++成型慢, 一般公司都不会用新人, 更别说专科毕业的了.
那些习惯了速成的人是不会明白的.
好了, 吐槽完毕. 切换模式.
C++实现网络爬虫
#include <iostream>#include <vector>#include <list>#include <map>#include <queue>#include <string>#include <utility>#include <regex>#include <fstream>#include <WinSock2.h>#include <Windows.h>#pragma comment(lib, "ws2_32.lib")using namespace std;void startupWSA(){ WSADATA wsadata; WSAStartup( MAKEWORD(2,0), &wsadata);}inline void cleanupWSA(){ WSACleanup();}inline pair<string, string> binaryString(const string &str, const string &dilme){ pair<string, string> result(str, ""); auto pos = str.find(dilme); if ( pos != string::npos ) { result.first = str.substr(0, pos); result.second = str.substr(pos + dilme.size()); } return result;}inline string getIpByHostName(const string &hostName){ hostent* phost = gethostbyname( hostName.c_str() ); return phost? inet_ntoa(*(in_addr *)phost->h_addr_list[0]): "";}inline SOCKET connect(const string &hostName){ auto ip = getIpByHostName(hostName); if ( ip.empty() ) return 0; auto sock = socket(AF_INET, SOCK_STREAM, 0); if ( sock == INVALID_SOCKET ) return 0; SOCKADDR_IN addr; addr.sin_family = AF_INET; addr.sin_port = htons(80); addr.sin_addr.s_addr = inet_addr(ip.c_str()); if ( connect(sock, (const sockaddr *)&addr, sizeof(SOCKADDR_IN)) == SOCKET_ERROR ) return 0; return sock;}inline bool sendRequest(SOCKET sock, const string &host, const string &get){ string http = "GET " + get + " HTTP/1.1\r\n" + "HOST: " + host + "\r\n" + "Connection: close\r\n\r\n"; return http.size() == send(sock, &http[0], http.size(), 0);}inline string recvRequest(SOCKET sock){ static timeval wait = {2, 0}; static auto buffer = string(2048 * 100, ‘\0‘); auto len = 0, reclen = 0; do { fd_set fd = {0}; FD_SET(sock, &fd); reclen = 0; if ( select(0, &fd, nullptr, nullptr, &wait) > 0 ) { reclen = recv(sock, &buffer[0] + len, 2048 * 100 - len, 0); if (reclen > 0) len += reclen; } FD_ZERO(&fd); } while (reclen > 0); return len > 11 ? buffer[9] == ‘2‘ && buffer[10] == ‘0‘ && buffer[11] == ‘0‘ ? buffer.substr(0, len) : "" : "";}inline void extUrl(const string &buffer, queue<string> &urlQueue){ if (buffer.empty()) { return ; } smatch result; auto curIter = buffer.begin(); auto endIter = buffer.end(); while ( regex_search(curIter, endIter, result, regex("href=http://www.mamicode.com/"(https?:)?//\\S+\"") ) ) { urlQueue.push(regex_replace( result[0].str(), regex("href=http://www.mamicode.com/"(https?:)?//(\\S+)\""), "$2") ); curIter = result[0].second; }}void Go(const string &url, int count){ queue<string> urls; urls.push(url); for (auto i = 0; i != count; ++i) { if ( !urls.empty() ) { auto &url = urls.front(); auto pair = binaryString( url, "/" ); auto sock = connect(pair.first); if ( sock && sendRequest(sock, pair.first, "/" + pair.second) ) { auto buffer = move( recvRequest(sock) ); extUrl(buffer, urls); }
closesocket(sock); cout << url << ": count=> " << urls.size() << endl; urls.pop(); } }}int main(){ startupWSA(); Go("www.hao123.com", 200); cleanupWSA(); return 0;}
该爬虫只花了1个小时左右.
其实我想说, 写的很烂, 大伙不要喷.
http协议, socket, 正则表达式咱们就不说了..
说说这个原理,
所有的 URL 全都放在 urls 这个队列中.
首先要 push 一个根 URL.
之后爬虫就行动了.
过程大概是这样:
从urls取出一个URL => 读出URL网页全部内容 => 分析所有URL => 把URL放进 urls => 从 urls 弹出一个 URL.
URL 是 host + get.
因此需要一个 binaryString 把它切开.
效率也不是很快, 1分钟大概4W条URL, 去掉重复至少也有好几千吧.
有一点需要注意.
C++11 的正则表达式真心有点难用~~~
我不知道怎么多次匹配..
只好用一个循环了..
网上搜出来一个答案, 写法有点莫名其妙..
执行结果
C++ 实现网络爬虫