Python 最近因开发项目的需要,有一个需求,就是很多SNS网站都有的通过 Email地址 导入好友列表,不过这次要导入的不是Email 列表,而是QQ的好友列表。 实现方式: 通过goog

news/2024/5/9 2:17:41/文章来源:https://blog.csdn.net/wangzhaopeng0316/article/details/8271113
Python

最近因开发项目的需要,有一个需求,就是很多SNS网站都有的通过 Email地址 导入好友列表,不过这次要导入的不是Email 列表,而是QQ的好友列表。

实现方式:

通过google一搜,实现的方式大概有下面这篇文章提到的几种方法:

http://www.cnblogs.com/hblhs/archive/2008/07/30/1256597.html

 

最后我选择了通过模拟登录QQ邮箱的方式来实现,该实现方式在海内网上的好友查找功能也可以看到。

QQ邮箱的官方登陆地址是http://mail.qq.com/

与其他大部分邮箱不同的是,如果使用纯数字的QQ号登录的话,除了密码,还需要输入验证码。

看到海内上的QQ好友导入功能也是需要输入验证码的,而且验证码的样子和QQ邮箱的很像。由于这是需要在用户手动输入密码的情况下才能实现的功能,因此输入验证码的工作也可以让用户手动来完成。

 

验证码处理:

通过对  http://mail.qq.com/ 页面的分析,  QQ邮箱的验证码方式实现原理其实是很简单,当需要一张验证码图片或看不清而需要换一张时,它都是向地址 http://ptlogin2.qq.com/getimage?aid=23000101 发出请求,(页面上该地址是通过js生成的,为了防止浏览器缓存,地址末尾还会带有随机一个随机数),而该链接不但返回一张图片,还在http头部带有设置cookie的一段header。这样当用户提交表单的时候,浏览器就会把该cookie发送回服务器,服务器通过比较 该cookie值和经过某种运算后的表单中的验证码值 就可以判断验证码是否填写正确。

 

现在的问题是由于cookie的安全机制,验证码图片不能直接从腾讯的服务器上去取,那样用户在将QQ和密码发送到我们的服务器时,验证码的cookie不会一起发过来。

 

解决方式其实也很简单,将验证码的获取地址改为我们自己的服务器,我们的服务器作为简单的代理,从腾讯的服务器上去获取真正的验证码,再将图片内容和那段cookie发送回用户浏览器。那样用户提交表单的时候,那段cookie就又会发送回我们的服务器了。

 

绕过其他验证安全机制:

一般上有了账号,密码,验证码这3样东西就可以实现模拟登录很多网站了,但是QQ邮箱还有其他的安全机制,在QQ邮箱登陆的表单中还有一个像这样 <input type="hidden" name="ts" value="1234672721" />  的 hidden 域,该value每次刷新页面都会改变,同时在表单提交的时候,还会通过js将该值与其他hidden 域的值进行某些计算才正式提交表单。

 

通过多次模拟登录,估计该值是用来判断登录session超时的,同时也参与其他的一些干扰加密的计算。而且该值与验证码是完全无关的,因此在显示我们表单时,只要先去抓取一下  http://mail.qq.com/  页面,从里面提取出ts 值, 连同其他所有 hidden 域 和相关计算的js代码放入我们的表单中就可以了。

 

因此,实际上我们的表单只需要稍微修改一下   http://mail.qq.com/  页面的内容就可以作为显示给用户的表单。主要包括以下几个方面,这里我使用的django,所以使用django的模板语法:

1、<input type="hidden" name="ts" value="1234672721" />    改为  <input type="hidden" name="ts" value="{{ts}}" />

2、表单的action地址改为我们自己这里假设为 /friends/    因此

<form name="form1" method="post" action="http://m11.mail.qq.com/cgi-bin/login?sid=0,2,zh_CN" onSubmit="return checkInput();" >

改为

<form name="form1" method="post" action="/friends/" onSubmit="return checkInput();">

 

3、图片验证码地址,有两个地方要改:

document.write("<img id='vfcode' src='http://ptlogin2.qq.com/getimage?aid=23000101&",Math.random(),"' style='cursor:pointer;border:1px solid #e4eef9' οnclick='changeimg()'>");

改为

document.write("<img id='vfcode' src='/qq-captcha/?aid=23000101&", Math.random(), "' style='cursor:pointer;border:1px solid #e4eef9' οnclick='changeimg()'>");

 

另外一个changeimg 函数内, 也将相应的地址改为我们自己的服务器即可。

 

改了这些,页面看上去和原来几乎一样,只是所有交互都改到了我们的服务器上,出于版权和页面统一的需要,在使用到自己的网站上时,可以使用自己设计的页面,只要表单的初始化和提交与原来一样就可以了,甚至也可以通过阅读js部分的源代码,把ts部分的计算移到服务器端进行。

 

示例代码:

以下是整个views.py的代码,包括后面会讲到模拟登录部分,login和qq_captcha分别用来初始化登陆页和获取图片验证码:

Python代码   收藏代码
  1. # Create your views here.  
  2. from django.shortcuts import render_to_response  
  3. from urllib2 import Request, urlopen, build_opener, HTTPCookieProcessor  
  4. from urllib import urlencode  
  5. from cookielib import CookieJar  
  6. from django.http import HttpResponse  
  7. import re  
  8. from xml.sax.saxutils import unescape  
  9. from BeautifulSoup import BeautifulSoup  
  10. server_no = 'm11'  
  11. login_error_re = re.compile('"errtype=(\d)"')  
  12. login_succ_re = re.compile('"frame_html\?sid=(.+?)"')  
  13. hacked_friendlist_page_re = re.compile(r'\<ul\s+class="grouplist"\>.+?\</ul\>', re.DOTALL)  
  14. body_re = re.compile(r'\<body\sclass="tbody"\sid="list"\>.+?\</body\>', re.DOTALL)  
  15.   
  16. def login(request):  
  17.     url = 'http://mail.qq.com/'  
  18.     re_obj = re.compile(r'name="ts"\svalue="(\d+)"')  
  19.     match_obj = re_obj.search(urlopen(url).read())  
  20.     ts = match_obj.group(1)  
  21.     return render_to_response('login.html', locals())  
  22.   
  23. def qq_captcha(request):  
  24.     url = 'http://ptlogin2.qq.com/getimage?aid=%s' % request.GET['aid']  
  25.     f = urlopen(url)  
  26.     r = HttpResponse(f.read(), mimetype = f.info()['Content-Type'], )  
  27.     r['Pragma'] = 'no-cache'  
  28.     r.set_cookie('verifysession', f.info()['Set-Cookie'].split(';')[0].split('=')[1].strip())  
  29.     return r  
  30.   
  31. def qq_friends(request):  
  32.     for k in request.POST:  
  33.         print '%s : %s' % (k, request.POST[k])  
  34.     verifysession = request.COOKIES['verifysession']  
  35.     print verifysession  
  36.     headers = {'Cookie':'''''verifysession=%s''' % verifysession,  
  37.                'Content-Type':'application/x-www-form-urlencoded',  
  38.                'Referer':'http://mail.qq.com/',  
  39.                'User-Agent':'Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN; rv:1.9.0.1) Gecko/2008070208 Firefox/3.0.1',  
  40.                }  
  41.     data = urlencode(request.POST)  
  42.     login_request = Request('http://%s.mail.qq.com/cgi-bin/login?sid=0,2,zh_CN' % server_no, data, headers)  
  43.     result = urlopen(login_request)  
  44.     content = result.read()  
  45.     login_error = login_error_re.search(content)  
  46.     if login_error:  
  47.         error_no = login_error.group(1#1:password wrong 2: captcha wrong  
  48.         if error_no == '1':  
  49.             error_msg = 'password or qq wrong'  
  50.         elif error_no == '2':  
  51.             error_msg = 'captcha wrong'  
  52.         return render_to_response('friends.html', locals())  
  53.     sid = login_succ_re.search(content).group(1)  
  54.           
  55.     friends_list_headers = {'Referer':'http://mail.qq.com/',  
  56.                            'User-Agent':'Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN; rv:1.9.0.1) Gecko/2008070208 Firefox/3.0.1',  
  57.                            }  
  58.     friends_list_request = Request('http://%s.mail.qq.com/cgi-bin/addr_listall?sid=%s&sorttype=null&category=common' % (server_no, sid), headers = friends_list_headers)  
  59.     cj = CookieJar()  
  60.     cj.extract_cookies(result, friends_list_request)  
  61.     opener = build_opener(HTTPCookieProcessor(cj))  
  62.     result = opener.open(friends_list_request)  
  63.     grouplist = hacked_friendlist_page_re.search(result.read().decode('gb2312''ignore')).group(0)  
  64.     soup = BeautifulSoup(grouplist, fromEncoding = 'utf-8')  
  65.     grouplist = soup.findAll('li')  
  66.     friend_list = {}  
  67.     for group in grouplist:  
  68.         friend_list[group.a.string] = []  
  69.         list_request = Request('http://%s.mail.qq.com%s' % (server_no, group.a['href']), headers = friends_list_headers)  
  70.         result = opener.open(list_request)  
  71.         body = BeautifulSoup(body_re.search(result.read().decode('gb2312''ignore')).group(0), fromEncoding = 'utf-8')  
  72.         friends = body.findAll('div', attrs={'class':'M'})  
  73.         for friend in friends:  
  74.             friend_name = unescape(friend.p.span.contents[1].replace('&nbsp;'''1))  
  75.             friend_email = friend.p.img['addr']  
  76.             friend_list[group.a.string].append((friend_name, friend_email))  
  77.       
  78.     return render_to_response('friends.html', locals())  

 

模板 friends.html 的代码:

Html代码   收藏代码
  1. <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">  
  2. <html>  
  3. <head>  
  4. <title>好友列表</title>  
  5. <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />  
  6. </head>  
  7. <body>  
  8. {%if error_msg%}  
  9. {{error_msg}}  
  10. {%endif%}  
  11.   
  12. {%for group, friends in friend_list.items %}  
  13. <ul>  
  14. {{group}}:  
  15.     {%for name, email in friends%}   
  16.     <li>{{name|safe}} {{email}}</li>  
  17.     {%endfor%}  
  18. </ul>  
  19. {%endfor%}  
  20. </body>  
  21. </html>  

 

模拟登录部分都在view qq_friends 里面进行,基本上就像前面说得,获取图片验证码的cookie,将表单提交的值原样发送post到腾讯的服务器,如果都正确的话就可以登录了。

发送的结果大概分为3种,验证码错误,账号或密码错误,登陆成功。不管在哪种情况下,腾讯的服务器都是返回一段js代码,通过那段代码重定向到错误页或邮箱登陆后的首页。

其中验证码错误像是下面这样:

<script> var urlHead="http://m11.mail.qq.com/cgi-bin/"; var targetUrl=""; var mailto=""; targetUrl = urlHead + "loginpage?" +"errtype=2" +"&verify=true" +"&clientuin=12345678" +"&t=" +"&alias=" +"&regalias=" +"&delegate_url=" +"&title=" +"&url=%2Fcgi-bin%2Flogin%3Fsid%3D0%2C2%2Czh_CN" +"&org_fun=" +"&aliastype=@qq.com" +"&ss=" +"&from=" +"&autologin=n" if (targetUrl == "") { targetUrl = ""; } document.write("<META http-equiv=Refresh content=\'0; url=\"" + targetUrl + "\"\'/>"); </script> <script type="text/javascript"> location.href=targetUrl </script>

 

账号或密码错误是这样:

<script> var urlHead="http://m11.mail.qq.com/cgi-bin/"; var targetUrl=""; var mailto=""; targetUrl = urlHead + "loginpage?" +"errtype=1" +"&verify=false" +"&clientuin=172564012" +"&t=" +"&alias=" +"&regalias=" +"&delegate_url=" +"&title=" +"&url=%2Fcgi-bin%2Flogin%3Fsid%3D0%2C2%2Czh_CN" +"&org_fun=" +"&aliastype=@qq.com" +"&ss=" +"&from=" +"&autologin=n" if (targetUrl == "") { targetUrl = ""; } document.write("<META http-equiv=Refresh content=\'0; url=\"" + targetUrl + "\"\'/>"); </script> <script type="text/javascript"> location.href=targetUrl </script>

 

登录成功是这样:

<script> var urlHead="http://m11.mail.qq.com/cgi-bin/"; var targetUrl=""; var mailto=""; targetUrl = urlHead + "frame_html?sid=UJh1e2XMWhOEbWcu"; if (targetUrl == "") { targetUrl = ""; } document.write("<META http-equiv=Refresh content=\'0; url=\"" + targetUrl + "\"\'/>"); </script> <script type="text/javascript"> location.href=targetUrl </script>

 

其中登录成功后的反馈不但是上面的js代码,还包括一大堆cookie,与邮箱服务器的后续交互都需要用到这些cookie。

另外上面js代码中frame_html?sid=UJh1e2XMWhOEbWcu的sid部分需要多次用到在后面抓取好友列表页面的url中,所以也需要提取出来。

 

登录邮箱后,就可以抓取好友列表了,可以通过先访问页面

http://m11.mail.qq.com/cgi-bin/addr_listall?sid=UJh1e2XMWhOEbWcu&sorttype=null&category=common

该页面的<ul class="grouplist"></ul>部分可以获取好友分组名称和相应的url地址。

然后在相应的访问各个好友分组的页面的就可以获取好友列表了。

 

这里由于我自己的QQ号限制,不知道如果没有好友没有分组页面会是什么情况,以及如果某个分组的好友很多的话,该好友列表页面是否会有分页(目前是没有见到分页)。有什么错误的话可以留言给我。

 

 

其他说明:

1、由于有些QQ用户给自己取火星文昵称,导致抓取的页面上很容易出现字符编码错误及HTML转义问题。

2、不知道是故意为了防止抓取还是设计不规范,好多页面用BeautifulSoup直接实例化,会导致大量信息的丢失,所以上面的代码都是尽可能先用正则表达式提取必须部分的信息。

3、由于QQ邮箱服务器的代码也在升级(事实上,我前几天刚写的代码就刚失效了,马上做了一些修改,昨天海内上的那个功能也出现了问题,看来他们也是通过QQ邮箱方式来实现的),所以并不能保证你在看到本文章时,上面的代码依然使用有效。但是总体上的思路依然可以提供参考。

 

 

Demo地址:

http://www.playdjango.cn/qq-friends/

由于要输入自己的QQ密码,如果你觉得不可信的话还是别试了,你也可以把上面的代码整理一下弄到自己的机器上进行测试也一样。

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.luyixian.cn/news_show_877691.aspx

如若内容造成侵权/违法违规/事实不符,请联系dt猫网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

wordpress搜索引擎蜘蛛统计插件SEO

索引擎蜘蛛统计插件可以实时对搜索引擎来访进行统计&#xff0c;指导站长的运营&#xff0c;来访的蜘蛛类型和访问的页面都能在后台一目了然&#xff01; 免费使用下载https://yomeiyo.lanzouw.com/ifh5300s4p7a安装即可 插件安装方法 上传压缩包安装插件 如果是付费插件的话…

用Django建一个网站

目标&#xff1a;用最短的步骤在本机创建一个网站。 第一步 安装好Django之后&#xff0c;在放置网页的目录下打开命令行&#xff0c;输入 django-admin startproject mysite 即可自动创建一个名为mysite的文件夹。 进入mysite文件夹&#xff0c;创建第一个web程序app01(任意):…

提升大型网站并发访问性能

应用服务器 网络应用 中国电信 Linux配置管理 一个小型的网站&#xff0c;可以使用最简单的html静态页面就实现了&#xff0c;配合一些图片达到美化效果&#xff0c;所有的页面均存放在一个目录下&#xff0c;这样的网站对系统架构、性能的要求都很简单。随着互联网业务的不断丰…

requests 正则表达正式基础爬虫案例二:爬取网站数据

要求&#xff1a;爬取https://ssr1.scrape.center/ 网站中所有电影标题、类型、地区、电影时长、上映日期、评分、简介&#xff1b; 分析&#xff1a;网站共有两个页面组成&#xff0c;电影列表和详情&#xff0c;我们所需要的内容都在详情页面里面可以找到。 列表页面共10页&a…

PC端网站 rem适配方案

以1920设计稿为准 1、使用sass语法 转换px rem &#xff0c; vscode编辑器安装插件easy-scss 安装之后打开插件setting.json&#xff0c; 在setting.json里配置转换后css的路径 “easysass.targetDir”: “./css” 创建css文件、scss文件&#xff1a; 项目中如何使用index.sc…

给大家分享下织梦网站模板

最近需要用到一个官网模板&#xff0c;作为一个后台程序员来说写前端和专业的差了远了&#xff0c;所以想找个模板学习下&#xff0c;可是我这面是一顿搜一顿找找了好多都快绝望了因为都不是免费下载的不过最终还是找到了&#xff0c;在这里分享给大家 模板一 红色大气响应式重…

网站案例,成功案例--我的个人作品

西南贝思建筑图书连锁店网上书店成都美可快餐诚信促进会 成都商务投资网中国西部土工材料网广东劳斯丹顿卫浴设备有限公司盛世外贸服装批发城西南时报成都免费网站中国检察网成都乐于健康护理成都顺世文化传播有限公司西南大学生联盟成都川国味餐饮有限公司成都市科技进修学院…

vue 微软插件实现根据第三方网站链接预览word、pd、excelf等文件

一开始做的时候没想到会预览不了&#xff0c;报错 File not found The URL of the original file is not valid or the document is not publicly accessible. Verify the URL is correct, then contact the document owner.如下&#xff1a; 我在微软官网查到&#xff1a; 官…

世界第五大外包公司(CGI group inc)能够做出来多烂的网站,通过系分的课程设计我对于人机交互和团队合作的几个总结

文章目录 前情提要为开始刨祖坟模式做准备CGI inc发家史什么是外包公司CGI group inc看看CGI公司外包的网站你以为CGI做的网站这就算差了&#xff1f;听说你想自己改成好记的密码&#xff0c;想多了吧信息填写一次过&#xff0c;听说你想写到一半保存&#xff1f;我是随便的人吗…

小型网站到大型网站的演化过程

本文摘自书籍《大型网站技术架构.核心原理与案例分析》 1. 初始阶段&#xff1a; 应用程序、数据库、文件等所有资源都在一台服务器上。 2. 应用服务与数据服务分离 应用和数据分离后整个网站使用三台服务器&#xff1a;应用服务器、文件服务器和数据库服务器。这三台服务器对…

python爬取唯品会电商网站数据并保存到csv中

文章目录 1.目标、思路2. 获取数据3.解析数据4.保存数据 1.目标、思路 目标&#xff1a; 本次爬虫爬取的目标是唯品会中口红分类的商品。&#xff08;url &#xff09; 思路&#xff1a;打开网页后点击F12打开抓包工具&#xff0c;找到我们想要的数据包&#xff0c;并分析。 …

各大视频网站下载神器

先说介绍工具&#xff0c;后说使用方法&#xff08;其实不难&#xff09; you-get 是GitHub上的一个项目&#xff0c;其实就是通过几个命令行下载各大网站视频的&#xff0c;这对于做视频的爱好者及一些司机都是很奈斯的选择。 地址&#xff1a;https://github.com/soimort/y…

电子商务网站的 10 个易用性规则

电子商务网站的 10 个易用性规则 电子商务网站的竞争越来越激烈&#xff0c;虽然你可能在以最低的价格卖最好的东西&#xff0c;但同样重要的的是用户的购买体验&#xff0c;对电子商务网站而言&#xff0c;易用性就是让用户尽 可能快而简单的完成购买&#xff0c;有时候&…

基于jquery Stellar.js实现 网站视差滚动效果

stellar.js是一个 jQuery插件&#xff0c;能很容易地给网站添加视差滚动效果。 虽然已经停止了维护&#xff0c;但它非常稳定&#xff0c;与最新版本的jQuery兼容。 http://markdalgleish.com/projects/stellar.js/ 官网 1.引用js 包 <script src"path/to/jquery/jq…

推荐一个前沿的无线通信网站——定为电子

欢迎来到定为官网 成都定为电子技术有限公司是一家集咨询、设计、研发和生产无线通信数字基带和模拟前端等产品的高新技术企业&#xff0c;公司一直专注于算法原型验证、教学实验、无线通信和便携式仪器设备等领域。 在算法原型验证方面&#xff0c;定为电子坚持为客户提供最…

网站被DDOS攻击,我只用了2招应对

做网站难免遇到被黑客攻击&#xff0c;特别是关键词排名较靠前的站点&#xff0c;一些不正当的竞争对手会使用不正当的方法把你网站搞瘫&#xff0c;以便把流量引到自己平台上让自己得利。不管是大平台网站还是小平台网站都会遇到DDOS攻击&#xff0c;难道面对DDOS&#xff0c;…

【Python】利用网站API接口获取天气信息

本文主要讲如何利用Python来获取天气信息。主要程序实现思路是从命令行传递坐标信息&#xff0c;然后利用天气预报网站的免费接口获取到相关天气信息&#xff0c;返回结果以json格式显示&#xff0c;并打印出需要的近3天天气情况。 信息源&#xff1a;https://openweathermap.…

做一个酷网站(2)

成品 成品源码 接上篇博客 相信你根据 Demo 的源代码 已经可以制作一个简单的动态网站了 核心思想 同时动态修改 style 标签 和 页面上 某个元素的内容产生一种网站在动态展示的效果 知识点 ES6 箭头函数使用setInterval(fn, n)substring(n, m)document.querySelector()dom 元…

用户访问一个网站的整过程

原文网址&#xff1a;https://blog.csdn.net/yonggeit/article/details/72857630 用户访问网站流程框架 第一步&#xff1a;客户端用户从浏览器输入www.baidu.com网站网址后回车&#xff0c;系统会查询本地hosts文件及DNS缓存信息&#xff0c;查找是否存在网址对应的IP解析记…

手机网页的设计vs2008开发wap网站(一)

vs2008开发wap网站(一)首先新创建个项目&#xff0c;打开VS2008&#xff0c;新建个网站项目&#xff0c;我们添加新项时会发现以前在vs2003或vs2005中的“移动Web窗体”项没有了&#xff0c;下图为vs2003和2005中的。 vs2008中就没有“移动Web窗体”这一项&#xff0c;这…