Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1""" 

2@file 

3@brief Provides functionalities around `LinkedIn <http://fr.linkedin.com/>`_. 

4 

5Some pointers: 

6 

7- `API LinkedIn <http://developer.linkedin.com/rest>`_ 

8- `examples with LinkedIn API <http://developer.linkedin.com/apis>`_ 

9- `using oauth <https://developer.linkedin.com/documents/getting-oauth-token-python>`_ 

10 

11Existing Python libraries: 

12 

13- `python-linkedin <https://github.com/ozgur/python-linkedin>`_ 

14- `linkedin-client-library <https://github.com/mrgaaron/LinkedIn-Client-Library>`_ 

15 

16.. require: requests_oauthlib 

17 

18You should install: `sdpython/python-linkedin <https://github.com/sdpython/python-linkedin>`_ 

19 

20""" 

21import copy 

22import warnings 

23 

24 

25class LinkedInAccess: 

26 

27 """ 

28 This class manages the access to :epkg:`LinkedIn` functionalities. 

29 

30 It assumes you requested an access to the `API LinkedIn <http://developer.linkedin.com/rest>`_ 

31 See also `ipython + python-linkedin 

32 <http://nbviewer.ipython.org/urls/raw.github.com/ptwobrussell/ 

33 Mining-the-Social-Web-2nd-Edition/master/ipynb/Chapter%203%20-%20Mining%20LinkedIn.ipynb>`_. 

34 

35 See `linkedin <https://github.com/andrewychoi/python3-linkedin>`_, `linkedin <https://github.com/ozgur/python-linkedin>`_. 

36 This class proposes simplified versions of the same methods, you should follow those link to see what is missing. 

37 About :epkg:`LinkedIn`: 

38 

39 - Throttle limits: see `throttle-limits <http://developer.linkedin.com/documents/throttle-limits>`_ 

40 - To see your API usage: `API limits <https://www.linkedin.com/secure/developer>`_ 

41 - Setting up your `Permissions <https://developer.linkedin.com/documents/authentication#granting>`_ 

42 - `Developing page <https://developer.linkedin.com/whydevelop>`_ 

43 - `Connections API <http://developer.linkedin.com/documents/connections-api>`_ 

44 - `Extend your throttle limits <http://developer.linkedin.com/themes/linkedin-home/form-api.html>`_ 

45 - `Accounts <http://www.linkedin.com/mnyfe/subscriptionv2?displaySalesProduct=&identify=false&crm=sfdc>`_ 

46 - `Accounts comparison <http://www.linkedin.com/mnyfe/subscriptionv2?displayProducts=&family=general&commpare_acct=>`_ 

47 

48 Search API:: 

49 

50 https://api.linkedin.com/v1/company-search:(companies:(id,name,logo_url))?keywords=keyword?oauth2_access_token={accesstoken}&format=json 

51 

52 .. exref:: 

53 :title: How to Get data from your linkedin profile? 

54 

55 Search and get profiles: 

56 

57 :: 

58 

59 accessToken = ["w....", "n....", 

60 "3....", "2..." ] 

61 

62 linkedin = LinkedInAccess(*TestLinkedIn.accessToken) 

63 linkedin.connect() 

64 se = linkedin.search_profile(params={"last-name":"dupre", "first-name":"xavier"}) 

65 for _ in se["people"]["values"]: 

66 print(_) 

67 

68 Same results in a DataFrame: 

69 

70 :: 

71 

72 accessToken = ["w....", "n....", 

73 "3....", "2..." ] 

74 

75 linkedin = LinkedInAccess(*TestLinkedIn.accessToken) 

76 linkedin.connect() 

77 

78 df = linkedin.search_profile(params={"keywords":"ensae"}, 

79 count=500, as_df=True ) 

80 """ 

81 

82 default_selectors_profile = ['id', 'first-name', 'last-name', 

83 'location', 'distance', 'num-connections', 

84 'skills', 'educations', 

85 # 'school-name', 

86 ] 

87 

88 default_selectors_search_profile = [ 

89 {'people': ['id', 'first-name', 'last-name', 'educations', 

90 # 'school-name', 

91 'public-profile-url', 'location', 'headline', 

92 'last-modified-timestamp', 'date-of-birth', 

93 'member-url-resources', 'email-address', 

94 ]}] 

95 

96 def __init__(self, api_key, secret_key, user_token, user_secret): 

97 """ 

98 All the following parameter are given when you request an access 

99 to :epkg:`linkedin`. 

100 

101 @param api_key api key ``[a-z0-9]+`` 

102 @param secrect_key secret key ``[A-Za-z]+`` 

103 @param user_token user token ``guid style`` 

104 @param user_secret user_secret ``guid style`` 

105 """ 

106 

107 self.api_key = api_key 

108 self.secret_key = secret_key 

109 self.user_token = user_token 

110 self.user_secret = user_secret 

111 

112 def connect(self, all_permissions=True): 

113 """ 

114 Opens the connection to :epkg:`linkedin` 

115 (using the api_key and the secret_key). 

116 

117 @param all_permissions True to get all permissions, otherwise, only public profiles 

118 @return client 

119 """ 

120 from linkedin_v2 import linkedin # pylint: disable=E0401 

121 # permissions = linkedin.PERMISSIONS.enums.values() if all_permissions \ 

122 # else linkedin.PERMISSIONS.BASIC_PROFILE 

123 self.authentication = linkedin.LinkedInDeveloperAuthentication( 

124 self.api_key, 

125 self.secret_key, 

126 self.user_token, 

127 self.user_secret, 

128 "http://localhost:8000/" 

129 ) 

130 

131 self.application = linkedin.LinkedInApplication(self.authentication) 

132 self.all_permissions = all_permissions 

133 return self.application 

134 

135 def get_profile(self, selectors=None, idu=None, url=None): 

136 """ 

137 Returns the profile of the connected user. 

138 

139 @param selectors if None, it is replace by ``LinkedInAccess.default_selectors`` 

140 @param idu search by id 

141 @param url search by url 

142 @return json 

143 

144 See `selectors <http://developer.linkedin.com/documents/profile-api>`_ 

145 to get the full of allowed selectors. 

146 """ 

147 if selectors is None and self.all_permissions: 

148 selectors = LinkedInAccess.default_selectors_profile 

149 return self.application.get_profile(selectors=selectors, 

150 member_id=idu, member_url=url) 

151 

152 def search_profile(self, params, selectors=None, count=10, 

153 as_df=False, start=0, fLOG=None): 

154 """ 

155 Searches profiles on linkedin, allowed parameters (replace _ by -): 

156 

157 - first-name 

158 - last-name 

159 

160 The others fields are (see `search api <http://developer.linkedin.com/documents/people-search-api>`_ :: 

161 

162 http://api.linkedin.com/v1/people-search? 

163 keywords=[space delimited keywords]& 

164 first-name=[first name]& 

165 last-name=[last name]& 

166 company-name=[company name]& 

167 current-company=[true|false]& 

168 title=[title]& 

169 current-title=[true|false]& 

170 school-name=[school name]& 

171 current-school=[true|false]& 

172 country-code=[country code]& 

173 postal-code=[postal code]& 

174 distance=[miles]& 

175 start=[number]& 

176 count=[1-25]& 

177 facet=[facet code, values]& 

178 facets=[facet codes]& 

179 sort=[connections|recommenders|distance|relevance] 

180 

181 @param params dictionary ``{ field: value }`` (see above) 

182 @param selectors if None, uses the default selectors 

183 @param count 1 to 25, if -1 or > 25, search for all (do multiple searches and concatenate them 

184 @param as_df return a DataFrame 

185 @param start first result to fetch 

186 @param fLOG logging function 

187 @return json format (or DataFrame if as_df is True or None if there is nothing to return) 

188 

189 Example of code:: 

190 

191 accessToken = [ "w....", 

192 "n....", 

193 "3....", 

194 "2..." ] 

195 

196 linkedin = LinkedInAccess (*TestLinkedIn.accessToken) 

197 linkedin.connect() 

198 se = linkedin.search_profile ( params = {"last-name":"dupre", "first-name":"xavier"} ) 

199 for _ in se["people"]["values"] : 

200 print(_) 

201 """ 

202 from linkedin_v2.exceptions import LinkedInError # pylint: disable=E0401 

203 

204 if selectors is None and self.all_permissions: 

205 selectors = LinkedInAccess.default_selectors_search_profile 

206 

207 bound = count if count != -1 and count <= 25 else 25 

208 params = copy.copy(params) 

209 params["count"] = bound 

210 params["start"] = start 

211 

212 if 0 <= count <= 25: 

213 res = self.application.search_profile( # pylint: disable=E1101 

214 selectors=selectors, 

215 params=params) 

216 if as_df: 

217 if len(res) == 1: 

218 first = res.popitem()[1] 

219 values = first["values"] 

220 import pandas 

221 return pandas.DataFrame(values) 

222 else: 

223 raise Exception( 

224 "expecting a result such as {'people': ...}") 

225 else: 

226 return res 

227 

228 else: 

229 res = [] 

230 start = 0 

231 total = 0 

232 while True: 

233 params["start"] = start 

234 try: 

235 se = self.application.search_profile( # pylint: disable=E1101 

236 selectors=selectors, 

237 params=params) 

238 except LinkedInError as e: 

239 if "Throttle limit" in str(e): 

240 warnings.warn(e) 

241 break 

242 

243 if len(se) == 1: 

244 first = se.popitem()[1] 

245 fetched = first.get("_count", 0) 

246 total += fetched 

247 alls = first.get("_total", 0) 

248 else: 

249 raise Exception( 

250 "expecting a result such as {'people': ...} +\n" + 

251 str(se)) 

252 

253 if as_df: 

254 values = first.get("values", []) 

255 res.extend(values) 

256 else: 

257 res.append(se) 

258 

259 if fLOG: 

260 fLOG("LinkedInAccess.search_profile [bound=%d,count=%d,fetched=%d,total=%d,alls=%d]" % 

261 (bound, count, fetched, total, alls)) 

262 if fetched < bound or (count != -1 and len(res) >= count): 

263 break 

264 start = total 

265 

266 if as_df: 

267 if len(res) > 0: 

268 import pandas 

269 return pandas.DataFrame(res) 

270 else: 

271 return None 

272 else: 

273 return res 

274 

275 def get_connections(self, totals_only=None, params=None, headers=None): 

276 """ 

277 Retrieves the connection for a given profile. 

278 """ 

279 res = self.application.get_connections( 

280 totals_only=totals_only, params=params, headers=headers) 

281 return res