python实现douban.fm简易客户端

一个月前心血来潮用python实现了一个简单的douban.fm客户端,计划是陆续将其完善成为ubuntu下可替代web版本的douban.fm客户端。但后来因为事多,被一直搁着,没有再继续完善。就在昨天,一位园友在评论中提到了登录的实现,虽然最近依然事多,但突然很想实现这个功能。正好,前几天因为一些需要,曾用python实现过网站登录,约摸估计这douban.fm的登录不会差太多。

关于网站身份验证

http协议被设计为无连接协议,但现实中,很多网站需要对用户进行身份识别,cookie就是为此而诞生的。当我们用浏览器浏览网站时,浏览器会帮我们透明的处理cookie。而我们现在要第三方登录网站,这就必须对cookie的工作流程有一定的了解。

另外,很多网站为了防止程序自动登录而使用了验证码机制,验证码的介入会使登录过程变得麻烦,但也还不算太难处理。

实际中douban.fm的登录流程

为了模拟一个干净(不使用已有cookie)的登录流程,我使用chromium的隐身模式。

1.jpg

立即学习“Python免费学习笔记(深入)”;

观察请求和响应头,可以看到,第一次请求的请求头是没有Cookie字段的,而服务器的响应头中包含着Set-Cookie字段,这告诉浏览器下次请求该网站时需要携带Cookie。

这里我注意到了一个有意思的现象,访问douban.fm,实际中经过了3次重定向。当然,一般来说我们并不需要关注这些细节,浏览器和高级的httplib会透明的处理重定向,但如果使用底层的C Socket,就必须小心的处理这些重定向。

点击登录按钮,浏览器发起几个新的请求,其中有几个至关重要的请求,这几个请求是我们第三方登录douban.fm的关键所在。

首先,有一条请求的URL是http://douban.fm/j/new_captcha,请求该URL,服务器会返回一个随机字符串,这有什么用呢?(其实是个验证码)

再看下一条请求,http://douban.fm/misc/captcha?size=m&id=0iPlm837LsnSsJTMJrf5TZ7e,这条请求会返回验证码。原来如此,请求http://douban.fm/j/new_captcha,将服务器返回的字符串作为下一条请求的id参数值。

我们可以写一段python代码来验证我们的想法。

值得注意的是python提供了3个http库,httplib、urllib和urllib2,能透明处理cookie的是urllib2,想我之前用httplib手动处理cookie,那个痛苦啊。

代码如下:

opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(CookieJar()))captcha_id = opener.open(urllib2.Request('http://douban.fm/j/new_captcha')).read().strip('"')captcha = opener.open(urllib2.Request('http://douban.fm/misc/captcha?size=m&id=' + captcha_id)).read())file = open('captcha.jpg', 'wb')file = write(captcha)file.close()

登录后复制

这段代码实现了验证码的下载。

接着,我们填写表单,并提交。

可以看到,登录表单的目标地址为http://douban.fm/j/login,参数有:

source: radio

alias: 用户名

form_password: 密码

captcha_solution: 验证码

captcha_id: 验证码ID

task: sync_channel_list

接下来要做的是用python构造一个表单。

opener.open(    urllib2.Request('http://douban.fm/j/login'),    urllib.urlencode({        'source': 'radio',        'alias': username,        'form_password': password,        'captcha_solution': captcha,        'captcha_id': captcha_id,        'task': 'sync_channel_list'}))

登录后复制

服务器返回的数据格式是json,具体格式这里不赘诉了,大家可以自己测试。

我们怎么知道登录是否起作用了呢?是了,之前的文章提到过channel=-3为红心兆赫,是用户的收藏列表,没有登录是获取不到该频道的播放列表的。请求http://douban.fm/j/mine/playlist?type=n&channel=-3,如果返回你自己收藏过的音乐列表,那么就说明登录起作用了。

代码整理

结合之前的版本和新增的登录功能,再加上命令行参数处理、频道选择,一个稍稍完善的douban.fm就完成的

View Code #!/usr/bin/python # coding: utf-8    import sys import os import subprocess import getopt import time import json import urllib import urllib2 import getpass import ConfigParser from cookielib import CookieJar    # 保存到文件 def save(filename, content):     file = open(filename, 'wb')     file.write(content)     file.close()       # 获取播放列表 def getPlayList(channel='0', opener=None):     url = 'http://douban.fm/j/mine/playlist?type=n&channel=' + channel     if opener == None:         return json.loads(urllib.urlopen(url).read())     else:         return json.loads(opener.open(urllib2.Request(url)).read())       # 发送桌面通知 def notifySend(picture, title, content):     subprocess.call([         'notify-send',         '-i',         os.getcwd() + '/' + picture,         title,         content])       # 登录douban.fm def login(username, password):     opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(CookieJar()))     while True:         print '正在获取验证码……'         captcha_id = opener.open(urllib2.Request(             'http://douban.fm/j/new_captcha')).read().strip('"')         save(             '验证码.jpg',             opener.open(urllib2.Request(                 'http://douban.fm/misc/captcha?size=m&id=' + captcha_id             )).read())         captcha = raw_input('验证码: ')         print '正在登录……'         response = json.loads(opener.open(             urllib2.Request('http://douban.fm/j/login'),             urllib.urlencode({                 'source': 'radio',                 'alias': username,                 'form_password': password,                 'captcha_solution': captcha,                 'captcha_id': captcha_id,                 'task': 'sync_channel_list'})).read())         if 'err_msg' in response.keys():             print response['err_msg']         else:             print '登录成功'             return opener       # 播放douban.fm def play(channel='0', opener=None):     while True:         if opener == None:             playlist = getPlayList(channel)         else:             playlist = getPlayList(channel, opener)                    if playlist['song'] == []:             print '获取播放列表失败'             break                 picture,            for song in playlist['song']:             picture = 'picture/' + song['picture'].split('/')[-1]                # 下载专辑封面             save(                 picture,                 urllib.urlopen(song['picture']).read())                # 发送桌面通知             notifySend(                 picture,                 song['title'],                 song['artist'] + '' + song['albumtitle'])                # 播放             player = subprocess.Popen(['mplayer', song['url']])             time.sleep(song['length'])             player.kill()       def main(argv):     # 默认参数     channel = '0'     user = ''     password = ''        # 获取、解析命令行参数     try:          opts, args = getopt.getopt(             argv, 'u:p:c:', ['user=', 'password=', 'channel='])      except getopt.GetoptError as error:         print str(error)         sys.exit(1)        # 命令行参数处理     for opt, arg in opts:         if opt in ('-u', '--user='):             user = arg         elif opt in ('-p', '--password='):             password = arg         elif opt in ('-c', '--channel='):             channel = arg        if user == '':         play(channel)     else:         if password == '':             password = getpass.getpass('密码:')         opener = login(user, password)         play(channel, opener)       if __name__ == '__main__':     main(sys.argv[1:])

登录后复制

1.png

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至253000106@qq.com举报,一经查实,本站将立刻删除。

发布者:PHP中文网,转转请注明出处:https://www.chuangxiangniao.com/p/2284131.html

(0)
上一篇 2025年2月27日 20:36:26
下一篇 2025年2月25日 00:04:59

AD推荐 黄金广告位招租... 更多推荐

相关推荐

  • 用70行代码实现日志分析程序​

    python又一力作,感受python的强大。用70行代码实现日志分析程序 功能介绍:可直接对文本日至进行分组和排序功能,完了输出结果粘贴到excel里就可以直接生成图表,对于排查一些生产环境问题有很大的作用。 代码: #encoding=…

    编程技术 2025年2月27日
    200
  • 关于python如何实现各进制转换的总结大全

    这篇文章主要给大家总结了python实现各进制转换的相关资料,其中包括字符串与十六进制转换、内置函数hex()与进制互转等相关内容,需要的朋友可以参考借鉴,下面来一起看看吧。 前言 玩ctf经常遇到进制转换的问题,就正好做一个进制转换总结,…

    编程技术 2025年2月27日
    200
  • python如何实现xml与数据库读取转换的示例代码分享

    这篇文章主要给大家介绍了关于利用python实现xml与数据库读取转换的方法,文中通过示例代码介绍的非常详细,对大家具有一定的参考学习价值,需要的朋友们下面来一起看看吧。 前言 xml课的第三第四个作业都是用java编程来实现xml dom…

    编程技术 2025年2月27日
    200
  • 如何使用python来实现个性化词云的示例代码分享

    最近看到可视化的词云,看到网上也很多这样的工具,但是都不怎么完美,有些不支持中文,有的中文词频统计得莫名其妙、有的不支持自定义形状、所有的都不能自定义颜色,于是网上找了一下,决定用python绘制词云 先上图片 词云图 需要模板 pip i…

    2025年2月27日
    200
  • 教你怎么使用XML库实现RPC通信的功能

    1、先说结论:使用xml-rpc的机制可以很方便的实现服务器间的rpc调用。 2、试验结果如下:   3、源码如下: 服务器端的源代码如下: import operator, mathfrom SimpleXMLRPCServer impo…

    2025年2月27日
    200
  • python3实现TCP协议的简单服务器和客户端

    利用python3来实现tcp协议,和udp类似。udp应用于及时通信,而tcp协议用来传送文件、命令等操作,因为这些数据不允许丢失,否则会造成文件错误或命令混乱。下面代码就是模拟客户端通过命令行操作服务器。客户端输入命令,服务器执行并且返…

    编程技术 2025年2月27日
    200
  • python http长连接客户端实例教程

    背景: 线上机器,需要过滤access日志,发送给另外一个api期初是单进程,效率太低,改为多进程发送后,查看日志中偶尔会出现异常错误(忘记截图了。。。)总之就是端口不够用了报错 原因: 每一条日志都是一次请求发送给api,短连接产生大量t…

    编程技术 2025年2月27日
    200
  • Python中选择排序的实例详解

    选择排序:   选择排序(selection sort)是一种简单直观的排序算法。它的工作原理如下。首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置,然后,再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。…

    编程技术 2025年2月27日
    200
  • 分享Python实现选择排序的实例教程

    选择法也算是入门的一种排序算法,比起冒泡法,它的方法巧妙了一些,它的出发点在于“挑”,每次挑选数组的最值,与前置元素换位,然后继续挑选剩余元素的最值并重复操作。个人认为选择排序的意义不在于排序本身,而在于挑选和置换的方法,对于一些问题很有帮…

    编程技术 2025年2月27日
    200
  • 分享Python如何实现头像拼接技术/

    微信好友全头像 话不多说,直接上代码 import itchatimport mathimport PIL.Image as Imageimport ositchat.auto_login()friends = itchat.get_fri…

    2025年2月27日
    200

发表回复

登录后才能评论