├── README.md ├── get_tencent_exmail_contacts.py └── requirements.txt /README.md: -------------------------------------------------------------------------------- 1 | # 说明 2 | 根据帐号密码,获取腾讯企业邮箱通讯录 3 | 4 | 运行完成后,会生成两个文件 5 | 6 | 一个只包含邮箱 7 | 8 | 一个包含部门和部门员工邮箱,类似 9 | 10 | ``` 11 | ----人事部---- 12 | 张三 zhangsan@domain.com 13 | ----技术部---- 14 | 李四 lisi@domain.com 15 | 王五 wangwu@domain.com 16 | ``` 17 | # 安装依赖 18 | ``` 19 | pip install -r requirements.txt 20 | ``` 21 | # 运行 22 | ``` 23 | python get_tencent_exmail_contacts.py -u name@domain.com -p passw0rd 24 | ``` 25 | 更多参数,运行 -h 查看 26 | ``` 27 | python get_tencent_exmail_contacts.py -h 28 | ``` 29 | 30 | 31 | -------------------------------------------------------------------------------- /get_tencent_exmail_contacts.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | # @Author: blinking.yan 4 | # @Date: 2016-03-03 21:56:26 5 | # @Last Modified by: blinking.yan 6 | # @Last Modified time: 2016-03-03 22:45:18 7 | # @Description: 获取腾讯企业邮箱通讯录 8 | import requests 9 | import re 10 | import rsa 11 | import sys 12 | import base64 13 | import time 14 | import argparse 15 | reload(sys) 16 | 17 | sys.setdefaultencoding('utf8') 18 | 19 | 20 | # 打印部门人员信息 21 | def print_tree(id, department_infos, level, staff_infors, f): 22 | prefix = '----' * level 23 | text = prefix + department_infos[id]['name'] + prefix 24 | print text 25 | f.write(text + '\n') 26 | for key, value in department_infos.items(): 27 | if value['pid'] == id: 28 | print_tree( 29 | value['id'], department_infos, level + 1, staff_infors, f) 30 | prefix = ' ' * level 31 | for staff in staff_infors: 32 | if staff['pid'] == id: 33 | text = prefix + staff['name'] + ' ' + staff['alias'] 34 | print text 35 | f.write(text + '\n') 36 | 37 | 38 | # 提取RSA算法的公钥 39 | def get_public_key(content): 40 | regexp = r'var\s*PublicKey\s*=\s*"(\w+?)";' 41 | results = re.findall(regexp, content) 42 | if results: 43 | return results[0] 44 | 45 | 46 | # 获取ts参数 47 | def get_ts(content): 48 | regexp = r'PublicTs\s*=\s*"([0-9]+)"' 49 | results = re.findall(regexp, content) 50 | if results: 51 | return results[0] 52 | 53 | 54 | # 计算p参数 55 | def get_p(public_key, password, ts): 56 | public_key = rsa.PublicKey(int(public_key, 16), 65537) 57 | res_tmp = rsa.encrypt( 58 | '{password}\n{ts}\n'.format(password=password, ts=ts), public_key) 59 | return base64.b64encode(res_tmp) 60 | 61 | 62 | def msg(): 63 | return 'python get_tencent_exmail_contacts.py -u name@domain.com -p passw0rd' 64 | 65 | if __name__ == "__main__": 66 | description = "获取腾讯企业邮箱通讯录" 67 | parser = argparse.ArgumentParser(description=description, usage=msg()) 68 | parser.add_argument( 69 | "-u", "--email", required=True, dest="email", help="邮箱名") 70 | parser.add_argument( 71 | "-p", "--password", required=True, dest="password", help="邮箱密码") 72 | parser.add_argument( 73 | "-l", "--limit", required=False, dest="limit", default=10000, help="通讯录条数") 74 | parser.add_argument( 75 | "-e", "--efile", required=False, dest="emailfile", default="emails.txt", help="邮箱保存文件") 76 | parser.add_argument( 77 | "-d", "--dfile", required=False, dest="departfile", default="departments.txt", help="部门信息保存文件") 78 | args = parser.parse_args() 79 | email = args.email 80 | password = args.password 81 | limit = args.limit 82 | emailfile = args.emailfile 83 | departfile = args.departfile 84 | session = requests.Session() 85 | 86 | headers = {'Connection': 'keep-alive', 87 | 'Cache-Control': 'max-age=0', 88 | 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8', 89 | 'Upgrade-Insecure-Requests': '1', 90 | 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/48.0.2564.116 Safari/537.36', 91 | 'DNT': '1', 92 | 'Accept-Encoding': 'gzip, deflate, sdch', 93 | 'Accept-Language': 'zh-CN,zh;q=0.8,en-US;q=0.6,en;q=0.4', 94 | } 95 | resp = session.get('https://exmail.qq.com/login', headers=headers) 96 | content = resp.content 97 | 98 | public_key = get_public_key(content) 99 | 100 | ts = get_ts(content) 101 | 102 | p = get_p(public_key, password, ts) 103 | 104 | # print ts 105 | # print public_key 106 | # print p 107 | 108 | uin = email.split('@')[0] 109 | domain = email.split('@')[1] 110 | # print uin 111 | # print domain 112 | 113 | post_data = {} 114 | post_data['sid'] = '' 115 | post_data['firstlogin'] = False 116 | post_data['domain'] = domain 117 | post_data['aliastype'] = 'other' 118 | post_data['errtemplate'] = 'dm_loginpage' 119 | post_data['first_step'] = '' 120 | post_data['buy_amount'] = '' 121 | post_data['year'] = '' 122 | post_data['company_name'] = '' 123 | post_data['is_get_dp_coupon'] = '' 124 | post_data['starttime'] = int(time.time() * 1000) 125 | post_data['redirecturl'] = '' 126 | post_data['f'] = 'biz' 127 | post_data['uin'] = uin 128 | post_data['p'] = p 129 | post_data['delegate_url'] = '' 130 | post_data['ts'] = ts 131 | post_data['from'] = '' 132 | post_data['ppp'] = '' 133 | post_data['chg'] = 0 134 | post_data['loginentry'] = 3 135 | post_data['s'] = '' 136 | post_data['dmtype'] = 'bizmail' 137 | post_data['fun'] = '' 138 | post_data['inputuin'] = email 139 | post_data['verifycode'] = '' 140 | 141 | headers = {'Content-Type': 'application/x-www-form-urlencoded'} 142 | url = 'https://exmail.qq.com/cgi-bin/login' 143 | resp = session.post(url, headers=headers, data=post_data) 144 | 145 | regexp = r'sid=(.*?)"' 146 | 147 | sid = re.findall(regexp, resp.content)[0] 148 | url = 'http://exmail.qq.com/cgi-bin/laddr_biz?action=show_party_list&sid={sid}&t=contact&view=biz' 149 | resp = session.get(url.format(sid=sid)) 150 | 151 | text = resp.text 152 | regexp = r'{id:"(\S*?)", pid:"(\S*?)", name:"(\S*?)", order:"(\S*?)"}' 153 | results = re.findall(regexp, text) 154 | department_ids = [] 155 | department_infor = dict() 156 | root_department = None 157 | for item in results: 158 | department_ids.append(item[0]) 159 | department = dict(id=item[0], pid=item[1], name=item[2], order=item[3]) 160 | department_infor[item[0]] = department 161 | if item[1] == 0 or item[1] == '0': 162 | root_department = department 163 | 164 | regexp = r'{uin:"(\S*?)",pid:"(\S*?)",name:"(\S*?)",alias:"(\S*?)",sex:"(\S*?)",pos:"(\S*?)",tel:"(\S*?)",birth:"(\S*?)",slave_alias:"(\S*?)",department:"(\S*?)",mobile:"(\S*?)"}' 165 | 166 | all_emails = [] 167 | staff_infors = [] 168 | for department_id in department_ids: 169 | url = 'http://exmail.qq.com/cgi-bin/laddr_biz?t=memtree&limit={limit}&partyid={partyid}&action=show_party&sid={sid}' 170 | resp = session.get( 171 | url.format(limit=limit, sid=sid, partyid=department_id)) 172 | text = resp.text 173 | results = re.findall(regexp, text) 174 | 175 | for item in results: 176 | all_emails.append(item[3]) 177 | print item[3] 178 | staff = dict(uin=item[0], pid=item[1], name=item[2], alias=item[3], sex=item[4], pos=item[ 179 | 5], tel=item[6], birth=item[7], slave_alias=item[8], department=item[9], mobile=item[10]) 180 | staff_infors.append(staff) 181 | 182 | with open(emailfile, 'w') as f: 183 | for item in all_emails: 184 | f.write(item + '\n') 185 | 186 | with open(departfile, 'w') as f: 187 | print_tree(root_department['id'], department_infor, 0, staff_infors, f) 188 | 189 | print("total email count: %i" % len(all_emails)) 190 | print("total department count: %i" % len(department_ids)) 191 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | requests>=2.9.1 2 | rsa 3 | argparse 4 | pyopenssl 5 | ndg-httpsclient 6 | pyasn1 --------------------------------------------------------------------------------