前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >通俗易懂,从函数 def 到类 Class

通俗易懂,从函数 def 到类 Class

作者头像
周萝卜
发布2019-07-17 15:36:33
2K0
发布2019-07-17 15:36:33
举报
文章被收录于专栏:萝卜大杂烩

摘要:初学 Python 过程中,我们可能习惯了使用函数(def),在开始学习类(Class)的用法时,可能会觉得它的写法别扭,类的代码写法也不像函数那么简单直接,也会产生「有了函数为什么还需要类」的疑问。然而面向对象编程是 Python 最重要的思想,类(Class)又是面向对象最重要的概念之一,所以要想精通 Python ,则必须得会使用类(Class)来编写代码,而且 Pyspider 和 Scrapy 两大框架都使用了类的写法,基于此,本文将介绍如何从函数的写法顺利过渡到类的编写习惯。

关于类(Class)的教程,网上主要有两类,一类是廖雪峰大佬的,另一类是不加说明默认你已经会这种写法,而直接使用的。

廖雪峰的教程非常棒,但是更适合入门 Python 有一段时间或者看过一些更基础的教程之后再回过头来看,否则可能会觉得他的教程理论性重于实用性。我第一次看了他的教程中关于类的相关知识后,觉得理解了,但一尝试自己写时就不太会了。

第二类教程,网上有很多案例,这类教程存在的问题就是,你能看懂意思,但还是不太会运用到自己的代码中。

总结一下这两类教程对新手不友好的地方就是 没有同时给出两种写法的实例对比,而「对比学习」是一种学习新知识非常快的途径,简单来说就是学新知识的时候,先从我们已经掌握的知识出发,和新知识进行对比,快速找到共同点和不同点,共同点我们能很快掌握,针对不同点通过对比去感悟领会,从而快速学会新知识。

接下来,就举几个同时使用了函数写法和类的写法的案例,希望能够帮助你快速完成从函数到类的编程思想的过渡转换。

▌爬取豆瓣电影 TOP250

第一个案例是爬取豆瓣电影 TOP250,我们实现的目标就是通过调用豆瓣 API 接口,获取电影名称、评分、演员等信息,然后存储到 CSV 文件中,部分代码如下:

代码语言:javascript
复制
 1def get_content(start_page):
 2    url = 'https://api.douban.com/v2/movie/top250?'
 3    params = {
 4        'start':start_page,
 5        'count':50
 6        }
 7    response = requests.get(url,params=params).json()
 8    movies = response['subjects']
 9    data = [{
10        'rating':item['rating']['average'],
11        'genres':item['genres'],
12        'name':item['title'],
13        'actor':get_actor(item['casts']),
14        'original_title':item['original_title'],
15        'year':item['year'],
16    } for item in movies]
17    write_to_file(data)
18
19def get_actor(actors):
20    actor = [i['name'] for i in actors]
21    return actor
22
23def write_to_file(data):
24    with open('douban_def.csv','a',encoding='utf_8_sig',newline='') as f:
25        w = csv.writer(f)
26        for item in data:
27            w.writerow(item.values())
28
29def get_douban(total_movie):
30    # 每页50条,start_page循环5次
31    for start_page in range(0,total_movie,50):
32        get_content(start_page)
33if __name__ == '__main__':
34    get_douban(250)

打开 CSV 文件查看输出的结果:

以上,我们通过四个函数就完成了数据的爬取和存储,逻辑很清晰,下面我们使用类的写法实现同样的功能,部分代码如下:

代码语言:javascript
复制
 1class Douban(object):
 2    def __init__(self):
 3        self.url = 'https://api.douban.com/v2/movie/top250?'
 4    def get_content(self,start_page):
 5        params = {
 6            'start':start_page,
 7            'count':50
 8            }
 9        response = requests.get(self.url,params=params).json()
10        movies = response['subjects']
11        data = [{
12            'rating':item['rating']['average'],
13            'genres':item['genres'],
14            'name':item['title'],
15            'actor':self.get_actor(item['casts']),
16            'original_title':item['original_title'],
17            'year':item['year'],
18        } for item in movies]
19        self.write_to_file(data)
20
21    def get_actor(self,actors):
22        actor = [i['name'] for i in actors]
23        return actor
24
25    def write_to_file(self,data):
26        with open('douban_class.csv','a',encoding='utf_8_sig',newline='') as f:
27            w = csv.writer(f)
28            for item in data:
29                w.writerow(item.values())
30    def get_douban(self,total_movie):
31        # 每页50条,start_page循环5次
32        for start_page in range(0,total_movie,50):
33            self.get_content(start_page)
34
35if __name__ == '__main__':
36    douban = Douban()
37    douban.get_douban(250)

可以看到,上面的案例中,类的写法和函数的写法大部分都是一样的,仅少数部分存在差异。主要有这么几点差异:

  • 增加了一个__init__函数。 这是一个特殊的函数,它的作用主要是事先把一些重要的属性填写进来,它的特点是第一个参数永远是self,表示创建的实例本身,这里的实例就是最下面的 douban(实例通过类名+() 创建)。
  • 类中的函数和普通的函数相比,只有一点不同。 类中的函数(也称为方法)的第一个参数永远是实例变量self,并且调用时,不用传递该参数。除此之外,类的方法和普通函数没有什么区别。
  • 在执行类的时候需要先实例化 这里我们定义了一个类,类名是 Douban(首字母要大写),在运行类的时候,需要先实例化,这里实例化为douban,然后调用 get_douban()方法完成数据的爬取和存储。

下面,我们再来看看第二个例子。

▌模拟登陆 IT 桔子

我们的目标是使用 Selenium 模拟登陆 IT 桔子网并输出网页源码,函数写法的部分代码如下:

代码语言:javascript
复制
 1def login():
 2    browser = webdriver.Chrome()
 3    browser.get('https://www.itjuzi.com/user/login')
 4    account = browser.find_element(By.ID, "create_account_email")
 5    password = browser.find_element(By.ID, "create_account_password")
 6    account.send_keys('irw27812@awsoo.com') # 输入账号和密码
 7    password.send_keys('test2018') # 输入账号密码
 8    submit = browser.find_element(By.ID,"login_btn")
 9    submit.click() # 点击登录按钮
10    get_content()
11
12def get_content():
13    browser.get('http://radar.itjuzi.com/investevent')
14    print(browser.page_source)  # 输出网页源码
15
16if __name__ == '__main__':
17    login()

以上代码实现了自动输入账号密码然后进入 IT 桔子网,下面我们再来看看类的写法:

代码语言:javascript
复制
 1class Spider(object):
 2    def __init__(self,account,password):
 3        self.login_url = 'https://www.itjuzi.com/user/login'
 4        self.get_url = 'http://radar.itjuzi.com/investevent'
 5        self.account = account
 6        self.password = password
 7
 8    def login(self):
 9        browser.get(self.login_url)
10        account = browser.find_element(By.ID, "create_account_email")
11        password = browser.find_element(By.ID, "create_account_password")
12        account.send_keys(self.account) # 输入账号和密码
13        password.send_keys(self.password)
14        submit = browser.find_element(By.ID,"login_btn")
15        submit.click() # 点击登录按钮
16        self.get_content()   # 调用下面的方法
17
18    def get_content(self):
19        browser.get(self.get_url)
20        print(browser.page_source)  # 输出网页源码
21
22if __name__ == '__main__':
23    spider = Spider(account='irw27812@awsoo.com',password='test2018')
24    # 当有其他账号时,在这里更改即可,很方便
25    # spider = Spider('fru68354@nbzmr.com','test2018')
26    spider.login()

这里,我们将一些固定的参数,比如 URL、Headers 都放在 __init__ 方法中,需要的时候在各个函数中进行调用,这样的写法逻辑更加清晰。

下面,我们再看看第三个例子,从普通类的写法过渡到 pyspider 框架中类的写法,这样有助于快速上手 pyspider 框架。

▌爬取虎嗅文章

我们目标是通过分析 AJAX 请求,遍历爬取虎嗅网的文章信息,先来看看普通类的写法,部分代码如下:

代码语言:javascript
复制
 1client = pymongo.MongoClient('localhost',27017)
 2db = client.Huxiu
 3mongo_collection = db.huxiu_news
 4
 5class Huxiu(object):
 6    def __init__(self):
 7        self.headers = {
 8            'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.181 Safari/537.36',
 9            'X-Requested-With': 'XMLHttpRequest'
10            }
11        self.url = 'https://www.huxiu.com/v2_action/article_list'
12
13    def get_content(self,page):
14        data = {
15            'page': page,
16            }
17        response = requests.post(self.url,data=data,headers=self.headers)
18        self.parse_content(response)
19
20    def parse_content(self,response):
21        content = response.json()['data']
22        doc = pq(content)
23        lis = doc('.mod-art').items()
24        data =[{
25        'title': item('.msubstr-row2').text(),
26        'url':'https://www.huxiu.com'+ str(item('.msubstr-row2').attr('href')),
27        'name': item('.author-name').text(),
28        'write_time':item('.time').text(),
29        'comment':item('.icon-cmt+ em').text(),
30        'favorites':item('.icon-fvr+ em').text(),
31        'abstract':item('.mob-sub').text()
32        } for item in lis] 
33        self.save_to_file(data)
34    # 存储到 mongodb
35    def save_to_file(self,data):
36        df = pd.DataFrame(data)
37        content = json.loads(df.T.to_json()).values()
38        if mongo_collection.insert_many(content):
39            print('存储到 mongondb 成功')
40        else:
41            print('存储失败')
42
43    def get_huxiu(self,start_page,end_page):
44        for page in range(start_page,end_page) :
45            print('正在爬取第 %s 页' % page)
46            self.get_content(page)
47
48if __name__ == '__main__':
49    huxiu = Huxiu()
50    huxiu.get_huxiu(1,2000)

然后再看看在 Pyspider 中的写法:

代码语言:javascript
复制
 1class Handler(BaseHandler):
 2    crawl_config:{
 3        "headers":{
 4            'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.181 Safari/537.36',
 5            'X-Requested-With': 'XMLHttpRequest'
 6            }
 7    }
 8
 9    # 修改taskid,避免只下载一个post请求
10    def get_taskid(self,task):
11        return md5string(task['url']+json.dumps(task['fetch'].get('data','')))
12
13    def on_start(self):
14        for page in range(1,2000):
15            print('正在爬取第 %s 页' % page)
16            self.crawl('https://www.huxiu.com/v2_action/article_list',method='POST',data={'page':page}, callback=self.index_page)
17
18    def index_page(self, response):
19        content = response.json['data']
20        # 注意,在上面的class写法中,json后面需要添加(),pyspider中则不用
21        doc = pq(content)
22        lis = doc('.mod-art').items()
23        data = [{
24            'title': item('.msubstr-row2').text(),
25            'url':'https://www.huxiu.com'+ str(item('.msubstr-row2').attr('href')),
26            'name': item('.author-name').text(),
27            'write_time':item('.time').text(),
28            'comment':item('.icon-cmt+ em').text(),
29            'favorites':item('.icon-fvr+ em').text(),
30            'abstract':item('.mob-sub').text()
31            } for item in lis ] 
32        return data
33
34    def on_result(self,result):
35        if result:
36            self.save_to_mongo(result)
37
38    def save_to_mongo(self,result):
39        df = pd.DataFrame(result)
40        content = json.loads(df.T.to_json()).values()
41        if mongo_collection.insert_many(content):
42            print('存储到 mongondb 成功')

可以看到,pyspider 中主体部分和普通类的写法差不多,不同的地方在于 pyspider 中有一些固定的语法,这可以通过参考 pyspider 教程快速掌握。

通过以上三个例子的对比,我们可以感受到函数(def)、 类(Class)和 pyspider 三种代码写法的异同点,采取这样对比式的学习能够快速掌握新的知识。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2019-01-08,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 萝卜大杂烩 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • ▌爬取豆瓣电影 TOP250
  • ▌模拟登陆 IT 桔子
  • ▌爬取虎嗅文章
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档