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/>`_.
5Some pointers:
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>`_
11Existing Python libraries:
13- `python-linkedin <https://github.com/ozgur/python-linkedin>`_
14- `linkedin-client-library <https://github.com/mrgaaron/LinkedIn-Client-Library>`_
16.. require: requests_oauthlib
18You should install: `sdpython/python-linkedin <https://github.com/sdpython/python-linkedin>`_
20"""
21import copy
22import warnings
25class LinkedInAccess:
27 """
28 This class manages the access to :epkg:`LinkedIn` functionalities.
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>`_.
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`:
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=>`_
48 Search API::
50 https://api.linkedin.com/v1/company-search:(companies:(id,name,logo_url))?keywords=keyword?oauth2_access_token={accesstoken}&format=json
52 .. exref::
53 :title: How to Get data from your linkedin profile?
55 Search and get profiles:
57 ::
59 accessToken = ["w....", "n....",
60 "3....", "2..." ]
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(_)
68 Same results in a DataFrame:
70 ::
72 accessToken = ["w....", "n....",
73 "3....", "2..." ]
75 linkedin = LinkedInAccess(*TestLinkedIn.accessToken)
76 linkedin.connect()
78 df = linkedin.search_profile(params={"keywords":"ensae"},
79 count=500, as_df=True )
80 """
82 default_selectors_profile = ['id', 'first-name', 'last-name',
83 'location', 'distance', 'num-connections',
84 'skills', 'educations',
85 # 'school-name',
86 ]
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 ]}]
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`.
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 """
107 self.api_key = api_key
108 self.secret_key = secret_key
109 self.user_token = user_token
110 self.user_secret = user_secret
112 def connect(self, all_permissions=True):
113 """
114 Opens the connection to :epkg:`linkedin`
115 (using the api_key and the secret_key).
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 )
131 self.application = linkedin.LinkedInApplication(self.authentication)
132 self.all_permissions = all_permissions
133 return self.application
135 def get_profile(self, selectors=None, idu=None, url=None):
136 """
137 Returns the profile of the connected user.
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
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)
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 -):
157 - first-name
158 - last-name
160 The others fields are (see `search api <http://developer.linkedin.com/documents/people-search-api>`_ ::
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]
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)
189 Example of code::
191 accessToken = [ "w....",
192 "n....",
193 "3....",
194 "2..." ]
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
204 if selectors is None and self.all_permissions:
205 selectors = LinkedInAccess.default_selectors_search_profile
207 bound = count if count != -1 and count <= 25 else 25
208 params = copy.copy(params)
209 params["count"] = bound
210 params["start"] = start
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
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
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))
253 if as_df:
254 values = first.get("values", [])
255 res.extend(values)
256 else:
257 res.append(se)
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
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
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