【Ruby爬虫01】某吃瓜网站图片数据采集

介绍

由于最近在学习Ruby,写一个爬虫锻炼一下。涉及xml解析、多线程、xpath语法等基础知识。

实现代码

使用说明

使用前请先安装如下gem

gem install  nokogiri http openssl

# nokogiri:一个解析xml和html的库,支持css、xpath语法
# http:一个发送http请求的库
源代码
require 'nokogiri'  
require 'openssl'  
require 'time'  
require 'http'  
require 'thread'  

# 由于网站涉不良内容,网站已编码,自行研究解码方式  
BASE_URL = 'l5VKR[9`aI10.P;m*LzIh,]@P17&0^F' 
  
  
# AES-128-CBC解密 ,网站图片有加密,需要解密 
def aes_128_cbc_decrypt(encrypted_data, key = 'f5d965df75336270', iv = '97b60394abc2fbe1')  
  aes = OpenSSL::Cipher.new('aes-128-cbc')  
  aes.decrypt  
  aes.key = key  
  aes.iv = iv  
  aes.padding = 0  # 禁用填充  
  aes.update(encrypted_data) + aes.final  
end  
  
  
# 获取页面  
def get_page_doc(page_url)   
  begin    # 使用HTTP.follow自动跟随重定向  
    resp = HTTP.follow.get(page_url)  
    # 转换为doc  
    doc = Nokogiri::HTML(resp.body.to_s)  
  rescue Exception => e  
    puts e.message  
  end  
  doc  
end  
  
# 获取列表页面  
def fetch_list_urls(doc)  
  page_list = []  
  urls = []  
  infos = []  
  
  # 获取页面链接地址  
  doc.xpath('//*[@id="archive"]/article/a/@href').each do |link|  
    # 添加  
    urls << BASE_URL + link  
  end  
  
  # 匹配标题及发布时间  
  doc.xpath('//*[@class="post-card"]/div[2]/div').each do |title|  
    info = {}  
    if title.content.gsub(/\s+/,'')!=''  
      # 获取标题  
      t = title.xpath('h2[@class="post-card-title"]/text()')[0].content  
      # 获取发布时间  
      time_str = title.xpath('div[@class="post-card-info"]/span[2]/@content')[0].content  
      publish_time = Time.parse(time_str).strftime('%Y/%m/%d')  
  
      info['title'] , info['publish_time']= t ,publish_time  
      infos << info  
    else  
      # 内容为空的都为广告  
      info['title'], info['publish_time'] = '',''  
      infos << info  
    end  
  end  
  # 转换hash对象  
  urls.each_with_index do |url, i|  
    page= {'url' => url,'title'=>infos[i]['title'],'publish_time'=> infos[i]['publish_time']}  
    page_list << page  
  end  
  # 返回page_list  
  page_list  
end  
  
  
# 获取某一页的图片  
def fetch_page(title,page_url)   
  doc = get_page_doc(page_url)  
  # 去除特殊字符,不然创建目录会失败,windows环境  
  title = title.gsub(/[“”:、\-*<>?\|\/?!!\s]*/,'')  
  # filename = "images/#{title}"  
  filename = File.join(File.dirname($0), "images/#{title}")  
  
  unless doc.nil?  
    # 创建目录  
    Dir.mkdir(filename) unless Dir.exist?(filename)  
    # 匹配页面中的图片  
    urls = doc.xpath('//*[@itemprop="articleBody"]/p/img/@data-xkrkllgl')  
    # 将url添加进队列  
    work_queue = Queue.new  
    urls.each { |img_url| work_queue << img_url }  
  
  
    workers = (1..urls.size).map do |i|  
        Thread.new(i) do  
          begin            
	          while (img_url = work_queue.pop(true))  
	              begin  
	                p "下载图片:#{img_url.content}"  
	                # 读取图片数据,设置超时时间为3s  
	                raw_data = HTTP.timeout(3).get(img_url.content).body.to_s  
	                sleep 0.1  
	                # 解密保存  
	                raw_data = aes_128_cbc_decrypt(raw_data)  
	                File.binwrite("#{filename}/image#{i}.jpg", raw_data)  
	              rescue Exception => e  
	                p e.message  
	                next  
	              end            
	            end          
          rescue ThreadError    
          end  
        end    
    end    
    workers.map(&:join)  
  end  
end  
  
  
def start_crawl  
  page_index = 1  
  loop do  
    begin      
      url = "#{BASE_URL}category/wpcz/#{page_index}/" # 今日吃瓜页面  
      p "正在抓取#{page_index}页,地址:#{url}"  
      doc = get_page_doc(url)  
  
      fetch_list_urls(doc).each do |page|  
        fetch_page(page['title'],page['url'])  
      end  
      # 匹配下一页按钮  
      next_page_xpath = '//*[@class="page-navigator"]/ol/li[@class="btn btn-primary next"]/a/text()'  
      # 退出抓取的条件  
      break if  doc.xpath(next_page_xpath)[0].content != "下一页"  
      # 抓取下一页  
      page_index += 1  
      sleep 0.1  
    rescue Exception => e  
      p e.message  
      page_index += 1  
      next  
    end  
   end
  end  
  
# 执行抓取方法  
if __FILE__==$0  
  start_crawl  
end

本文由【产品经理不是经理】gzh 同步发布,欢迎关注

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/715090.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

元数据、数据元、数据字典、数据模型及元模型的区别详解

在数据管理和分析领域&#xff0c;有许多相似的概念&#xff0c;如元数据、数据元、数据字典、数据模型和元模型。这些概念的定义和应用往往容易混淆。 数据元 数据元是通过一系列属性描述的数据单元&#xff0c;包括定义、标识、表示以及允许值等。这些属性帮助我们理解和使用…

SinNerf理解和效果

文章目录 SinNerf 解决的问题方法和结构自己训练的效果 SinNerf 解决的问题 该方法主要解决的问题是&#xff1a; 现有都使用多张照片来进行nerf 表示的学习&#xff0c;这篇文章的话&#xff0c;主要是想使用一张单视角的照片来Nerf表示的学习。通过从单张照片中得到的伪标签…

书生·浦语大模型实战营第二期作业六

1、安装环境&#xff1a; 2、安装legent和agentlego&#xff1a; 3、部署apiserver&#xff1a; 4、legent web demo&#xff1a; 5、没搜到&#xff0c;很尴尬&#xff1a; 6、自定义工具&#xff1a; 7、智能体“乐高”&#xff1a; 8、智能体工具&#xff0c;识别图片&#…

掌握高等数学、线性代数、概率论所需数学知识及标题建议

在数学的广袤领域中&#xff0c;高等数学、线性代数和概率论作为三大核心分支&#xff0c;不仅在理论研究中占据重要地位&#xff0c;更在实际应用中发挥着举足轻重的作用。为了深入理解和掌握这三门学科&#xff0c;我们需要掌握一系列扎实的数学知识。 高等数学所需数学知识 …

使用自定义注解进行权限校验

一&#xff0c;前言 对于一些重复性的操作我们可以用提取为util的方式进行处理&#xff0c;但也可以更简便一些&#xff0c;比如自定义个注解进行。选择看这篇文章的小伙伴想必都对注解不陌生&#xff0c;但是可能对它的工作原理不太清楚。这里我们用注解实现对接口的权限校验…

Centos7离线安装GCC,G++

系统&#xff1a;Centos7&#xff0c;Py版本&#xff1a;3.9.0 解压完python包后&#xff0c;执行./configure --prefix/usr/local/python39 --enable-shared编译时显示缺少相关编译器&#xff0c;即缺少gcc相关的C编译器&#xff0c;内容如下&#xff1a; 安装gcc所需要的依…

【算法专题--链表】删除排序链表中的重复元素II -- 高频面试题(图文详解,小白一看就懂!!)

目录 一、前言 二、题目描述 三、解题方法 ⭐ 双指针 -- 采用 哨兵位头节点 &#x1f95d; 什么是哨兵位头节点&#xff1f; &#x1f34d; 解题思路 &#x1f34d; 案例图解 四、总结与提炼 五、共勉 一、前言 删除排序链表中的重复元素II元素这道题&#xff0c…

嵌入式复古游戏项目开发与实现

大家好,今天看到一个火柴盒项目,非常的小巧,分享给大家,感兴趣的话,可以复刻一个玩一玩。 MicroByte 是一款微型主机,能够运行 NES、GameBoy、GameBoy Color、Game Gear 和 Sega Master 系统的游戏,所有元器件都设计在这 78 x 17 x 40 mm 的封装中。尽管成品尺寸很小,但…

什么是git?

前言 Git 是一款免费、开源的分布式版本控制系统&#xff0c;用于敏捷高效地处理任何或小或大的项目。是的&#xff0c;我对git的介绍就一条&#xff0c;想看简介的可以去百度一下&#x1f618;&#x1f618;&#x1f618; 为什么要用git&#xff1f; OK&#xff0c;想象一下…

【单片机毕业设计选题24008】-基于单片机的寝室系统设计

系统功能: 1. 采用STM32最小系统板控制&#xff0c;将采集到温湿度光照等传感器数据显示在OLED上 2. 通过离线语音模块开关灯&#xff0c;风扇&#xff0c;门。 3. 监测到MQ2烟雾后触发报警。 4. 语音&手动&定时控制窗帘。 5. 按键开启布防模式&#xff0c;布防后…

C语言实现动态栈

#include<stdio.h> #include<stdlib.h> #include<stdbool.h>// 每一个节点的数据类型 typedef struct Node {int data;struct Node * pNext; }NODE, * PNODE; // NODE等价 struct Node PNODE等价于 struct Node *// 栈 typedef struct Stack {PNODE pTop;P…

Modbus为何要转成ProfiNET

Modbus与ProfiNET代表了工业通讯不同阶段的发展&#xff0c;各自具有优缺点。Modbus简单易用&#xff0c;适合小型系统&#xff1b;ProfiNET高效稳定&#xff0c;适用于大型复杂网络。转换Modbus为ProfiNET可提高系统性能和扩展性。实际场景下&#xff0c;升级生产线控制器为Pr…

Golang: 依赖注入与wire —— 构建高效模块化应用的秘诀

&#x1f49d;&#x1f49d;&#x1f49d;欢迎莅临我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:「stormsha的主页」…

代码随想录-Day32

122. 买卖股票的最佳时机 II 给你一个整数数组 prices &#xff0c;其中 prices[i] 表示某支股票第 i 天的价格。 在每一天&#xff0c;你可以决定是否购买和/或出售股票。你在任何时候 最多 只能持有 一股 股票。你也可以先购买&#xff0c;然后在 同一天 出售。 返回 你能…

Python文本处理:初探《三国演义》

Python文本处理&#xff1a;初探《三国演义》 三国演义获取文本文本预处理分词与词频统计引入停用词后进行词频统计分析人物出场次数结果可视化完整代码 三国演义 《三国演义》是中国古代四大名著之一&#xff0c;它以东汉末年到晋朝统一之间的历史为背景&#xff0c;讲述了魏…

阿里云服务器-Linux搭建fastDFS文件服务器

阿里云官网购买服务器&#xff0c;一般会有降价活动&#xff0c;这两天就发现有活动&#xff0c;99计划活动&#xff08;在活动期内&#xff0c;续费都是99元&#xff09; 阿里云官网-云服务器ECS 在这里&#xff0c;我购买了这台服务器&#xff0c;活动期内续费每年99元&…

二叉树-距离是K的二叉树节点(hard)

目录 一、问题描述 二、解题思路 1.总体思路&#xff08;DFSBFS结合&#xff09; 2.下面举具体例子来对思路进行解释 (1)返回值在一侧的情况 (2)返回值在两侧的情况 三、代码实现 四、刷题链接 一、问题描述 二、解题思路 1.总体思路&#xff08;DFSBFS结合&#xff0…

对接钉钉Stream模式考勤打卡相关事件的指南

钉钉之前的accessToken是公司级别的&#xff0c;现在的accessToken是基于应用的&#xff0c;接口的权限也是基于应用的。所以第一步是在钉钉开放平台&#xff08;https://open-dev.dingtalk.com/&#xff09;创建一个应用。 创建好应用之后&#xff0c;因为我们后续还需要调用钉…

---异常---

我们在运行程序时总遇到各种与报错&#xff0c;数组越界&#xff0c;空指针的引用&#xff0c;这些在java中都称为异常 对于不同的错误都具有一个与他对应的异常类来秒描述 这是对于数组越界这个类里有的方法&#xff0c;这些是描述异常的 在java中有一个完整的描述异常的类的…