左手用R右手Python系列——面向对象编程基础

面向对象编程是程序设计中一种重要且高效的编程规范,它区别于常见的面向过程编程。在R语言以及Python的程序包开发过程中,大量使用了面向对象的编程范式。

百度百科关于面向对象编程的权威解释是:

面向对象程序设计(英语:Object-oriented programming,缩写:OOP)是一种程序设计范型,同时也是一种程序开发的方法。其最重要的三大特征是封装、继承、多态。

对象指的是类的实例。它将对象作为程序的基本单元,将程序和数据封装其中,以提高软件的重用性、灵活性和扩展性。

R语言中的面向对象编程是通过泛型函数来实现的,R语言中现有的S3类、S4类、以及R6类等都可以实现面向对象的编程规范。

如果你还对R语言的S3、S4类不熟悉,可以参考张丹老师的这几篇博客:

http://blog.fens.me/r-object-oriented-intro/
http://blog.fens.me/r-class-s3/
http://blog.fens.me/r-class-s4/

张丹老师的这几篇文章详细的介绍了R语言中S3类、S4类面向对象的实现。

以下我将之前一篇介绍多进程/多线程的案例改造成基于S3、S4类的面向对象模式。

library("RCurl")
library("XML")
library("magrittr")

定义类

因为我们的任务是抓取天善智能主页上大数据相关的职位信息,所以类定义为GetData,而后仅仅定义了一个可调用的方法——hellobi(类中可以定义的方法调用可以有很多个。)

GetData <- function(object) UseMethod("GetData")

定义类中的可调用方法

GetData.hellobi <- function(object){
    d      <- debugGatherer()
    handle <- getCurlHandle(debugfunction=d$update,followlocation=TRUE,cookiefile="",verbose = TRUE)
    while (object$i < 10){
        object$i = object$i+1
        url <- sprintf("https://www.hellobi.com/jobs/search?page=%d",object$i)
        tryCatch({
        content    <- getURL(url,.opts=list(httpheader=object$headers),.encoding="utf-8",curl=handle) %>% htmlParse() 
        job_item   <- content %>% xpathSApply(.,"//div[@class='job_item_middle pull-left']/h4/a",xmlValue)
        job_links  <- content %>% xpathSApply(.,"//div[@class='job_item_middle pull-left']/h4/a",xmlGetAttr,"href")
        job_info   <- content %>% xpathSApply(.,"//div[@class='job_item_middle pull-left']/h5",xmlValue,trim = TRUE) 
        job_salary <- content %>% xpathSApply(.,"//div[@class='job_item-right pull-right']/h4",xmlValue,trim = TRUE) 
        job_origin <- content %>% xpathSApply(.,"//div[@class='job_item-right pull-right']/h5",xmlValue,trim = TRUE)
        myreslut   <-  data.frame(job_item,job_links,job_info,job_salary,job_origin,stringsAsFactors = FALSE) 
        object$fullinfo  <- rbind(object$fullinfo,myreslut) 
        cat(sprintf("第【%d】页已抓取完毕!",object$i),sep = "\n")
        },error = function(e){
        cat(sprintf("第【%d】页抓取失败!",object$i),sep = "\n")
        })
        Sys.sleep(runif(1))
    }
        cat("all page is OK!!!")
        return (object$fullinfo)
}

创建类实例(同时将实例与方法绑定):

initialize <- list(
             i = 0,
             fullinfo = data.frame(),
             headers  = c(
                       "Referer"="https://www.hellobi.com/jobs/search",
                       "User-Agent"="Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.79 Safari/537.36"
                     )
              )
mywork <- structure(initialize, class = "hellobi")

执行程序

mydata <- GetData(mywork)

当然你也可以在GetData类下定义多个方法,比如抓取课程信息,抓取博客文章信息等等。仅需将实例绑定到对应的方法上,那么在类中传入实例之后,类便可以自动搜寻到该实例的方法,并自动执行该实例对应方法的函数调用,R语言中的summary、plot、print函数等都是通过这种泛型函数的模式来实现的。

使用基于S4类的方法来实现以上案例的面向对象模式

initialize <- list(
             i = 0,
             fullinfo = data.frame(),
             headers  = c(
                       "Referer"="https://www.hellobi.com/jobs/search",
                       "User-Agent"="Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.79 Safari/537.36"
                      )
             )

注册类:

setClass("GetData",
         slots = c(
                    i="numeric", 
                    fullinfo="data.frame",
                    headers="character"
                    ),
         prototype = initialize
        )

创建类

GetData <- new('GetData')

定义泛型函数:

setGeneric('hellobi',  
    function(object) {  
        standardGeneric('hellobi')  
    }  
  )

将泛型函数作为方法与类绑定

setMethod('hellobi', 'GetData',   
    function(object) {  
    d      <- debugGatherer()
    handle <- getCurlHandle(debugfunction=d$update,followlocation=TRUE,cookiefile="",verbose = TRUE)
    while (object@i < 10){
        object@i = object@i+1
        url <- sprintf("https://www.hellobi.com/jobs/search?page=%d",object@i)
        tryCatch({
        content    <- getURL(url,.opts=list(httpheader=object@headers),.encoding="utf-8",curl=handle) %>% htmlParse() 
        job_item   <- content %>% xpathSApply(.,"//div[@class='job_item_middle pull-left']/h4/a",xmlValue)
        job_links  <- content %>% xpathSApply(.,"//div[@class='job_item_middle pull-left']/h4/a",xmlGetAttr,"href")
        job_info   <- content %>% xpathSApply(.,"//div[@class='job_item_middle pull-left']/h5",xmlValue,trim = TRUE) 
        job_salary <- content %>% xpathSApply(.,"//div[@class='job_item-right pull-right']/h4",xmlValue,trim = TRUE) 
        job_origin <- content %>% xpathSApply(.,"//div[@class='job_item-right pull-right']/h5",xmlValue,trim = TRUE)
        myreslut   <-  data.frame(job_item,job_links,job_info,job_salary,job_origin,stringsAsFactors = FALSE) 
        object@fullinfo  <- rbind(object@fullinfo,myreslut) 
        cat(sprintf("第【%d】页已抓取完毕!",object@i),sep = "\n")
        },error = function(e){
        cat(sprintf("第【%d】页抓取失败!",object@i),sep = "\n")
        })
        Sys.sleep(runif(1))
    }
    cat("all page is OK!!!")
    return (object@fullinfo)
    }  
) 
###执行类中的方法:
mydata1 <- hellobi(GetData)  

DT::datatable(mydata1)

关于S3方法与S4方法之间的区别:

  1. 在定义S3类的时候,没有显式的定义过程,而定义S4类的时候需要调用函数setClass;
  2. 在初始化S3对象的时候,只是建立了一个list,然后设置其class属性,而初始化S4对象时需要使用函数new;
  3. 提取变量的符号不同,S3为$,而S4为@;
  4. 在应用泛型函数时,S3需要定义f.classname,而S4需要使用setMethod函数;
  5. 在声明泛型函数时,S3使用UseMethod(), 而S4使用setGeneric()。

Python:

from urllib.request import urlopen,Request
import pandas as pd
import time
from lxml import etree

定义类:

class GetData:
    #初始化参数
    def __init__(self):
        self.start = 0
        self.headers = {
             'User-Agent':'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.79 Safari/537.36',
             'Referer':'https://www.hellobi.com/jobs/search'
              }
        self.myresult =  {
              "job_item":[],
              "job_links":[],
              "job_info":[],
              "job_salary":[],
              "job_origin":[]
              }
              
    #定义可用方法:
    def getjobs(self):
        while self.start < 10:
            self.start +=1 
            url = "https://www.hellobi.com/jobs/search?page={}".format(self.start)
            try:
                pagecontent=urlopen(Request(url,headers=self.headers)).read().decode('utf-8')
                result = etree.HTML(pagecontent)
                self.myresult["job_item"].extend(result.xpath('//div[@class="job_item_middle pull-left"]/h4/a/text()'))
                self.myresult["job_links"].extend(result.xpath('//div[@class="job_item_middle pull-left"]/h4/a/@href'))
                self.myresult["job_info"].extend([ text.xpath('string(.)').strip() for text in  result.xpath('//div[@class="job_item_middle pull-left"]/h5')])
                self.myresult["job_salary"].extend(result.xpath('//div[@class="job_item-right pull-right"]/h4/span/text()'))
                self.myresult["job_origin"].extend(result.xpath('//div[@class="job_item-right pull-right"]/h5/span/text()'))
                print("正在抓取第【{}】页".format(self.start))
            except:
                print("第【{}】页抓取失败".format(self.start))
            time.sleep(1)
        print("everything is OK")
        return pd.DataFrame(self.myresult)
        
if __name__ == "__main__":
    t0 = time.time()
    mydata = GetData()
    myresult = mydata.getjobs()
    t1 = time.time()
    total = t1 - t0
    print("消耗时间:{}".format(total))

以上便是在R语言和Python中使用面向对象编程的模式所做的爬虫写程序,仅作为学习面向对象编程思维的实战案例,至于更为详尽的关于R语言和Python中面向对象的思维及其高阶应用,还需要各位小伙伴儿参考各大主流加载包的源码,比如R语言的ggplot2包、rvest包等内部大量使用基于S3类的编程模式,Python中的主流加载库也都是如此。 往期案例数据请移步本人GitHub: https://github.com/ljtyduyu/DataWarehouse/tree/master/File

原文发布于微信公众号 - 数据小魔方(datamofang)

原文发表时间:2017-12-24

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏about云

hadoop开发必读:认识Context类的作用

问题导读: 1.Context能干什么? 2.你对Context类了解多少? 3.Context在mapreduce中的作用是什么? 本文实在能够阅读源码...

3484
来自专栏灯塔大数据

技术 | Python从零开始系列连载(十九)

但它的特点就是下次使用next(a)时,接着上次的断点继续运行,直到下一个yield

1153
来自专栏Java帮帮-微信公众号-技术文章全总结

​图;代码轻松理解,代理

代理 代理是英文 Proxy 翻译过来的。我们在生活中见到过的代理,大概最常见的就是朋友圈中卖面膜的同学了。 她们从厂家拿货,然后在朋友圈中宣传,然后卖给熟人。...

3145
来自专栏小樱的经验随笔

51Nod 1182 完美字符串(字符串处理 贪心 Facebook Hacker Cup选拔)

1182 完美字符串 ?             题目来源:                         Facebook Hacker Cup选拔    ...

2967
来自专栏WD学习记录

牛客网 合唱团

有 n 个学生站成一排,每个学生有一个能力值,牛牛想从这 n 个学生中按照顺序选取 k 名学生,要求相邻两个学生的位置编号的差不超过 d,使得这 k 个学生的能...

1772
来自专栏皮皮之路

【JDK1.8】Java 8源码阅读汇总

3947
来自专栏用户2442861的专栏

C++ 智能指针详解

http://blog.csdn.net/xt_xiaotian/article/details/5714477

6441
来自专栏Albert陈凯

Scala代码编写中常见的十大陷阱

很多Java开发者在学习Scala语言的时候,往往觉得Scala的语法和用法有些过于复杂,充满语法糖,太“甜”了。在使用Scala编写代码时,由于语法和编写习惯...

2555
来自专栏企鹅号快讯

30分钟学会用Python编写简单程序

参与文末每日话题讨论,赠送异步新书 异步图书君 学习目标 知道有序的软件开发过程的步骤。 了解遵循输入、处理、输出(IPO)模式的程序,并能够以简单的方式修改它...

66810
来自专栏一枝花算不算浪漫

一道简单的HashMap面试题所想到的...

原以为自己对HashMap的源码理解的还算可以了,应该足够应付面试了。但是看到这个问题自己确实也是懵逼了一下。 查了下资料,答案是JDK1.7是插入到首部,...

1153

扫码关注云+社区

领取腾讯云代金券