Request and response moved to http package

This commit is contained in:
Musab Gültekin 2019-06-29 13:36:39 +03:00
parent 59757607eb
commit 1e109c555d
7 changed files with 63 additions and 62 deletions

View File

@ -27,7 +27,7 @@ Simple usage
```go
geziyor.NewGeziyor(&geziyor.Options{
StartURLs: []string{"http://api.ipify.org"},
ParseFunc: func(g *geziyor.Geziyor, r *geziyor.Response) {
ParseFunc: func(g *geziyor.Geziyor, r *http.Response) {
fmt.Println(string(r.Body))
},
}).Start()
@ -44,7 +44,7 @@ func main() {
}).Start()
}
func quotesParse(g *geziyor.Geziyor, r *geziyor.Response) {
func quotesParse(g *geziyor.Geziyor, r *http.Response) {
r.HTMLDoc.Find("div.quote").Each(func(i int, s *goquery.Selection) {
g.Exports <- map[string]interface{}{
"text": s.Find("span.text").Text(),
@ -78,7 +78,7 @@ After reading response, ```ParseFunc func(g *Geziyor, r *Response)``` called.
```go
geziyor.NewGeziyor(&geziyor.Options{
StartURLs: []string{"http://api.ipify.org"},
ParseFunc: func(g *geziyor.Geziyor, r *geziyor.Response) {
ParseFunc: func(g *geziyor.Geziyor, r *http.Response) {
fmt.Println(string(r.Body))
},
}).Start()
@ -95,7 +95,7 @@ geziyor.NewGeziyor(&geziyor.Options{
g.GetRendered("https://httpbin.org/anything", g.Opt.ParseFunc)
g.Head("https://httpbin.org/anything", g.Opt.ParseFunc)
},
ParseFunc: func(g *geziyor.Geziyor, r *geziyor.Response) {
ParseFunc: func(g *geziyor.Geziyor, r *http.Response) {
fmt.Println(string(r.Body))
},
}).Start()
@ -130,7 +130,7 @@ If response isn't HTML, ```response.HTMLDoc``` would be ```nil```.
```go
geziyor.NewGeziyor(&geziyor.Options{
StartURLs: []string{"http://quotes.toscrape.com/"},
ParseFunc: func(g *geziyor.Geziyor, r *geziyor.Response) {
ParseFunc: func(g *geziyor.Geziyor, r *http.Response) {
r.HTMLDoc.Find("div.quote").Each(func(_ int, s *goquery.Selection) {
log.Println(s.Find("span.text").Text(), s.Find("small.author").Text())
})
@ -146,7 +146,7 @@ You can export data automatically using exporters. Just send data to ```Geziyor.
```go
geziyor.NewGeziyor(&geziyor.Options{
StartURLs: []string{"http://quotes.toscrape.com/"},
ParseFunc: func(g *geziyor.Geziyor, r *geziyor.Response) {
ParseFunc: func(g *geziyor.Geziyor, r *http.Response) {
r.HTMLDoc.Find("div.quote").Each(func(_ int, s *goquery.Selection) {
g.Exports <- map[string]interface{}{
"text": s.Find("span.text").Text(),

View File

@ -149,39 +149,39 @@ func (g *Geziyor) Start() {
}
// Get issues a GET to the specified URL.
func (g *Geziyor) Get(url string, callback func(g *Geziyor, r *Response)) {
func (g *Geziyor) Get(url string, callback func(g *Geziyor, r *http.Response)) {
req, err := stdhttp.NewRequest("GET", url, nil)
if err != nil {
log.Printf("Request creating error %v\n", err)
return
}
g.Do(&Request{Request: req}, callback)
g.Do(&http.Request{Request: req}, callback)
}
// GetRendered issues GET request using headless browser
// Opens up a new Chrome instance, makes request, waits for 1 second to render HTML DOM and closed.
// Rendered requests only supported for GET requests.
func (g *Geziyor) GetRendered(url string, callback func(g *Geziyor, r *Response)) {
func (g *Geziyor) GetRendered(url string, callback func(g *Geziyor, r *http.Response)) {
req, err := stdhttp.NewRequest("GET", url, nil)
if err != nil {
log.Printf("Request creating error %v\n", err)
return
}
g.Do(&Request{Request: req, Rendered: true}, callback)
g.Do(&http.Request{Request: req, Rendered: true}, callback)
}
// Head issues a HEAD to the specified URL
func (g *Geziyor) Head(url string, callback func(g *Geziyor, r *Response)) {
func (g *Geziyor) Head(url string, callback func(g *Geziyor, r *http.Response)) {
req, err := stdhttp.NewRequest("HEAD", url, nil)
if err != nil {
log.Printf("Request creating error %v\n", err)
return
}
g.Do(&Request{Request: req}, callback)
g.Do(&http.Request{Request: req}, callback)
}
// Do sends an HTTP request
func (g *Geziyor) Do(req *Request, callback func(g *Geziyor, r *Response)) {
func (g *Geziyor) Do(req *http.Request, callback func(g *Geziyor, r *http.Response)) {
if req.Synchronized {
g.do(req, callback)
} else {
@ -191,7 +191,7 @@ func (g *Geziyor) Do(req *Request, callback func(g *Geziyor, r *Response)) {
}
// Do sends an HTTP request
func (g *Geziyor) do(req *Request, callback func(g *Geziyor, r *Response)) {
func (g *Geziyor) do(req *http.Request, callback func(g *Geziyor, r *http.Response)) {
g.acquireSem(req)
defer g.releaseSem(req)
if !req.Synchronized {
@ -201,13 +201,13 @@ func (g *Geziyor) do(req *Request, callback func(g *Geziyor, r *Response)) {
for _, middlewareFunc := range g.requestMiddlewares {
middlewareFunc(g, req)
if req.cancelled {
if req.Cancelled {
return
}
}
// Do request normal or Chrome and read response
var response *Response
var response *http.Response
var err error
if !req.Rendered {
response, err = g.doRequestClient(req)
@ -233,7 +233,7 @@ func (g *Geziyor) do(req *Request, callback func(g *Geziyor, r *Response)) {
}
}
func (g *Geziyor) doRequestClient(req *Request) (*Response, error) {
func (g *Geziyor) doRequestClient(req *http.Request) (*http.Response, error) {
// Do request
resp, err := g.Client.Do(req.Request)
@ -260,7 +260,7 @@ func (g *Geziyor) doRequestClient(req *Request) (*Response, error) {
return nil, errors.Wrap(err, "Reading body error")
}
response := Response{
response := http.Response{
Response: resp,
Body: body,
Meta: req.Meta,
@ -270,7 +270,7 @@ func (g *Geziyor) doRequestClient(req *Request) (*Response, error) {
return &response, nil
}
func (g *Geziyor) doRequestChrome(req *Request) (*Response, error) {
func (g *Geziyor) doRequestChrome(req *http.Request) (*http.Response, error) {
var body string
var reqID network.RequestID
var res *network.Response
@ -317,7 +317,7 @@ func (g *Geziyor) doRequestChrome(req *Request) (*Response, error) {
// Set new URL in case of redirection
req.URL, _ = url.Parse(res.URL)
response := Response{
response := http.Response{
Response: &stdhttp.Response{
Request: req.Request,
StatusCode: int(res.Status),
@ -331,7 +331,7 @@ func (g *Geziyor) doRequestChrome(req *Request) (*Response, error) {
return &response, nil
}
func (g *Geziyor) acquireSem(req *Request) {
func (g *Geziyor) acquireSem(req *http.Request) {
if g.Opt.ConcurrentRequests != 0 {
g.semGlobal <- struct{}{}
}
@ -349,7 +349,7 @@ func (g *Geziyor) acquireSem(req *Request) {
}
}
func (g *Geziyor) releaseSem(req *Request) {
func (g *Geziyor) releaseSem(req *http.Request) {
if g.Opt.ConcurrentRequests != 0 {
<-g.semGlobal
}

View File

@ -8,9 +8,9 @@ import (
"github.com/geziyor/geziyor"
"github.com/geziyor/geziyor/exporter"
"github.com/geziyor/geziyor/extractor"
http2 "github.com/geziyor/geziyor/http"
"github.com/geziyor/geziyor/http"
"github.com/geziyor/geziyor/metrics"
"net/http"
httpstd "net/http"
"net/http/httptest"
"testing"
"unicode/utf8"
@ -20,7 +20,7 @@ func TestSimple(t *testing.T) {
defer leaktest.Check(t)()
geziyor.NewGeziyor(&geziyor.Options{
StartURLs: []string{"http://api.ipify.org"},
ParseFunc: func(g *geziyor.Geziyor, r *geziyor.Response) {
ParseFunc: func(g *geziyor.Geziyor, r *http.Response) {
fmt.Println(string(r.Body))
},
}).Start()
@ -31,7 +31,7 @@ func TestSimpleCache(t *testing.T) {
geziyor.NewGeziyor(&geziyor.Options{
StartURLs: []string{"http://api.ipify.org"},
Cache: httpcache.NewMemoryCache(),
ParseFunc: func(g *geziyor.Geziyor, r *geziyor.Response) {
ParseFunc: func(g *geziyor.Geziyor, r *http.Response) {
fmt.Println(string(r.Body))
g.Exports <- string(r.Body)
g.Get("http://api.ipify.org", nil)
@ -48,7 +48,7 @@ func TestQuotes(t *testing.T) {
}).Start()
}
func quotesParse(g *geziyor.Geziyor, r *geziyor.Response) {
func quotesParse(g *geziyor.Geziyor, r *http.Response) {
r.HTMLDoc.Find("div.quote").Each(func(i int, s *goquery.Selection) {
// Export Data
g.Exports <- map[string]interface{}{
@ -73,7 +73,7 @@ func TestAllLinks(t *testing.T) {
geziyor.NewGeziyor(&geziyor.Options{
AllowedDomains: []string{"books.toscrape.com"},
StartURLs: []string{"http://books.toscrape.com/"},
ParseFunc: func(g *geziyor.Geziyor, r *geziyor.Response) {
ParseFunc: func(g *geziyor.Geziyor, r *http.Response) {
g.Exports <- []string{r.Request.URL.String()}
r.HTMLDoc.Find("a").Each(func(i int, s *goquery.Selection) {
if href, ok := s.Attr("href"); ok {
@ -91,7 +91,7 @@ func TestStartRequestsFunc(t *testing.T) {
StartRequestsFunc: func(g *geziyor.Geziyor) {
g.Get("http://quotes.toscrape.com/", g.Opt.ParseFunc)
},
ParseFunc: func(g *geziyor.Geziyor, r *geziyor.Response) {
ParseFunc: func(g *geziyor.Geziyor, r *http.Response) {
r.HTMLDoc.Find("a").Each(func(_ int, s *goquery.Selection) {
g.Exports <- s.AttrOr("href", "")
})
@ -105,7 +105,7 @@ func TestGetRendered(t *testing.T) {
StartRequestsFunc: func(g *geziyor.Geziyor) {
g.GetRendered("https://httpbin.org/anything", g.Opt.ParseFunc)
},
ParseFunc: func(g *geziyor.Geziyor, r *geziyor.Response) {
ParseFunc: func(g *geziyor.Geziyor, r *http.Response) {
fmt.Println(string(r.Body))
fmt.Println(r.Header)
},
@ -118,7 +118,7 @@ func TestHEADRequest(t *testing.T) {
StartRequestsFunc: func(g *geziyor.Geziyor) {
g.Head("https://httpbin.org/anything", g.Opt.ParseFunc)
},
ParseFunc: func(g *geziyor.Geziyor, r *geziyor.Response) {
ParseFunc: func(g *geziyor.Geziyor, r *http.Response) {
fmt.Println(string(r.Body))
},
}).Start()
@ -127,8 +127,8 @@ func TestHEADRequest(t *testing.T) {
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 {
ParseFunc: func(g *geziyor.Geziyor, r *http.Response) {
if len(g.Client.Cookies(r.Request.URL.String())) == 0 {
t.Fatal("Cookies is Empty")
}
},
@ -136,8 +136,8 @@ 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 {
ParseFunc: func(g *geziyor.Geziyor, r *http.Response) {
if len(g.Client.Cookies(r.Request.URL.String())) != 0 {
t.Fatal("Cookies exist")
}
},
@ -148,7 +148,7 @@ func TestCookies(t *testing.T) {
func TestBasicAuth(t *testing.T) {
geziyor.NewGeziyor(&geziyor.Options{
StartRequestsFunc: func(g *geziyor.Geziyor) {
req, _ := geziyor.NewRequest("GET", "https://httpbin.org/anything", nil)
req, _ := http.NewRequest("GET", "https://httpbin.org/anything", nil)
req.SetBasicAuth("username", "password")
g.Do(req, nil)
},
@ -170,14 +170,14 @@ func TestExtractor(t *testing.T) {
}
func TestCharsetDetection(t *testing.T) {
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
ts := httptest.NewServer(httpstd.HandlerFunc(func(w httpstd.ResponseWriter, r *httpstd.Request) {
fmt.Fprint(w, "\xf0ültekin")
}))
defer ts.Close()
geziyor.NewGeziyor(&geziyor.Options{
StartURLs: []string{ts.URL},
ParseFunc: func(g *geziyor.Geziyor, r *geziyor.Response) {
ParseFunc: func(g *geziyor.Geziyor, r *http.Response) {
if !utf8.Valid(r.Body) {
t.Fatal()
}
@ -187,7 +187,7 @@ func TestCharsetDetection(t *testing.T) {
geziyor.NewGeziyor(&geziyor.Options{
StartURLs: []string{ts.URL},
ParseFunc: func(g *geziyor.Geziyor, r *geziyor.Response) {
ParseFunc: func(g *geziyor.Geziyor, r *http.Response) {
if utf8.Valid(r.Body) {
t.Fatal()
}
@ -200,10 +200,10 @@ func TestCharsetDetection(t *testing.T) {
func BenchmarkGeziyor_Do(b *testing.B) {
// Create Server
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
ts := httptest.NewServer(httpstd.HandlerFunc(func(w httpstd.ResponseWriter, r *httpstd.Request) {
fmt.Fprint(w, "Hello, client")
}))
ts.Client().Transport = http2.NewClient().Transport
ts.Client().Transport = http.NewClient().Transport
defer ts.Close()
// As we don't benchmark creating a server, reset timer.
@ -212,7 +212,7 @@ func BenchmarkGeziyor_Do(b *testing.B) {
geziyor.NewGeziyor(&geziyor.Options{
StartRequestsFunc: func(g *geziyor.Geziyor) {
// Create Synchronized request to benchmark requests accurately.
req, _ := geziyor.NewRequest("GET", ts.URL, nil)
req, _ := http.NewRequest("GET", ts.URL, nil)
req.Synchronized = true
// We only bench here !

View File

@ -1,4 +1,4 @@
package geziyor
package http
import (
"io"
@ -11,13 +11,12 @@ type Request struct {
Meta map[string]interface{}
Synchronized bool
Rendered bool
cancelled bool
Cancelled bool
}
// Cancel request
func (r *Request) Cancel() {
r.cancelled = true
r.Cancelled = true
}
// NewRequest returns a new Request given a method, URL, and optional body.

View File

@ -1,4 +1,4 @@
package geziyor
package http
import (
"github.com/PuerkitoBio/goquery"
@ -28,7 +28,8 @@ func (r *Response) JoinURL(relativeURL string) string {
return joinedURL.String()
}
func (r *Response) isHTML() bool {
// IsHTML checks if response content is HTML by looking to content-type header
func (r *Response) IsHTML() bool {
contentType := r.Header.Get("Content-Type")
for _, htmlContentType := range []string{"text/html", "application/xhtml+xml", "application/vnd.wap.xhtml+xml"} {
if strings.Contains(contentType, htmlContentType) {

View File

@ -16,10 +16,10 @@ import (
// RequestMiddleware called before requests made.
// Set request.Cancelled = true to cancel request
type RequestMiddleware func(g *Geziyor, r *Request)
type RequestMiddleware func(g *Geziyor, r *http.Request)
// ResponseMiddleware called after request response receive
type ResponseMiddleware func(g *Geziyor, r *Response)
type ResponseMiddleware func(g *Geziyor, r *http.Response)
func init() {
log.SetOutput(os.Stdout)
@ -28,7 +28,7 @@ func init() {
// recoverMiddleware recovers scraping being crashed.
// Logs error and stack trace
func recoverMiddleware(g *Geziyor, r *Request) {
func recoverMiddleware(g *Geziyor, r *http.Request) {
if r := recover(); r != nil {
log.Println(r, string(debug.Stack()))
g.metrics.PanicCounter.Add(1)
@ -36,7 +36,7 @@ func recoverMiddleware(g *Geziyor, r *Request) {
}
// allowedDomainsMiddleware checks for request host if it exists in AllowedDomains
func allowedDomainsMiddleware(g *Geziyor, r *Request) {
func allowedDomainsMiddleware(g *Geziyor, r *http.Request) {
if len(g.Opt.AllowedDomains) != 0 && !internal.Contains(g.Opt.AllowedDomains, r.Host) {
//log.Printf("Domain not allowed: %s\n", req.Host)
r.Cancel()
@ -45,7 +45,7 @@ func allowedDomainsMiddleware(g *Geziyor, r *Request) {
}
// duplicateRequestsMiddleware checks for already visited URLs
func duplicateRequestsMiddleware(g *Geziyor, r *Request) {
func duplicateRequestsMiddleware(g *Geziyor, r *http.Request) {
if !g.Opt.URLRevisitEnabled {
key := r.Request.URL.String() + r.Request.Method
if _, visited := g.visitedURLs.LoadOrStore(key, struct{}{}); visited {
@ -56,7 +56,7 @@ func duplicateRequestsMiddleware(g *Geziyor, r *Request) {
}
// defaultHeadersMiddleware sets default request headers
func defaultHeadersMiddleware(g *Geziyor, r *Request) {
func defaultHeadersMiddleware(g *Geziyor, r *http.Request) {
r.Header = http.SetDefaultHeader(r.Header, "Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8")
r.Header = http.SetDefaultHeader(r.Header, "Accept-Charset", "utf-8")
r.Header = http.SetDefaultHeader(r.Header, "Accept-Language", "en")
@ -64,7 +64,7 @@ func defaultHeadersMiddleware(g *Geziyor, r *Request) {
}
// delayMiddleware delays requests
func delayMiddleware(g *Geziyor, r *Request) {
func delayMiddleware(g *Geziyor, r *http.Request) {
if g.Opt.RequestDelayRandomize {
min := float64(g.Opt.RequestDelay) * 0.5
max := float64(g.Opt.RequestDelay) * 1.5
@ -75,29 +75,29 @@ func delayMiddleware(g *Geziyor, r *Request) {
}
// logMiddleware logs requests
func logMiddleware(g *Geziyor, r *Request) {
func logMiddleware(g *Geziyor, r *http.Request) {
log.Println("Fetching: ", r.URL.String())
}
// metricsRequestMiddleware sets stats
func metricsRequestMiddleware(g *Geziyor, r *Request) {
func metricsRequestMiddleware(g *Geziyor, r *http.Request) {
g.metrics.RequestCounter.With("method", r.Method).Add(1)
}
// parseHTMLMiddleware parses response if response is HTML
func parseHTMLMiddleware(g *Geziyor, r *Response) {
if !g.Opt.ParseHTMLDisabled && r.isHTML() {
func parseHTMLMiddleware(g *Geziyor, r *http.Response) {
if !g.Opt.ParseHTMLDisabled && r.IsHTML() {
r.HTMLDoc, _ = goquery.NewDocumentFromReader(bytes.NewReader(r.Body))
}
}
// metricsResponseMiddleware sets stats
func metricsResponseMiddleware(g *Geziyor, r *Response) {
func metricsResponseMiddleware(g *Geziyor, r *http.Response) {
g.metrics.ResponseCounter.With("method", r.Request.Method).Add(1)
}
// extractorsMiddleware extracts data from loaders conf and exports it to exporters
func extractorsMiddleware(g *Geziyor, r *Response) {
func extractorsMiddleware(g *Geziyor, r *http.Response) {
// Check if we have extractors and exporters
if len(g.Opt.Extractors) != 0 && len(g.Opt.Exporters) != 0 {

View File

@ -2,6 +2,7 @@ package geziyor
import (
"github.com/fpfeng/httpcache"
"github.com/geziyor/geziyor/http"
"github.com/geziyor/geziyor/metrics"
"time"
)
@ -19,7 +20,7 @@ type Options struct {
StartRequestsFunc func(g *Geziyor)
// ParseFunc is callback of StartURLs response.
ParseFunc func(g *Geziyor, r *Response)
ParseFunc func(g *Geziyor, r *http.Response)
// Extractors extracts items from pages
Extractors []Extractor