如何在Ruby中通过Web Server/TCP sockets传输爬虫采集的数据
问题解答
问题a:Rails中scraper.rb的存放规范
- 如果你这个爬虫是会被Rails业务逻辑(比如控制器、定时任务)频繁调用的服务类,统一放到
app/services/目录下,命名为scraper_service.rb,类名定义为ScraperService,符合Rails服务对象的约定,Rails会自动加载该目录下的文件,无需手动require。 - 如果这个爬虫是独立的、仅偶尔手动执行的脚本类工具,放到
lib/目录下即可,使用时需要手动require引入。
问题b:服务端返回采集数据的实现方法
分两种场景说明:
场景1:沿用你当前的原生TCPServer实现
你现在的代码无法被Postman正常访问的核心原因是:没有遵循HTTP协议规范,Postman发送的是标准HTTP请求,但你的服务只返回了裸数据,没有HTTP响应头,客户端无法识别。只需要修改返回内容的结构,补全HTTP头即可:
- 首先返回HTTP状态行:
HTTP/1.1 200 OK - 然后返回必要的响应头,比如内容类型、内容长度,这里返回JSON的话设
Content-Type: application/json - 空一行,再返回序列化后的采集数据
另外你现在每次请求都会重新爬一遍所有网站,响应会非常慢,建议可以加缓存,比如定时爬取存在内存或者数据库里,请求时直接返回缓存数据。
场景2:接入Rails框架的实现
- 把爬虫逻辑封装到
app/services/scraper_service.rb的run方法中,方法返回采集到的数组 - 新建一个控制器,比如
app/controllers/scrapes_controller.rb,写一个action比如index,调用ScraperService.run,然后用render json: 结果返回数据 - 在
config/routes.rb里加路由配置:get '/scrape', to: 'scrapes#index' - 启动Rails服务后,请求
http://你的ip:3000/scrape就能拿到JSON格式的采集数据。
问题c:ports、sockets、routing的功能关系
- 端口(ports):操作系统层面的网络标识,是0-65535的数字,用来区分同一台服务器上运行的不同网络应用,你开服务占用2000端口,其他应用就不能再用这个端口,相当于服务器的「门牌号」,外部请求要先找到对应端口才能和目标应用通信。
- 套接字(sockets):应用和操作系统网络栈交互的接口,是网络通信的端点,一个socket对应唯一的「IP+端口」组合,负责在客户端和服务端之间传输二进制数据,相当于门旁边的「邮箱」,所有进出这个端口的数据都要通过socket收发。
- 路由(routing):应用层的请求分发规则,同一个端口的socket会收到很多不同的请求(比如请求路径是
/scrape还是/health,请求方法是GET还是POST),路由就是用来把这些请求匹配到对应的处理逻辑上,相当于进了门之后的「房间号」,决定你这次请求要执行哪段代码、返回什么内容。
现有代码修改后的可用版本
require 'socket' require 'mechanize' require 'awesome_print' require 'json' port = ENV.fetch("PORT",2000).to_i server = TCPServer.new(port) puts "Listening on port #{port}..." puts "Current Time : #{Time.now}" # 提前爬一次缓存,避免每次请求都重爬,你也可以加定时任务定期更新缓存 def scrape_data general_sites = [ "https://www.lovebscott.com/", "https://bleacherreport.com/", "https://balleralert.com/", "https://peopleofcolorintech.com/", "https://afrotech.com/", "https://bossip.com/", "https://www.itsonsitetv.com/", "https://theshaderoom.com/", "https://shadowandact.com/", "https://hollywoodunlocked.com/", "https://www.essence.com/", "http://karencivil.com/", "https://www.revolt.tv/" ] holder=[] agent = Mechanize.new general_sites.each do |site| page=agent.get(site); newRet = page.search('a') newRet.each do |e| data = e.attr('href').to_s if(data.length > 50) holder.push(data) end end pp holder.length.to_s + " [ posts total] ==> Now Scraping --> " + site end holder end # 启动时先缓存数据 cached_data = scrape_data loop do client = server.accept # 读取请求行,忽略请求内容简单处理,你也可以在这里解析请求路径做路由分发 request_line = client.gets next unless request_line # 序列化数据为JSON response_body = cached_data.to_json # 补全HTTP响应头 response = <<~HTTP HTTP/1.1 200 OK Content-Type: application/json Content-Length: #{response_body.bytesize} Connection: close #{response_body} HTTP client.write(response) client.close end
修改后启动服务,用Postman访问http://localhost:2000就能拿到JSON格式的采集数据,如果要让同一局域网的同事访问,把localhost换成你机器的局域网IP即可。
内容的提问来源于stack exchange,提问作者jMwofford
相关产品推荐
相关产品推荐

