Cookies support added.
This commit is contained in:
		| @@ -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) | ||||||
|   | |||||||
							
								
								
									
										16
									
								
								geziyor.go
									
									
									
									
									
								
							
							
						
						
									
										16
									
								
								geziyor.go
									
									
									
									
									
								
							| @@ -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() | ||||||
| 	} | 	} | ||||||
|   | |||||||
| @@ -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() | ||||||
|  | } | ||||||
|   | |||||||
| @@ -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 | ||||||
|   | |||||||
| @@ -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 | ||||||
| } | } | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user