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)
- Request Delays (Constant/Randomized)
- Automatic response decoding to UTF-8
- Cookies
- Middlewares
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
```go
geziyor.NewGeziyor(geziyor.Options{
geziyor.NewGeziyor(&geziyor.Options{
StartURLs: []string{"http://api.ipify.org"},
ParseFunc: func(g *geziyor.Geziyor, r *geziyor.Response) {
fmt.Println(string(r.Body))
@ -35,7 +36,7 @@ Advanced usage
```go
func main() {
geziyor.NewGeziyor(geziyor.Options{
geziyor.NewGeziyor(&geziyor.Options{
StartURLs: []string{"http://quotes.toscrape.com/"},
ParseFunc: quotesParse,
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.
```go
geziyor.NewGeziyor(geziyor.Options{
geziyor.NewGeziyor(&geziyor.Options{
StartURLs: []string{"http://api.ipify.org"},
ParseFunc: func(g *geziyor.Geziyor, r *geziyor.Response) {
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
geziyor.NewGeziyor(geziyor.Options{
geziyor.NewGeziyor(&geziyor.Options{
StartRequestsFunc: func(g *geziyor.Geziyor) {
g.GetRendered("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
type Geziyor struct {
Opt Options
Opt *Options
Client *internal.Client
Exports chan interface{}
client *http.Client
wg sync.WaitGroup
semGlobal chan struct{}
semHosts struct {
@ -47,9 +47,9 @@ func init() {
// NewGeziyor creates new Geziyor with default values.
// If options provided, options
func NewGeziyor(opt Options) *Geziyor {
func NewGeziyor(opt *Options) *Geziyor {
geziyor := &Geziyor{
client: internal.NewClient(),
Client: internal.NewClient(opt.CookiesDisabled),
Opt: opt,
Exports: make(chan interface{}),
requestMiddlewares: []RequestMiddleware{
@ -69,11 +69,11 @@ func NewGeziyor(opt Options) *Geziyor {
geziyor.Opt.MaxBodySize = 1024 * 1024 * 1024 // 1GB
}
if opt.Cache != nil {
geziyor.client.Transport = &httpcache.Transport{
Transport: geziyor.client.Transport, Cache: opt.Cache, MarkCachedResponses: true}
geziyor.Client.Transport = &httpcache.Transport{
Transport: geziyor.Client.Transport, Cache: opt.Cache, MarkCachedResponses: true}
}
if opt.Timeout != 0 {
geziyor.client.Timeout = opt.Timeout
geziyor.Client.Timeout = opt.Timeout
}
if opt.ConcurrentRequests != 0 {
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())
// Do request
resp, err := g.client.Do(req.Request)
resp, err := g.Client.Do(req.Request)
if resp != nil {
defer resp.Body.Close()
}

View File

@ -13,7 +13,7 @@ import (
)
func TestSimple(t *testing.T) {
geziyor.NewGeziyor(geziyor.Options{
geziyor.NewGeziyor(&geziyor.Options{
StartURLs: []string{"http://api.ipify.org"},
ParseFunc: func(g *geziyor.Geziyor, r *geziyor.Response) {
fmt.Println(string(r.Body))
@ -23,7 +23,7 @@ func TestSimple(t *testing.T) {
func TestSimpleCache(t *testing.T) {
defer leaktest.Check(t)()
geziyor.NewGeziyor(geziyor.Options{
geziyor.NewGeziyor(&geziyor.Options{
StartURLs: []string{"http://api.ipify.org"},
Cache: httpcache.NewMemoryCache(),
ParseFunc: func(g *geziyor.Geziyor, r *geziyor.Response) {
@ -36,7 +36,7 @@ func TestSimpleCache(t *testing.T) {
func TestQuotes(t *testing.T) {
defer leaktest.Check(t)()
geziyor.NewGeziyor(geziyor.Options{
geziyor.NewGeziyor(&geziyor.Options{
StartURLs: []string{"http://quotes.toscrape.com/"},
ParseFunc: quotesParse,
Exporters: []geziyor.Exporter{&exporter.JSONExporter{}},
@ -65,7 +65,7 @@ func quotesParse(g *geziyor.Geziyor, r *geziyor.Response) {
func TestAllLinks(t *testing.T) {
defer leaktest.Check(t)()
geziyor.NewGeziyor(geziyor.Options{
geziyor.NewGeziyor(&geziyor.Options{
AllowedDomains: []string{"books.toscrape.com"},
StartURLs: []string{"http://books.toscrape.com/"},
ParseFunc: func(g *geziyor.Geziyor, r *geziyor.Response) {
@ -90,7 +90,7 @@ func TestRandomDelay(t *testing.T) {
}
func TestStartRequestsFunc(t *testing.T) {
geziyor.NewGeziyor(geziyor.Options{
geziyor.NewGeziyor(&geziyor.Options{
StartRequestsFunc: func(g *geziyor.Geziyor) {
g.Get("http://quotes.toscrape.com/", g.Opt.ParseFunc)
},
@ -104,7 +104,7 @@ func TestStartRequestsFunc(t *testing.T) {
}
func TestGetRendered(t *testing.T) {
geziyor.NewGeziyor(geziyor.Options{
geziyor.NewGeziyor(&geziyor.Options{
StartRequestsFunc: func(g *geziyor.Geziyor) {
g.GetRendered("https://httpbin.org/anything", g.Opt.ParseFunc)
},
@ -116,7 +116,7 @@ func TestGetRendered(t *testing.T) {
}
func TestHEADRequest(t *testing.T) {
geziyor.NewGeziyor(geziyor.Options{
geziyor.NewGeziyor(&geziyor.Options{
StartRequestsFunc: func(g *geziyor.Geziyor) {
g.Head("https://httpbin.org/anything", g.Opt.ParseFunc)
},
@ -125,3 +125,24 @@ func TestHEADRequest(t *testing.T) {
},
}).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
import (
"errors"
"net"
"net/http"
"net/http/cookiejar"
"net/url"
"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
func NewClient() *http.Client {
return &http.Client{
func NewClient(cookiesDisabled bool) *Client {
client := &http.Client{
Transport: &http.Transport{
Proxy: http.ProxyFromEnvironment,
DialContext: (&net.Dialer{
@ -24,6 +37,35 @@ func NewClient() *http.Client {
},
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

View File

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