IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> C++实现图片爬虫 -> 正文阅读

[C++知识库]C++实现图片爬虫

C++实现图片爬虫

代码是从B站学的,所以并不是很原创

实现思路

  1. 输入需要爬取的网址加入网址队列;
  2. 使用BFS:
    1. 获取网址队列队首中的网址,并从队列中弹出,解析出网址中的主机名以及文件存放的目录(html);
    2. 通过解析出的解析出的信息使用Socket套接字建立连结,发送GET请求;
    3. 获取GET得到的HTML文件内容,并使用正则表达式获得其中关于JPG(或者其他格式的图片)的链接和跳转链接分别存于两个vector数组;
    4. 遍历存放图片链接的vector数组,建立链接,使用GET方法获取JPG图片,存放至文件夹,遍历结束后清空数组(因为当前html页面中的图片都获取了);
    5. 遍历存放跳转链接的vector数组,如果没有获取过该网址的图片(使用map对爬过的网址进行标记)则加入到网址队列。

实现代码

广度优先遍历该网站 BFS

??BFS需要获得起始网址的链接。

void bfs(string beginUrl)
{
    queue<string>q;
    q.push(beginUrl);
    while (!q.empty())
    {
        string cur = q.front();
        mp[cur]++;
        q.pop();
        char tmp[800];
        strcpy(tmp, cur.c_str());
        analyUrl(tmp);  //得到主机名和子链接
        preConnect();   //连接服务器GET发送请求图片数据
        PutHTMLtoSet(); //获取并解析HTML文件中的图片和跳转链接
        for(int i=0;i<photoUrl.size();i++)
            OutImage(photoUrl[i]);  //获取每个图片
        photoUrl.clear();
        for(int i=0;i<comUrl.size();i++)
            if (mp[comUrl[i]] == 0) //将跳转链接加入到队列
                q.push(comUrl[i]);
        comUrl.clear();
    }
}

解析网址 analyUrl

??analyUrl需要参数url即存放待解析的网址数组的地址
??使用strstr判断其中是否有"http://"的信息,如果没有则结束,如果有则从http://的下一个位置开始(所以pos要+7)使用sscanf读取到’/'之前的内容即host,以及之后的内容即文件存放的位置othPath。

bool analyUrl(char* url)
{
    char* pos = strstr(url, "http://");
    if (pos == NULL) return NULL;
    else
    {
        pos += 7;
    }
    sscanf(pos, "%[^/]%s", host, othPath);
    cout << "host: " << host << " repath: " << othPath << endl;
    return true;
}

读取HTML中的图片链接 regexGetimage

??其中allHTML为获得的HTML文本内容,参考正则表达式的使用,图片链接是 src=“链接” 的形式,所以正则表达式为"src="“开头(’'反斜杠是转义符),其中的链接应以.jpg结尾,”.?“中.为匹配除”\n"之外的任何单个字符,*是多个字符,最后?是最短匹配即可,那么从allHTML头开始至结尾使用pattern模式进行匹配,mat[0]为匹配上的串,mat[1]为"(.?.jpg)"中匹配上的串,跳转到mat[0]的下一个位置继续匹配。
?? 正则表达式的使用在文后附上参考博客链接

void regexGetimage(string& allHtml)
{
    smatch mat;
    regex pattern("src=\"(.*?\.jpg)\"");
    string::const_iterator start = allHtml.begin();
    string::const_iterator end = allHtml.end();
    while (regex_search(start, end, mat, pattern))
    {
        string msg(mat[1].first, mat[1].second);
        //photoUrl.push_back(msg);
        photoUrl.push_back("http:"+msg);    //有的网站html文件中图片不在本网站,链接前又没有HTTP://
        start = mat[0].second;
    }
}

读取HTML中的跳转链接 regexCom

??其余同上,跳转href=开头包含http://以及一个或多个非空白符和不是单引号双引号的字符(’\s’为匹配任意的空白符,包括空格、制表符、换页符等空白字符的其中任意一个)最后以"结尾。

void regexGetcom(string& allHtml)
{
	smatch mat;
	//regex pattern("href=\"(http://[^\s'\"]+)\"");
    regex pattern("href=\"([^\s'\"]+)\"");
	string::const_iterator start = allHtml.begin();
	string::const_iterator end = allHtml.end();
	while (regex_search(start, end, mat, pattern))
	{
		string msg(mat[1].first, mat[1].second);
		comUrl.push_back(msg);
		start = mat[0].second;
	}
}

与网站建立连接 preConnect

??前半部分为Socket相关,建立连结后发送GET请求格式如下:
??GET 文件存放地址 HTTP/1.1
??Host: 主机名
??Connection: Close
??存放地址之前解析出在OthPath,主机名在Host中。

void preConnect()
{
    WSADATA wd;
    WSAStartup(MAKEWORD(2, 2), &wd);
    sock = socket(AF_INET, SOCK_STREAM, 0);
    if (sock == INVALID_SOCKET)
    {
        cout << "建立socket失败,错误码" << WSAGetLastError() << endl;
        return;
    }
    sockaddr_in sa = { AF_INET };
    int n = bind(sock, (sockaddr*)&sa, sizeof(sa));
    if (n == SOCKET_ERROR)
    {
        cout << "bind函数失败,错误码" << WSAGetLastError() << endl;
        return;
    }
    struct hostent* p = gethostbyname(host);//根据主机名得到主机信息
    if (p == NULL)
    {
        cout << "主机无法解析出ip,错误码" << WSAGetLastError() << endl;
        return;
    }
    sa.sin_port=htons(80);
    memcpy(&sa.sin_addr,p->h_addr, 4);
    //cout << sa.sin_addr.S_un.S_addr << endl;
    n = connect(sock, (sockaddr*)&sa, sizeof(sa));
    if (n == SOCKET_ERROR)
    {
        cout << "connect函数失败,错误码" << WSAGetLastError() << endl;
        return;
    }
    //向服务器发起GET
    string reqInfo = "GET " + (string)othPath + " HTTP/1.1\r\nHost: " + (string)host + "\r\nConnection:Close\r\n\r\n";
    //cout << reqInfo << endl;
    if (SOCKET_ERROR == send(sock, reqInfo.c_str(), reqInfo.size(), 0))
    {
        cout << "send错误,错误码" << WSAGetLastError() << endl;
        closesocket(sock);
        return;
    }
}

输出图片 OutImage

??从imageUrl中得到图片的链接,使用解析出主机名和存放地址并建立链接发送GET请求,从recv中逐段读取接收到的图片信息(接收到的图片命名就从链接最后开始往前读到/为止,因为希望将图片存放在当前目录的imgData文件夹中,所以photoname前在加上./imgData/的路径),接收到的HTML的头部以\r\n\r\n结束,之后为图片内容。

void OutImage(string imageurl)
{
    int n;
    char temp[800];
    //cout << imageurl << endl;
    strcpy(temp, imageurl.c_str());
    analyUrl(temp);
    preConnect();
    string photoname;
    photoname.resize(imageurl.size());
    int k = 0;
    for (int i = imageurl.length()-1; i >=0 && imageurl[i]!='/' ; i--)
    {
        photoname = imageurl[i]+ photoname;
    }
    photoname = "./imgData/" + photoname;// +".jpg";
    fstream file;
    file.open(photoname, ios::out | ios::binary);
    char buf[1024];
    memset(buf, 0, sizeof(buf));
    n = recv(sock, buf, sizeof(buf) - 1, 0);
    char* cpos = strstr(buf, "\r\n\r\n");
    file.write(cpos + strlen("\r\n\r\n"), n - (cpos - buf) - strlen("\r\n\r\n"));
    while ((n = recv(sock, buf,sizeof(buf)-1,0))>0)
    {
        file.write(buf, n);
    }
    file.close();
}

主函数 main

??输入网址,建立文件夹,调用BFS之后就没啥事了。

int main()
{
    int i = 0;
    string srul;
    cin >> srul;
    CreateDirectoryA("./imgData", 0);
    bfs(srul);
}

完整代码

#include<iostream>
#include<fstream>
#include<cstdio>
#include<string>
#include<cstring>
#include<regex>     //正则表达式
#include<vector>
#include<queue>
#include<map>
#include<algorithm>
#include<WinSock2.h>    
#pragma comment(lib,"ws2_32.lib")
using namespace std;
char host[500]; //存放主机数组
int num = 1;
char othPath[800];
string allHtml;
vector<string> photoUrl;
vector<string> comUrl;
map<string, int>mp;
SOCKET sock;
bool analyUrl(char* url)
{
    char* pos = strstr(url, "http://");
    if (pos == NULL) return NULL;
    else
    {
        pos += 7;
    }
    sscanf(pos, "%[^/]%s", host, othPath);
    cout << "host: " << host << " repath: " << othPath << endl;
    return true;
}

void regexGetimage(string& allHtml)
{
    smatch mat;
    regex pattern("src=\"(.*?\.jpg)\"");
    string::const_iterator start = allHtml.begin();
    string::const_iterator end = allHtml.end();
    while (regex_search(start, end, mat, pattern))
    {
        string msg(mat[1].first, mat[1].second);
        //photoUrl.push_back(msg);
        photoUrl.push_back("http:"+msg);    //有的网站html文件中图片不在本网站,链接前又没有HTTP://
        start = mat[0].second;
    }
}

void regexGetcom(string& allHtml)
{
	smatch mat;
	//regex pattern("href=\"(http://[^\s'\"]+)\"");
    regex pattern("href=\"([^\s'\"]+)\"");
	string::const_iterator start = allHtml.begin();
	string::const_iterator end = allHtml.end();
	while (regex_search(start, end, mat, pattern))
	{
		string msg(mat[1].first, mat[1].second);
		comUrl.push_back(msg);
		start = mat[0].second;
	}
}

void preConnect()
{
    WSADATA wd;
    WSAStartup(MAKEWORD(2, 2), &wd);
    sock = socket(AF_INET, SOCK_STREAM, 0);
    if (sock == INVALID_SOCKET)
    {
        cout << "建立socket失败,错误码" << WSAGetLastError() << endl;
        return;
    }
    sockaddr_in sa = { AF_INET };
    int n = bind(sock, (sockaddr*)&sa, sizeof(sa));
    if (n == SOCKET_ERROR)
    {
        cout << "bind函数失败,错误码" << WSAGetLastError() << endl;
        return;
    }
    struct hostent* p = gethostbyname(host);//根据主机名得到主机信息
    if (p == NULL)
    {
        cout << "主机无法解析出ip,错误码" << WSAGetLastError() << endl;
        return;
    }
    sa.sin_port=htons(80);
    memcpy(&sa.sin_addr,p->h_addr, 4);
    //cout << sa.sin_addr.S_un.S_addr << endl;
    n = connect(sock, (sockaddr*)&sa, sizeof(sa));
    if (n == SOCKET_ERROR)
    {
        cout << "connect函数失败,错误码" << WSAGetLastError() << endl;
        return;
    }
    //向服务器发起GET
    string reqInfo = "GET " + (string)othPath + " HTTP/1.1\r\nHost: " + (string)host + "\r\nConnection:Close\r\n\r\n";
    //cout << reqInfo << endl;
    if (SOCKET_ERROR == send(sock, reqInfo.c_str(), reqInfo.size(), 0))
    {
        cout << "send错误,错误码" << WSAGetLastError() << endl;
        closesocket(sock);
        return;
    }
}

void OutImage(string imageurl)
{
    int n;
    char temp[800];
    //cout << imageurl << endl;
    strcpy(temp, imageurl.c_str());
    analyUrl(temp);
    preConnect();
    string photoname;
    photoname.resize(imageurl.size());
    int k = 0;
    for (int i = imageurl.length()-1; i >=0 && imageurl[i]!='/' ; i--)
    {
        photoname = imageurl[i]+ photoname;
    }
    photoname = "./imgData/" + photoname;// +".jpg";
    fstream file;
    file.open(photoname, ios::out | ios::binary);
    char buf[1024];
    memset(buf, 0, sizeof(buf));
    n = recv(sock, buf, sizeof(buf) - 1, 0);
    char* cpos = strstr(buf, "\r\n\r\n");
    file.write(cpos + strlen("\r\n\r\n"), n - (cpos - buf) - strlen("\r\n\r\n"));
    while ((n = recv(sock, buf,sizeof(buf)-1,0))>0)
    {
        file.write(buf, n);
    }
    file.close();
}

void PutHTMLtoSet()
{
    
    int n;
    char buf[1024];
    //if (recv(sock, buf, sizeof(buf) - 1, 0) == SOCKET_ERROR) cout << "error";
    while ((n = recv(sock, buf, sizeof(buf) - 1, 0)) > 0)
    {
        buf[n] = '\0';
        allHtml += string(buf);
    }
    regexGetimage(allHtml);
    regexGetcom(allHtml);
}

void bfs(string beginUrl)
{
    queue<string>q;
    q.push(beginUrl);
    while (!q.empty())
    {
        string cur = q.front();
        mp[cur]++;
        q.pop();
        char tmp[800];
        strcpy(tmp, cur.c_str());
        analyUrl(tmp);  //得到主机名和子链接
        preConnect();   //连接服务器GET发送请求图片数据
        PutHTMLtoSet(); //获取并解析HTML文件中的图片和跳转链接
        for(int i=0;i<photoUrl.size();i++)
            OutImage(photoUrl[i]);  //获取每个图片
        photoUrl.clear();
        for(int i=0;i<comUrl.size();i++)
            if (mp[comUrl[i]] == 0) //将跳转链接加入到队列
                q.push(comUrl[i]);
        comUrl.clear();
    }
}
int main()
{
    int i = 0;
    string srul;
    cin >> srul;
    CreateDirectoryA("./imgData", 0);
    bfs(srul);
}

总结

  1. 从HTML中解析信息的过程中链接获取和存放需要根据爬取的网站进行一定的修改,要查看网站HTML文件中的具体信息
  2. 调试,有可能用到cmd中的telnet命令
  3. 正则表达式参考链接https://www.cnblogs.com/guozht/p/7610877.html
  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2021-11-12 19:24:25  更:2021-11-12 19:26:02 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 5:20:38-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码