用Ruby编写爬虫抓取拉钩的职位信息

2021-08-17

Ruby是非常优雅高效的编程语言,这次我们用Ruby演示如何编写爬虫程序抓取拉钩的职位信息。

本文会以抓取拉钩职位信息为目标,展示Ruby生态中的编写爬虫可能会用到的工具和相关技术。我们会利用Ruby语言的HTTP请求库和HTML格式解析库来抓取静态网页。然后继续探索如何抓取动态网页的内容。

请注意:在Ruby生态中,有很多优秀的工具来帮助实现爬虫程序,我们无法一一覆盖,大家可以自行探索。

我们假定你对Ruby的基本语法有所了解,安装好了ruby开发环境。如果没有,请参考一下链接:

发起HTTP请求

编写爬虫的第一步一般就是要发起请求到目标网页,把网页内容请求回来,然后对请求返回内容进行处理。Ruby中内置了HTTP请求标准库,同时也有很多优秀的第三方库帮助处理HTTP请求。我们主要列举了最通用的三种发送HTTP请求的方式:

  • Net/HTTP
  • Faraday
  • HTTParty

用户可根据实际情况和使用习惯选取其中一个方式即可。

我们先准备好我们的项目代码目录:

mkdir scraping-lagou
cd scraping-lagou
touch lagou.rb
选择一: Net/HTTP

Ruby语言的标准库中自带了HTTP请求的支持,即:Net/HTTP。使用Net/HTTP发送请求的代码如下:

require 'open-uri'
require 'Net/HTTP'

url = "https://www.lagou.com/guangzhou/"
uri = URI.parse(url)
response = Net::HTTP.get_response(uri)
puts response.body

代码很直接,先用open-uri标准库处理需要访问的链接地址,然后使用Net/HTTP发送HTTP请求并获取响应。响应的内容保存在response.body中。

选择二: Faraday

Faraday是最受欢迎的HTTP请求库,功能强大使用方便。因为Faraday是第三方Gem,我们先创建我们的Gemfile:touch Gemfile管理我们的依赖。然后编辑Gemfile添加需要安装的库:

source 'https://rubygems.org'
gem 'faraday'

然后运行:bundle install

编写代码:

require 'faraday'

response = Faraday.get('https://www.lagou.com/guangzhou/')
puts response.body
选择三: HTTParty

HTTParty也是非常优秀的HTTP请求库,同样的我们添加到依赖的Gemfile中,同时使用bundler来安装依赖。

然后看看怎么使用HTTParty发送请求:

require 'HTTParty'

response = HTTParty.get('https://www.lagou.com/guangzhou/')
puts response.body

几乎跟Faraday一样。无论你用那种方式发送请求,这时候我们应该已经能拿到请求响应回来的HTML内容了。下一步我们要解析HTML。

使用Nokogiri解析HTML

一旦我们拿到了目标网页的HTML内容,我们就需要解析HTML内容,然后抽取我们需要的数据内容了。要解析HTML,我们需要依赖Nokogiri库。

第一步还是在Gemfile中添加我们需要的依赖:

source 'https://rubygems.org'
gem 'faraday'
gem 'httparty'
gem 'nokogiri'

然后 bundle install安装依赖。

上面的三种请求方式,我们需要解析的HTML内容都已经保存在response.body里面了,现在添加代码解析HTML:

require 'faraday'
require 'nokogiri'

response = Faraday.get('https://www.lagou.com/guangzhou/')
doc = Nokogiri::HTML(response.body)

doc参数就是经过Nokogiri解析过的对象,事实上类似浏览器解析HTML内容形成的一系列树状结构的节点内容。Nokogiri优秀的地方在于解析好的HTML节点对象支持CSS和XPath选择器方法,能十分方便的定位我们需要的节点并抽取对应的内容。

我们先分析网页结构:

拉钩首页职位

对应的我们可以开始抽取,代码如下:

doc = Nokogiri::HTML(response.body)

jobs = []
doc.css('ul.position_list_ul li.position_list_item').each do |li|
    j = {}
    j['title'] = li.at_css('a.position_link').content
    j['salary'] = li.at_css('span.salary').content
    j['company'] = li.at_css('h3.company_name a').content

    jobs.append(j)
end

puts jobs

我们首先从解析的节点树找到职位的列表元素。然后在列表元素中找到对应的内容,我们现在只抽取职位名,薪资范围和公司名这三个主要的信息。这里请注意css方法默认会返回数组,如果明确知道是单个元素(例如在列表元素内,只有一个职位名)的话,直接用at_css方法会更加方便。

运行爬虫程序,应该就能看到我们抓取的职位信息了:

➜  scraping-lagou ruby lagou.rb
{"title"=>"社群文案策划", "salary"=>"6k-9k·13薪", "company"=>"千聊"}
{"title"=>"测试工程师(电商) (MJ004308)", "salary"=>"10k-20k·13薪", "company"=>"欢聚集团(JOYY Inc.)"}
{"title"=>"高级客户执行", "salary"=>"9k-15k·13薪", "company"=>"驰骛科技ChiefClouds"}
{"title"=>"销售顾问", "salary"=>"10k-15k·13薪", "company"=>"知衣科技"}
...

导出csv文件

爬虫脚本的基本功能已经完成了,不过我们现在的数据只是打印到标准输出上,不方便后续使用。我们利用标准库中的csv模块来帮助我们保存成文件。

require 'faraday'
require 'nokogiri'
require 'csv'

response = Faraday.get('https://www.lagou.com/guangzhou/')
doc = Nokogiri::HTML(response.body)

jobs = []
doc.css('ul.position_list_ul li.position_list_item').each do |li|
    j = {}
    j['title'] = li.at_css('a.position_link').content
    j['salary'] = li.at_css('span.salary').content
    j['company'] = li.at_css('h3.company_name a').content

    jobs.append(j)
end

CSV.open('lagou_jobs.csv', "w") do |csv|
    csv << jobs.first.keys
    jobs.each do |j|
        csv << j.values
    end
end

Bingo!我们抓取了拉钩首页的最新职位的相关信息并保存成了csv格式的文件以备后用。

抓取动态网页

上面的爬虫只能抓取静态网页内容,如果我们要抓取动态网页的内容我们应该怎么办呢。有两种选择:

  1. 使用Headless浏览器加载网页并渲染

比如我们可以利用ferrum库来帮助驱动headless浏览器。具体可以参看对应的示例代码

  1. 使用五号代理的API直接获取渲染以后的HTML内容

另外一种选择,更加方便,让成熟的服务帮助JS渲染,直接获取到渲染以后的HTML内容。五号代理就是其中一个选择。我们只需要修改一行代码就能完成动态网页的抓取,其他代码都不需要改变。

require 'faraday'
require 'nokogiri'
require 'csv'

# 直接使用五号代理的API进行JS渲染
response = Faraday.get('https://api.agentfive.cn/v1', {url: 'https://www.lagou.com/guangzhou/', key: 'YOUR-API-KEY', render: true})
doc = Nokogiri::HTML(response.body)

jobs = []
doc.css('ul.position_list_ul li.position_list_item').each do |li|
    j = {}
    j['title'] = li.at_css('a.position_link').content
    j['salary'] = li.at_css('span.salary').content
    j['company'] = li.at_css('h3.company_name a').content

    jobs.append(j)
end

CSV.open('lagou_jobs.csv', "w") do |csv|
    csv << jobs.first.keys
    jobs.each do |j|
        csv << j.values
    end
end

结语

Ruby语言动态灵活,生态成熟完善,也是编写爬虫的好选择,希望本文能够展示给大家一些跟爬虫相关的常用库和工具。