zoukankan      html  css  js  c++  java
  • 构建自己的DSL之一 Simple Crawler

    转载请标明出处:http://fuliang.iteye.com/blog/1122008 

    经常需要从网上抓取一些需要的内容做成语料,供分类使用。所以需要一个灵活的抓取、抽取程序-自己的DSL来做这件事,这样每次只需要写几行代码就能得到需要的内容。比如我比较希望以下几行代码就能把我的博客的内容给抓下来: 
    Ruby代码  收藏代码
    1. crawler = Crawler.new  
    2. 1.upto(10) do |pn|  
    3.     urls = []  
    4.     crawler.fetch "http://fuliang.iteye.com/?page=#{pn}" do |page|  
    5.         page.css("div.blog_title > h3 > a").each do |node|  
    6.             urls << "http://fuliang.iteye.com#{node.attributes['href']}"  
    7.         end  
    8.     end  
    9.   
    10.     urls.each do |url|  
    11.         crawler.fetch url do |page|  
    12.             page.xpath(:title => '//*[@id="main"]/div/div[2]/h3/a',:content => '//*[@id="blog_content"]').each do |entry|  
    13.                 printf("%s\t%s\n",entry[:title].text.gsub(/\s+/,""),entry[:content].text.gsub(/\s+/,""))  
    14.             end  
    15.         end  
    16.     end  
    17.     break  
    18. end  

    我们先创建一个Crawler对象,然后按照我博客的列表页分页特征,得到第pn页的url是 
    http://fuliang.iteye.com/?page=#{pn},当然有可能有复杂的规则,构建列表页的url列表,然后遍历。crawler只有一个fetch方法,就可以把页面fetch下来,然后得到这个页面在块中处理。这个页面可以直接根据xpath、css来得到需要抽取的内容,还可以一次抽取一个记录,只需要向xpath,css方法中传递一个字段到xpath/css的hash,然后得到对应的记录的hash。 
    按照上面的描述,我们先编写一个简单的Crawler,为了防止被封我们使用了几个代理: 
    Ruby代码  收藏代码
    1. class Crawler  
    2.     def initialize  
    3.         @proxies = 1.upto(6).collect{|index| "http://l-crwl#{index}:1080"}  
    4.     end  
    5.   
    6.     def fetch(url)  
    7.         yield Page.new( Nokogiri::HTML(open(url,fetch_options)) )  
    8.     end  
    9.   
    10. private  
    11.     def rand_proxy  
    12.         @proxies[(rand * 6).to_i]  
    13.     end  
    14.   
    15.     def fetch_options  
    16.         user_agent = "Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.2) Gecko/20061201 Firefox/2.0.0.2 (Ubuntu-feisty)"  
    17.   
    18.         fetch_options = {  
    19.             "User-Agent" => user_agent,  
    20.             "proxy" => rand_proxy  
    21.         }  
    22.     end  
    23. end  

    然后我们定义Page类,动态定义了css和xpath的方法,我们直接代理给Nokogiri 
    的css、xpath,让它来做事情,我们收集一下抽取的结果,一些就ok了: 
    Ruby代码  收藏代码
    1. class Page  
    2.     def initialize(html)  
    3.         @html = html  
    4.     end  
    5.   
    6.     class_eval do  
    7.         [:css,:xpath].each do |extract_by|  
    8.             define_method extract_by do |arg,&block|  
    9.                 if arg.is_a? String then  
    10.                     if block.nilthen  
    11.                        @html.send(extract_by,arg)  
    12.                     else  
    13.                         block.call(@html.send(extract_by,arg))  
    14.                     end  
    15.                 elsif arg.is_a? Hash then  
    16.                     extract_raw = arg.collect{|key,value| [key, @html.send(extract_by,value)]}  
    17.                     data = extract_raw.collect do |key, vals|  
    18.                         ([key] * vals.size).zip(vals)  
    19.                     end  
    20.                     result =  data[0].zip(*data[1..-1]).collect{|e| Hash[ * e.flatten ]}  
    21.                     if block.nilthen  
    22.                         result  
    23.                     else  
    24.                         block.call(result)  
    25.                     end  
    26.                 else  
    27.                     raise ArgumentError.new('Argument type must String or Hash type')  
    28.                 end  
    29.             end  
    30.         end  
    31.     end  
    32. end  


    整个的代码: 
    Ruby代码  收藏代码
    1. #!/usr/bin/env ruby  
    2.   
    3. require 'rubygems'  
    4. require 'nokogiri'  
    5. require 'open-uri'  
    6.   
    7. class Crawler  
    8.     def initialize  
    9.         @proxies = 1.upto(6).collect{|index| "http://l-crwl#{index}:1080"}  
    10.     end  
    11.       
    12.     def fetch(url)  
    13.         yield Page.new( Nokogiri::HTML(open(url,fetch_options)) )  
    14.     end  
    15.   
    16. private  
    17.     def rand_proxy  
    18.         @proxies[(rand * 6).to_i]    
    19.     end  
    20.   
    21.     def fetch_options    
    22.         user_agent = "Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.2) Gecko/20061201 Firefox/2.0.0.2 (Ubuntu-feisty)"  
    23.   
    24.         fetch_options = {    
    25.             "User-Agent" => user_agent,    
    26.             "proxy" => rand_proxy    
    27.         }    
    28.     end    
    29. end  
    30.   
    31. class Page  
    32.     def initialize(html)  
    33.         @html = html  
    34.     end  
    35.   
    36.     class_eval do  
    37.         [:css,:xpath].each do |extract_by|  
    38.             define_method extract_by do |arg,&block|  
    39.                 if arg.is_a? String then  
    40.                     if block.nilthen   
    41.                        @html.send(extract_by,arg)  
    42.                     else  
    43.                         block.call(@html.send(extract_by,arg))  
    44.                     end  
    45.                 elsif arg.is_a? Hash then  
    46.                     extract_raw = arg.collect{|key,value| [key, @html.send(extract_by,value)]}  
    47.                     data = extract_raw.collect do |key, vals|  
    48.                         ([key] * vals.size).zip(vals)  
    49.                     end  
    50.                     result =  data[0].zip(*data[1..-1]).collect{|e| Hash[ * e.flatten ]}  
    51.                     if block.nilthen  
    52.                         result  
    53.                     else  
    54.                         block.call(result)  
    55.                     end  
    56.                 else  
    57.                     raise ArgumentError.new('Argument type must String or Hash type')  
    58.                 end  
    59.             end  
    60.         end  
    61.     end  
    62. end  
    63.   
    64. crawler = Crawler.new  
    65. 1.upto(10) do |pn|  
    66.     urls = []  
    67.     crawler.fetch "http://fuliang.iteye.com/?page=#{pn}" do |page|  
    68.         page.css("div.blog_title > h3 > a").each do |node|  
    69.             urls << "http://fuliang.iteye.com#{node.attributes['href']}"  
    70.         end  
    71.     end  
    72.   
    73.     urls.each do |url|  
    74.         crawler.fetch url do |page|  
    75.             page.xpath(:title => '//*[@id="main"]/div/div[2]/h3/a',:content => '//*[@id="blog_content"]').each do |entry|  
    76.                 printf("%s\t%s\n",entry[:title].text.gsub(/\s+/,""),entry[:content].text.gsub(/\s+/,""))  
    77.             end  
    78.         end  
    79.     end  
    80.     break  
    81. end  
  • 相关阅读:
    若依ruoyi summernote 富文本提交数据 部分代码被过滤 修改xss配置可忽略过滤
    java中Map实现1对多
    Windows()64位)下Redis的安装使用
    SpringMvc java.lang.ClassNotFoundException: org.springframework.web.servlet.DispatcherServlet
    字符替换
    oracle null 空值排序- NVL,COALESCE , GREATEST ,LEAST
    oracle 日期,时间函数 date,to_date,extract,to_timestamp,last_day,frist_day
    获取树形数据(区域,父子级关系的树形数据)
    git的操作
    MySQL数据库之MyISAM与InnoDB的区别
  • 原文地址:https://www.cnblogs.com/lexus/p/2265965.html
Copyright © 2011-2022 走看看