亚洲今日精彩视频_精品一级黄色_免费一级A片在现观看视频_8050福利视频 - 一级免费黄色片

7*24小時應急電話:15927160396
首頁 新聞資訊 技術文章
使用Python模擬登錄QQ郵箱獲取QQ好友列表(摘)

最近因開發項目的需要,有一個需求,就是很多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' onclick='changeimg()'>");

改為

document.write("<img id='vfcode' src='/qq-captcha/?aid=23000101&", Math.random(), "' style='cursor:pointer;border:1px solid #e4eef9' onclick='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郵箱方式來實現的),所以并不能保證你在看到本文章時,上面的代碼依然使用有效。但是總體上的思路依然可以提供參考。

版權所有:武漢網福互聯科技有限公司    鄂ICP備09022096號
業務QQ:23444550 客服QQ:267052100 電郵:23444550@qq.com  

鄂公網安備 42010602000905號

手機站二維碼