-
Notifications
You must be signed in to change notification settings - Fork 15
/
Copy pathlinkedin.go
123 lines (100 loc) · 3.2 KB
/
linkedin.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
// Package golinkedin is a library for scraping Linkedin.
// Unfortunately, auto login is impossible (probably...), so you need to retrieve Linkedin session cookies manually.
// As mentioned above, the purpose of this package is only for scraping, so there is no method for create, update, or delete data.
// Not all object is documented or present because Franklin Collin Tamboto, the original author, does not fully understand the purpose
// of some object returned by Linkedin internal API, and because the nature of Linkedin internal API that treat almost every object as
// optional, empty field or object will not be returned by Linkedin internal API, so some object or fields might be missing.
// Feel free to fork and contribute!
package golinkedin
import (
"errors"
"io/ioutil"
"net/http"
"net/url"
"strings"
"time"
)
const (
// ErrSessionInvalid is returned when Linkedin session can not be used
ErrSessionInvalid = "Linkedin session invalid"
basePath = "https://www.linkedin.com"
userAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:82.0) Gecko/20100101 Firefox/82.0"
)
var apiBase string = basePath + "/voyager/api"
// Linkedin hold all (covered) features and data resources
type Linkedin struct {
client *http.Client
cookies []*http.Cookie
}
// New initiate new Linkedin object
func New() *Linkedin {
ln := &Linkedin{client: new(http.Client)}
return ln
}
// SetCookieStr set Linkedin session cookie string
func (ln *Linkedin) SetCookieStr(c string) {
header := http.Header{}
header.Add("Cookie", c)
request := http.Request{Header: header}
ln.SetCookies(request.Cookies())
}
// SetCookies set Linkedin session cookies from parsed cookie string
func (ln *Linkedin) SetCookies(c []*http.Cookie) {
ln.cookies = c
}
// SetProxy set proxy to client
func (ln *Linkedin) SetProxy(p string) error {
uri, err := url.Parse(p)
if err != nil {
return err
}
ln.client.Transport = &http.Transport{Proxy: http.ProxyURL(uri), TLSHandshakeTimeout: 20 * time.Second}
return nil
}
func (ln *Linkedin) get(path string, q url.Values) ([]byte, error) {
uri, err := url.Parse(apiBase + path)
if err != nil {
return nil, err
}
if q != nil {
rawQuery := strings.ReplaceAll(q.Encode(), "%28", "(")
rawQuery = strings.ReplaceAll(rawQuery, "%29", ")")
rawQuery = strings.ReplaceAll(rawQuery, "%2C", ",")
uri.RawQuery = rawQuery
}
req, err := http.NewRequest("GET", uri.String(), nil)
if err != nil {
return nil, err
}
req.Header.Set("Accept", "application/json")
req.Header.Set("Accept-Language", "en-US,en;q=0.5")
req.Header.Set("Connection", "keep-alive")
req.Header.Set("Upgrade-Insecure-Requests", "1")
req.Header.Set("x-restli-protocol-version", "2.0.0")
req.Header.Set("User-Agent", userAgent)
req.Header.Set("csrf-token", ln.jsessionid())
for _, cookie := range ln.cookies {
req.AddCookie(cookie)
}
resp, err := ln.client.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
raw, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, err
}
if resp.StatusCode > 200 {
return nil, errors.New(string(raw))
}
return raw, nil
}
func (ln *Linkedin) jsessionid() string {
for _, c := range ln.cookies {
if c.Name == "JSESSIONID" {
return c.Value
}
}
return ""
}