• 科林Linux7_网络爬虫


    一、爬虫

    网络资源的下载工具,工作与万维网环境,持续获取网页网站中的网络信息。可持续的数据采集机器人

    1、搜索引擎技术使用爬虫

    2、数据分析、数据挖掘领域,需要爬虫进行数据准备

    3、数据批处理、采集,大量获取某些网站中的网络资源 

    爬虫进行数据准备截断,数据下载完毕、如何处理与爬虫无关,爬虫只负责下载

    网站之间、网页之间是强关联的,通过超链接技术指向新的网页或网站。通过强关联特性,完成若干网页的拓扑跳转与处理

    网页与网页之间有关联:出链接、入链接

    理论上,通过一个有效的网页可以拓扑所有的网页。

    爬虫在网页的工作:

    1. 获取资源

    2. 获取跳转地址(新链接)

    二、网络资源的种类:

    1. 文本资源(txt,html,shtm,xml)

    2. 二进制数据(jpg,png,gif,bmp)

    3. 音频数据(mp3)

    4. 视频数据(mp4,rmvb,flv)

    URL网络资源定位符,所有的网络资源,都有唯一的URL

    三、关于B/S架构(浏览器/web服务器模型)

    爬虫属于客户端,模拟浏览器行为,获取网站资源

    使用http协议(基于TCP),只要获取了目标的端口和IP,可以直接对网站web服务进行连接

    如果网站使用https协议(SSL),我们需要与网站进行安全连接openssl,否则无法与网站交互

    ① http协议的使用

    ② 正则表达式技术(html语言)

    1)下载网页

    2)提取关键数据

    3)匹配更多新地址

    爬虫的步骤:

    1、下载资源

    2、持续拓扑执行,获取若干资源

    四、http下载

    资源的下载流程(http 80,https 443):

    (一)URL地址解析

    1. 资源完整的URL

    2. 协议类型

    3. 网站域名

    1. #include
    2. struct hostent* ent = gethostbyname(域名);
    3. //ent->h_addr_list;地址表中存储指向服务的公网IP,大端序

    4. 存储路径

    5. 资源名

    6. 端口

    7. ip地址

    1. //Analytical_url.c
    2. #include
    3. int Analytical_url(url_t* url){
    4. int flag=0;//遍历下标
    5. int j=0;//写入下标
    6. int start_len;//协议头长度
    7. int fsize=0;//文件名长度
    8. char* array[]={"http://","https://",NULL};
    9. //初始化url中未使用数组
    10. bzero(url->save_path,1024);
    11. bzero(url->domain,1024);
    12. bzero(url->file,1024);
    13. bzero(url->ip,16);
    14. //判定协议类型
    15. if(strncmp(url->origin,array[0],strlen(array[0]))==0){
    16. url->type=0;
    17. url->port=80;
    18. start_len=strlen(array[0]);
    19. }
    20. else{
    21. url->type=1;
    22. url->port=443;
    23. start_len=strlen(array[1]);
    24. }
    25. //获取域名
    26. for(flag=start_len;url->origin[flag]!='/';flag++){
    27. url->domain[j]=url->origin[flag];
    28. j++;
    29. }
    30. j=0;
    31. //获取文件名长度
    32. for(flag=strlen(url->origin);url->origin[flag]!='/';flag--,fsize++);
    33. //获取文件名
    34. for(flag=strlen(url->origin)-fsize+1;url->origin[flag]!='\0';flag++){
    35. url->file[j]=url->origin[flag];
    36. j++;
    37. }
    38. j=0;
    39. //获取路径
    40. for(flag=start_len+strlen(url->domain);flagorigin)-fsize+1;flag++){
    41. url->save_path[j]=url->origin[flag];
    42. j++;
    43. }
    44. //获取ip
    45. struct hostent* ent=NULL;
    46. if((ent=gethostbyname(url->domain))==NULL){
    47. printf("gethostbyname fail\n");
    48. exit(0);
    49. }
    50. inet_ntop(AF_INET,ent->h_addr_list[0],url->ip,16);
    51. printf("spider [1] Analytical_url:\norigin:%s\ndomain:%s\npath:%s\nfile:%s\nip:%s\nport:%d\ntype:%d\n",url->origin,url->domain,url->save_path,url->file,url->ip,url->port,url->type);
    52. }

    (二)网络初始化

    1. //Net_initalizer.c
    2. #include
    3. int Net_initalizer(){
    4. int sock;
    5. if((sock=socket(AF_INET,SOCK_STREAM,0))==-1){
    6. perror("sock create fail");
    7. exit(0);
    8. }
    9. printf("spider [2] sock create sucess\n");
    10. return sock;
    11. }

    (三)连接

    1. //Connection_web.c
    2. #include
    3. int Connection_web(int sock,url_t* url){
    4. //构造网络信息
    5. struct sockaddr_in webaddr;
    6. webaddr.sin_family=AF_INET;
    7. webaddr.sin_port=htons(url->port);
    8. inet_pton(AF_INET,url->ip,&webaddr.sin_addr.s_addr);
    9. if((connect(sock,(struct sockaddr*)&webaddr,sizeof(webaddr)))==1){
    10. perror("connection failed");
    11. exit(0);
    12. }
    13. printf("spider [3] connection sucess\n");
    14. return 0;
    15. }

    (四)资源下载

    1. 构建请求头(http请求协议)

    页面请求方式:GET POST 

    请求资源权重:对于一个网页的性能和体验来讲,控制好请求发起的优先级是非常重要的,网络带宽是有限的,优先去加载重要的资源,让次要的资源延后,就可以让我们的网站体验提升一个台阶。

    长链接:客户端主动连接,客户端主动断开

    短连接:客户端主动连接,服务端主动断开,请求响应交互一次

    1. #include
    2. int Create_request_str(char* request,url_t* node){
    3. bzero(request,4096);
    4. sprintf(request,"GET %s HTTP/1.1\r\n"\
    5. "Accept:text/html,application/sgtml+xml;q=0.9,image;q=0.8\r\n"\
    6. "User-Agent:Mozilla/5.0 (X11; Linux x86_64)\r\n"\
    7. "Host:%s\r\n"\
    8. "Connection:close\r\n\r\n"\
    9. ,node->origin,node->domain);
    10. printf("spider [4] create_requeset:%s sucess\n",request);
    11. return 0;
    12. }

    2. 发送请求头

    3. 服务器解析请求

    4. 爬虫客户端读取响应

    根据响应码,判断响应是否成功,成功则进入存储流程

    响应头一般都小于8192,为了一次完整的读完响应头,第一次读直接读8192。会读到完整的响应头和一部分响应体

    HTTP/1.1 响应码 响应信息\r\n

    1. #include
    2. int Get_response_code(char* response){
    3. regex_t reg;
    4. regmatch_t match[2];
    5. int code;
    6. char str_code[10];
    7. bzero(str_code,10);
    8. char* reg_str="HTTP/1.1 \\([^\r\n]\\+\\?\\)\r\n";
    9. regcomp(®,reg_str,0);
    10. if((regexec(®,response,2,match,0))==0){
    11. snprintf(str_code,match[1].rm_eo-match[1].rm_so+1,"%s",response+match[1].rm_so);
    12. }
    13. sscanf(str_code,"%d",&code);
    14. return code;
    15. }

    5. 爬虫处理响应内容获取存储资源到磁盘

    1. //Download.c
    2. #include
    3. int Download(char* request,int sock,url_t* url){
    4. char buf[8192];
    5. char response[4096];
    6. bzero(buf,sizeof(buf));
    7. bzero(response,sizeof(response));
    8. int len;
    9. char* pos=NULL;
    10. int fd;
    11. //发送请求头
    12. if((send(sock,request,strlen(request),0))==-1){
    13. perror("first send request faile");
    14. return -1;;
    15. }
    16. printf("spider [5] send_requset sucess\n");
    17. //首次读取响应
    18. if((len=recv(sock,buf,sizeof(buf),0))==-1){
    19. perror("firsr recv failed");
    20. return -1;
    21. }
    22. if((pos=strstr(buf,"\r\n\r\n"))==NULL){
    23. printf("strstr error\n");
    24. return -1;
    25. }
    26. snprintf(response,pos-buf+4,"%s",buf);//提取响应头
    27. printf("spider [6] Get_response head:%s sucess\n",response);
    28. //提取响应码
    29. int code;
    30. code=Get_response_code(response);
    31. if(code==200){
    32. fd=open(url->file,O_RDWR|O_CREAT,0664);
    33. //写入部分
    34. write(fd,pos+4,len-(pos-buf+4));
    35. bzero(buf,sizeof(buf));
    36. while((len=recv(sock,buf,sizeof(buf),0))>0){
    37. write(fd,buf,len);
    38. }
    39. close(fd);
    40. printf("spider [7] %d download sucess\n",code);
    41. }
    42. else{
    43. printf("spider [7] %d download fail\n",code);
    44. close(sock);
    45. return -1;
    46. }
    47. close(sock);
    48. return 0;
    49. }

    五、https下载

    关于HHTPS协议,最大限度保证传输安全

    openssl技术,可以完成https协议的安全认证

    1. sudo apt-get install libssl-dev
    2. sudo apt-get install libssl-doc
    3. man SSL

    http不安全,数据未加密保护,https如何改善?

    1. https协议采用嵌套加密方式,最大限度保证传输安全

    2. 通过认证,让客户端验证服务器的CA数字证书,看是否有效

    加密:

    密钥对。多个公钥和一个私钥构成,密文串由128位随机码组成,保证唯一性

    私钥加密数据,公钥解密

    对称加密:安全性低,适合加密大段数据,速度快

    非堆成加密(RSA):安全性高,适合加密小段重要数据,速度慢

    https 单向认证

    gcc *.c -I ../include -lssl -lcrypto -o download
    1. //Openssl_init.c
    2. #include
    3. ssl_t* Openssl_init(int sock){
    4. ssl_t* ssl=NULL;
    5. if((ssl=(ssl_t*)malloc(sizeof(ssl_t)))==NULL){
    6. perror("malloc ssl faile");
    7. return NULL;
    8. }
    9. SSL_load_error_strings();//初始化错误处理函数
    10. SSL_library_init();//初始化ssl库函数
    11. OpenSSL_add_ssl_algorithms();//初始化加密散列函数
    12. ssl->sslctx=SSL_CTX_new(SSLv23_method());
    13. ssl->sslsock=SSL_new(ssl->sslctx);
    14. SSL_set_fd(ssl->sslsock,sock);//使用tcp sock设置安全套接字
    15. SSL_connect(ssl->sslsock);//发起https安全认证
    16. return ssl;
    17. }

    1. //Download.c
    2. #include
    3. int Download(char* request,int sock,url_t* url,ssl_t* ssl){
    4. char buf[8192];
    5. char response[4096];
    6. bzero(buf,sizeof(buf));
    7. bzero(response,sizeof(response));
    8. int len;
    9. char* pos=NULL;
    10. int fd;
    11. if(ssl){
    12. //发送请求头
    13. if((SSL_write(ssl->sslsock,request,strlen(request)))==-1){
    14. perror("first send request faile");
    15. return -1;;
    16. }
    17. printf("spider [5] HTTPS send_requset sucess\n");
    18. //首次读取响应
    19. if((len=SSL_read(ssl->sslsock,buf,sizeof(buf)))==-1){
    20. perror("firsr recv failed");
    21. return -1;
    22. }
    23. if((pos=strstr(buf,"\r\n\r\n"))==NULL){
    24. printf("strstr error\n");
    25. return -1;
    26. }
    27. snprintf(response,pos-buf+4,"%s",buf);//提取响应头
    28. printf("spider [6] HTTPS Get_response head:%s sucess\n",response);
    29. //提取响应码
    30. int code;
    31. code=Get_response_code(response);
    32. if(code==200){
    33. fd=open(url->file,O_RDWR|O_CREAT,0664);
    34. //写入部分
    35. write(fd,pos+4,len-(pos-buf+4));
    36. bzero(buf,sizeof(buf));
    37. while((len=SSL_read(ssl->sslsock,buf,sizeof(buf)))>0){
    38. write(fd,buf,len);
    39. }
    40. close(fd);
    41. free(ssl);
    42. ssl=NULL;
    43. printf("spider [7] %d HTTPS download sucess\n",code);
    44. }
    45. else{
    46. printf("spider [7] %d HTTPS download fail\n",code);
    47. close(sock);
    48. free(ssl);
    49. ssl=NULL;
    50. return -1;
    51. }
    52. close(sock);
    53. }
    54. else{...}
    55. return 0;
    56. }

    五、持续拓扑与下载资源

    种子URL

    优化选项

    1、DNS 优化

    进程中自建缓存地址,便于后续的使用与访问,减少gethostbyname(开销大)的调用

    2、去重优化

    使用Hash表保存url,进行哈希去重。为了提高去重效率(哈希冲突),可以采用布隆过滤器。但是使用hash的内存占用问题无法解决

    3、并发优化,线程池并发爬虫

    获取更多时间片,提高爬虫的工作能力。随不能提高下载速度,但是可以多线程存储、多线程解析,缩减任务的完成时间

    4、UA池 IP池

    爬虫的抓取策略

    可以用图改变爬虫的工作方式

    反爬虫机制

    反反爬虫

  • 相关阅读:
    AI绘画-Stable Diffusion笔记
    C专家编程 第5章 对链接的思考 5.4 警惕Interpositioning
    计算机网络学习笔记——运输层(b站)
    自研长链接压测概览
    猿创征文 | 常见的五款BI报表介绍
    排序算法之归并排序与基数排序
    scapy工具交互式窗口
    Spring Cloud Config(分布式配置中心)
    ES6 Promise、Generator与async简单介绍与应用
    JEE(面试题一)
  • 原文地址:https://blog.csdn.net/RedGown_ovo/article/details/139967204