你们要的c++爬虫讲解,来了
在讲解c++爬虫之前,首先要了解爬虫的工作原理(知道的可跳过)
爬虫,并不是主动获取信息,而是向网页主机发送请求,网页根据请求返回信息。具体的
过程可以表述成以下几点:
1.写请求书
请求网页返回信息前,你要用一个请求书发起请求。请求书的格式称作http消息结构,
大抵如下:
请求方法 空格 url(子文件名) 空格 协议版本 回车 换行
头部字段名:值 回车 换行
....(重复上一行格式)
其中,请求方法就是python爬虫requests库中get、post等方法,注意要全部大写
url子文件名是去掉http://后,第一个斜线后的内容
协议版本是指http协议(超文本传输协议)的版本,一般填HTTP/1.1
头部字段名和值根据不同情况填写,但是应该有下面一行:
Host: 主机名
主机名就是去掉http之外,第一个斜线前的内容,如www.baidu.com ,等
示例:爬取百度源代码的请求书
GET / HTTP/1.1
User-Agent: curl/7.16.3 libcurl/7.16.3 OpenSSL/0.9.7l zlib/1.2.3
Host: www.baidu.com
Accept-Language: en, mi
2.创建套接字
写好了请求书,得有一个信封把他装起来,这个信封就是套接字(socket)。套接字里有网站
地址、端口号、请求书等等。这个过程比较简单,就不多讲了
3.把套接字发出去
通过send函数,我们可以把套接字发出去,让网站去读取请求。
4.接收信息
网站收到了请求,会做出回应,然后发回来,我们要做的就是定义一个大小足够的变量,存储信息。但是
接受数据用的recv函数可能不会一次全部接受回来,于是我们就要通过while循环,一次一次地接受信息。
接受完信息后,你会发现正文前有一段响应头,大概如下:
HTTP/1.1 200 OK
Date: Fri,24 Dec 2022 12:28:53 GMT
Server: Apache
Last-Modified: Thu, 23 Dec 2022 19:15:56 GMT
ETag: "34aa387-d-1568eb00"
Accept-Ranges: bytes
Content-Length: 51
Vary: Accept-Encoding
Content-Type: text/plain
这可能会对数据分析产生干扰,这时候就要用正则表达式或其他什么东西把他去掉,当然如果不影响也行
以上四步是爬虫需要经历的四步。我们在做python爬虫时,之所以不用这么麻烦,是应为requests和其他爬虫
库已经帮你把他封装成函数了。
那么接下来,开始正式代码讲解
#include <stdio.h>
#include <wchar.h>
#include <locale.h>
#include <bits/stdc++.h>
#include <iostream>
#include <string>
#include <netdb.h>
#include <string.h>
#include <regex.h>
#include <stdlib.h>
using namespace std;
void parseHostAndPagePath(const string url,string &hostUrl,string &pagePath){
//将url转化成无http或https的形式
hostUrl=url;
pagePath="/";//
int pos=hostUrl.find("http://");//第一步查找http(if不加大括号只能运行下一行)
if(-1!=pos)
hostUrl=hostUrl.replace(pos,7,"");//替换http
pos=hostUrl.find("https://");//第二步查找http
if(-1!=pos)
hostUrl=hostUrl.replace(pos,8,"");//再替换http
pos=hostUrl.find("/");//第三步查找斜线
if(-1!=pos)
{
pagePath=hostUrl.substr(pos);//只保存第一个斜线前的url
hostUrl=hostUrl.substr(0,pos);//保留斜线后的子文件名
}//存储数据
}
string getPageContent(const string url){//发起请求
struct hostent *host;
string hostUrl,pagePath;
parseHostAndPagePath(url,hostUrl,pagePath);//调用上一个函数,抽取斜线前的url
if(0==(host=gethostbyname(hostUrl.c_str())))//这个函数返回网站的主机信息,返回结构体
{
cout<<"gethostbyname error\n"<<endl;//若获取主机名失败,抛出错误
exit(1);
}
struct sockaddr_in pin;//调用地址结构体
int port=80;
bzero(&pin,sizeof(pin));//清空pin的内存
pin.sin_family=AF_INET;//传输使用ipv4的地址族
pin.sin_port=htons(port);//sin_port确定传输端口,htons将端口号转为主机可以接受的数据类型
pin.sin_addr.s_addr=((struct in_addr*)(host->h_addr))->s_addr;//确定主机地址
//注:以上均是在给地址结构体pin赋值,相当于在给信填地址
int isock;//isock 用于检测创建成功与否
if((isock=socket(AF_INET,SOCK_STREAM,0))==-1)//创建了套接字
{
cout<<"open socket error\n"<<endl;//创建套接字
exit(1);
}
string requestHeader;//写请求正文
requestHeader="GET "+pagePath+" HTTP/1.1\r\n";
requestHeader+="Host: "+hostUrl+"\r\n";
requestHeader+="Accept: */*\r\n";
requestHeader+="User-Agent: Mozilla/4.0(compatible)\r\n";
requestHeader+="connection:Keep-Alive\r\n";
requestHeader+="\r\n";
if(connect(isock,(const sockaddr*)&pin,sizeof(pin))==-1){//开始与网站主机连接,需代入套接字信息
cout<<"connect error\n"<<endl;//返回值为-1即为连接失败
exit(1);
}
if(send(isock,requestHeader.c_str(),requestHeader.size(),0)==-1){//发送请求正文,c_str函数将string转换成const char*
cout<<"send error\n"<<endl;
exit(1);
}
struct timeval timeout={1,0};//设置时间限制
setsockopt(isock,SOL_SOCKET,SO_RCVTIMEO,(char*)&timeout,sizeof(struct timeval));//发送时限
char c;
bool flag=true;//准备接收数据
while(recv(isock,&c,1,0)>0){
if('\r'==c){
continue;
}else if('\n'==c){
if(false==flag)
break;
flag=false;
}else{
flag=true;
}
}
int len,BUFFER_SIZE=512;
char buffer[BUFFER_SIZE];//一次接受数据的大小
string pageContent="";//存储内容
while((len=recv(isock,buffer,BUFFER_SIZE-1,0))>0){//用循环分批接收数据
buffer[len]='\0';
pageContent+=buffer;//将接收数据一步步存储在pageContent中
}
return pageContent;
}
int main() {
string f;
cin>>f;
cout<<getPageContent(f);//调用函数,获取网页源代码
return 0;
}
不喜勿喷~~