Cookies support added.

This commit is contained in:
Musab Gültekin 2019-06-17 13:31:19 +03:00
parent dd6687f976
commit a5ec28664d
5 changed files with 88 additions and 21 deletions

View File

@ -12,6 +12,7 @@ Geziyor is a blazing fast web crawling and web scraping framework. It can be use
- Limit Concurrency (Global/Per Domain) - Limit Concurrency (Global/Per Domain)
- Request Delays (Constant/Randomized) - Request Delays (Constant/Randomized)
- Automatic response decoding to UTF-8 - Automatic response decoding to UTF-8
- Cookies
- Middlewares - Middlewares
See scraper [Options](https://godoc.org/github.com/geziyor/geziyor#Options) for all custom settings. See scraper [Options](https://godoc.org/github.com/geziyor/geziyor#Options) for all custom settings.
@ -23,7 +24,7 @@ Since the project is in **development phase**, **API may change in time**. Thus,
Simple usage Simple usage
```go ```go
geziyor.NewGeziyor(geziyor.Options{ geziyor.NewGeziyor(&geziyor.Options{
StartURLs: []string{"http://api.ipify.org"}, StartURLs: []string{"http://api.ipify.org"},
ParseFunc: func(g *geziyor.Geziyor, r *geziyor.Response) { ParseFunc: func(g *geziyor.Geziyor, r *geziyor.Response) {
fmt.Println(string(r.Body)) fmt.Println(string(r.Body))
@ -35,7 +36,7 @@ Advanced usage
```go ```go
func main() { func main() {
geziyor.NewGeziyor(geziyor.Options{ geziyor.NewGeziyor(&geziyor.Options{
StartURLs: []string{"http://quotes.toscrape.com/"}, StartURLs: []string{"http://quotes.toscrape.com/"},
ParseFunc: quotesParse, ParseFunc: quotesParse,
Exporters: []geziyor.Exporter{exporter.JSONExporter{}}, Exporters: []geziyor.Exporter{exporter.JSONExporter{}},
@ -75,7 +76,7 @@ Geziyor makes concurrent requests to those URLs.
After reading response, ```ParseFunc func(g *Geziyor, r *Response)``` called. After reading response, ```ParseFunc func(g *Geziyor, r *Response)``` called.
```go ```go
geziyor.NewGeziyor(geziyor.Options{ geziyor.NewGeziyor(&geziyor.Options{
StartURLs: []string{"http://api.ipify.org"}, StartURLs: []string{"http://api.ipify.org"},
ParseFunc: func(g *geziyor.Geziyor, r *geziyor.Response) { ParseFunc: func(g *geziyor.Geziyor, r *geziyor.Response) {
fmt.Println(string(r.Body)) fmt.Println(string(r.Body))
@ -94,7 +95,7 @@ As it opens up a real browser, it takes a couple of seconds to make requests.
```go ```go
geziyor.NewGeziyor(geziyor.Options{ geziyor.NewGeziyor(&geziyor.Options{
StartRequestsFunc: func(g *geziyor.Geziyor) { StartRequestsFunc: func(g *geziyor.Geziyor) {
g.GetRendered("https://httpbin.org/anything", g.Opt.ParseFunc) g.GetRendered("https://httpbin.org/anything", g.Opt.ParseFunc)
g.Head("https://httpbin.org/anything", g.Opt.ParseFunc) g.Head("https://httpbin.org/anything", g.Opt.ParseFunc)

View File

@ -25,10 +25,10 @@ type Exporter interface {
// Geziyor is our main scraper type // Geziyor is our main scraper type
type Geziyor struct { type Geziyor struct {
Opt Options Opt *Options
Client *internal.Client
Exports chan interface{} Exports chan interface{}
client *http.Client
wg sync.WaitGroup wg sync.WaitGroup
semGlobal chan struct{} semGlobal chan struct{}
semHosts struct { semHosts struct {
@ -47,9 +47,9 @@ func init() {
// NewGeziyor creates new Geziyor with default values. // NewGeziyor creates new Geziyor with default values.
// If options provided, options // If options provided, options
func NewGeziyor(opt Options) *Geziyor { func NewGeziyor(opt *Options) *Geziyor {
geziyor := &Geziyor{ geziyor := &Geziyor{
client: internal.NewClient(), Client: internal.NewClient(opt.CookiesDisabled),
Opt: opt, Opt: opt,
Exports: make(chan interface{}), Exports: make(chan interface{}),
requestMiddlewares: []RequestMiddleware{ requestMiddlewares: []RequestMiddleware{
@ -69,11 +69,11 @@ func NewGeziyor(opt Options) *Geziyor {
geziyor.Opt.MaxBodySize = 1024 * 1024 * 1024 // 1GB geziyor.Opt.MaxBodySize = 1024 * 1024 * 1024 // 1GB
} }
if opt.Cache != nil { if opt.Cache != nil {
geziyor.client.Transport = &httpcache.Transport{ geziyor.Client.Transport = &httpcache.Transport{
Transport: geziyor.client.Transport, Cache: opt.Cache, MarkCachedResponses: true} Transport: geziyor.Client.Transport, Cache: opt.Cache, MarkCachedResponses: true}
} }
if opt.Timeout != 0 { if opt.Timeout != 0 {
geziyor.client.Timeout = opt.Timeout geziyor.Client.Timeout = opt.Timeout
} }
if opt.ConcurrentRequests != 0 { if opt.ConcurrentRequests != 0 {
geziyor.semGlobal = make(chan struct{}, opt.ConcurrentRequests) geziyor.semGlobal = make(chan struct{}, opt.ConcurrentRequests)
@ -212,7 +212,7 @@ func (g *Geziyor) doRequestClient(req *Request) (*Response, error) {
log.Println("Fetching: ", req.URL.String()) log.Println("Fetching: ", req.URL.String())
// Do request // Do request
resp, err := g.client.Do(req.Request) resp, err := g.Client.Do(req.Request)
if resp != nil { if resp != nil {
defer resp.Body.Close() defer resp.Body.Close()
} }

View File

@ -13,7 +13,7 @@ import (
) )
func TestSimple(t *testing.T) { func TestSimple(t *testing.T) {
geziyor.NewGeziyor(geziyor.Options{ geziyor.NewGeziyor(&geziyor.Options{
StartURLs: []string{"http://api.ipify.org"}, StartURLs: []string{"http://api.ipify.org"},
ParseFunc: func(g *geziyor.Geziyor, r *geziyor.Response) { ParseFunc: func(g *geziyor.Geziyor, r *geziyor.Response) {
fmt.Println(string(r.Body)) fmt.Println(string(r.Body))
@ -23,7 +23,7 @@ func TestSimple(t *testing.T) {
func TestSimpleCache(t *testing.T) { func TestSimpleCache(t *testing.T) {
defer leaktest.Check(t)() defer leaktest.Check(t)()
geziyor.NewGeziyor(geziyor.Options{ geziyor.NewGeziyor(&geziyor.Options{
StartURLs: []string{"http://api.ipify.org"}, StartURLs: []string{"http://api.ipify.org"},
Cache: httpcache.NewMemoryCache(), Cache: httpcache.NewMemoryCache(),
ParseFunc: func(g *geziyor.Geziyor, r *geziyor.Response) { ParseFunc: func(g *geziyor.Geziyor, r *geziyor.Response) {
@ -36,7 +36,7 @@ func TestSimpleCache(t *testing.T) {
func TestQuotes(t *testing.T) { func TestQuotes(t *testing.T) {
defer leaktest.Check(t)() defer leaktest.Check(t)()
geziyor.NewGeziyor(geziyor.Options{ geziyor.NewGeziyor(&geziyor.Options{
StartURLs: []string{"http://quotes.toscrape.com/"}, StartURLs: []string{"http://quotes.toscrape.com/"},
ParseFunc: quotesParse, ParseFunc: quotesParse,
Exporters: []geziyor.Exporter{&exporter.JSONExporter{}}, Exporters: []geziyor.Exporter{&exporter.JSONExporter{}},
@ -65,7 +65,7 @@ func quotesParse(g *geziyor.Geziyor, r *geziyor.Response) {
func TestAllLinks(t *testing.T) { func TestAllLinks(t *testing.T) {
defer leaktest.Check(t)() defer leaktest.Check(t)()
geziyor.NewGeziyor(geziyor.Options{ geziyor.NewGeziyor(&geziyor.Options{
AllowedDomains: []string{"books.toscrape.com"}, AllowedDomains: []string{"books.toscrape.com"},
StartURLs: []string{"http://books.toscrape.com/"}, StartURLs: []string{"http://books.toscrape.com/"},
ParseFunc: func(g *geziyor.Geziyor, r *geziyor.Response) { ParseFunc: func(g *geziyor.Geziyor, r *geziyor.Response) {
@ -90,7 +90,7 @@ func TestRandomDelay(t *testing.T) {
} }
func TestStartRequestsFunc(t *testing.T) { func TestStartRequestsFunc(t *testing.T) {
geziyor.NewGeziyor(geziyor.Options{ geziyor.NewGeziyor(&geziyor.Options{
StartRequestsFunc: func(g *geziyor.Geziyor) { StartRequestsFunc: func(g *geziyor.Geziyor) {
g.Get("http://quotes.toscrape.com/", g.Opt.ParseFunc) g.Get("http://quotes.toscrape.com/", g.Opt.ParseFunc)
}, },
@ -104,7 +104,7 @@ func TestStartRequestsFunc(t *testing.T) {
} }
func TestGetRendered(t *testing.T) { func TestGetRendered(t *testing.T) {
geziyor.NewGeziyor(geziyor.Options{ geziyor.NewGeziyor(&geziyor.Options{
StartRequestsFunc: func(g *geziyor.Geziyor) { StartRequestsFunc: func(g *geziyor.Geziyor) {
g.GetRendered("https://httpbin.org/anything", g.Opt.ParseFunc) g.GetRendered("https://httpbin.org/anything", g.Opt.ParseFunc)
}, },
@ -116,7 +116,7 @@ func TestGetRendered(t *testing.T) {
} }
func TestHEADRequest(t *testing.T) { func TestHEADRequest(t *testing.T) {
geziyor.NewGeziyor(geziyor.Options{ geziyor.NewGeziyor(&geziyor.Options{
StartRequestsFunc: func(g *geziyor.Geziyor) { StartRequestsFunc: func(g *geziyor.Geziyor) {
g.Head("https://httpbin.org/anything", g.Opt.ParseFunc) g.Head("https://httpbin.org/anything", g.Opt.ParseFunc)
}, },
@ -125,3 +125,24 @@ func TestHEADRequest(t *testing.T) {
}, },
}).Start() }).Start()
} }
func TestCookies(t *testing.T) {
geziyor.NewGeziyor(&geziyor.Options{
StartURLs: []string{"http://quotes.toscrape.com/login"},
ParseFunc: func(g *geziyor.Geziyor, r *geziyor.Response) {
if len(g.Client.Cookies("http://quotes.toscrape.com/login")) == 0 {
t.Fatal("Cookies is Empty")
}
},
}).Start()
geziyor.NewGeziyor(&geziyor.Options{
StartURLs: []string{"http://quotes.toscrape.com/login"},
ParseFunc: func(g *geziyor.Geziyor, r *geziyor.Response) {
if len(g.Client.Cookies("http://quotes.toscrape.com/login")) != 0 {
t.Fatal("Cookies exist")
}
},
CookiesDisabled: true,
}).Start()
}

View File

@ -1,14 +1,27 @@
package internal package internal
import ( import (
"errors"
"net" "net"
"net/http" "net/http"
"net/http/cookiejar"
"net/url"
"time" "time"
) )
var (
// ErrNoCookieJar is the error type for missing cookie jar
ErrNoCookieJar = errors.New("cookie jar is not available")
)
// Client is a small wrapper around *http.Client to provide new methods.
type Client struct {
*http.Client
}
// NewClient creates http.Client with modified values for typical web scraper // NewClient creates http.Client with modified values for typical web scraper
func NewClient() *http.Client { func NewClient(cookiesDisabled bool) *Client {
return &http.Client{ client := &http.Client{
Transport: &http.Transport{ Transport: &http.Transport{
Proxy: http.ProxyFromEnvironment, Proxy: http.ProxyFromEnvironment,
DialContext: (&net.Dialer{ DialContext: (&net.Dialer{
@ -24,6 +37,35 @@ func NewClient() *http.Client {
}, },
Timeout: time.Second * 180, // Google's timeout Timeout: time.Second * 180, // Google's timeout
} }
if !cookiesDisabled {
client.Jar, _ = cookiejar.New(nil)
}
return &Client{Client: client}
}
// SetCookies handles the receipt of the cookies in a reply for the given URL
func (c *Client) SetCookies(URL string, cookies []*http.Cookie) error {
if c.Jar == nil {
return ErrNoCookieJar
}
u, err := url.Parse(URL)
if err != nil {
return err
}
c.Jar.SetCookies(u, cookies)
return nil
}
// Cookies returns the cookies to send in a request for the given URL.
func (c *Client) Cookies(URL string) []*http.Cookie {
if c.Jar == nil {
return nil
}
parsedURL, err := url.Parse(URL)
if err != nil {
return nil
}
return c.Jar.Cookies(parsedURL)
} }
// SetDefaultHeader sets header if not exists before // SetDefaultHeader sets header if not exists before

View File

@ -64,4 +64,7 @@ type Options struct {
// Revisiting same URLs is disabled by default // Revisiting same URLs is disabled by default
URLRevisitEnabled bool URLRevisitEnabled bool
// If set true, cookies won't send.
CookiesDisabled bool
} }