HTTP Client can be changed now. Docs updated.
This commit is contained in:
parent
7bc782400c
commit
a64a262554
21
README.md
21
README.md
@ -58,7 +58,6 @@ func quotesParse(g *geziyor.Geziyor, r *geziyor.Response) {
|
|||||||
|
|
||||||
See [tests](https://github.com/geziyor/geziyor/blob/master/geziyor_test.go) for more usage examples.
|
See [tests](https://github.com/geziyor/geziyor/blob/master/geziyor_test.go) for more usage examples.
|
||||||
|
|
||||||
|
|
||||||
## Documentation
|
## Documentation
|
||||||
|
|
||||||
### Installation
|
### Installation
|
||||||
@ -101,7 +100,25 @@ geziyor.NewGeziyor(&geziyor.Options{
|
|||||||
}).Start()
|
}).Start()
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Exporting Data
|
||||||
|
|
||||||
|
You can export data automatically using exporters. Just send data to ```Geziyor.Exports``` chan.
|
||||||
|
[Available exporters](https://godoc.org/github.com/geziyor/geziyor/exporter)
|
||||||
|
|
||||||
|
```go
|
||||||
|
geziyor.NewGeziyor(&geziyor.Options{
|
||||||
|
StartURLs: []string{"http://quotes.toscrape.com/"},
|
||||||
|
ParseFunc: func(g *geziyor.Geziyor, r *geziyor.Response) {
|
||||||
|
r.HTMLDoc.Find("div.quote").Each(func(_ int, s *goquery.Selection) {
|
||||||
|
g.Exports <- map[string]interface{}{
|
||||||
|
"text": s.Find("span.text").Text(),
|
||||||
|
"author": s.Find("small.author").Text(),
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
Exporters: []geziyor.Exporter{&exporter.JSONExporter{}},
|
||||||
|
}).Start()
|
||||||
|
```
|
||||||
|
|
||||||
## Roadmap
|
## Roadmap
|
||||||
|
|
||||||
@ -112,6 +129,6 @@ If you're interested in helping this project, please consider these features:
|
|||||||
- Deploying Scrapers to Cloud
|
- Deploying Scrapers to Cloud
|
||||||
- ~~Automatically exporting extracted data to multiple places (AWS, FTP, DB, JSON, CSV etc)~~
|
- ~~Automatically exporting extracted data to multiple places (AWS, FTP, DB, JSON, CSV etc)~~
|
||||||
- Downloading media (Images, Videos etc) (like [this](https://docs.scrapy.org/en/latest/topics/media-pipeline.html))
|
- Downloading media (Images, Videos etc) (like [this](https://docs.scrapy.org/en/latest/topics/media-pipeline.html))
|
||||||
- Realtime metrics (Prometheus etc.)
|
- ~~Realtime metrics (Prometheus etc.)~~
|
||||||
|
|
||||||
|
|
24
geziyor.go
24
geziyor.go
@ -6,7 +6,7 @@ import (
|
|||||||
"github.com/chromedp/cdproto/network"
|
"github.com/chromedp/cdproto/network"
|
||||||
"github.com/chromedp/chromedp"
|
"github.com/chromedp/chromedp"
|
||||||
"github.com/fpfeng/httpcache"
|
"github.com/fpfeng/httpcache"
|
||||||
"github.com/geziyor/geziyor/internal"
|
"github.com/geziyor/geziyor/http"
|
||||||
"github.com/geziyor/geziyor/metrics"
|
"github.com/geziyor/geziyor/metrics"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/prometheus/client_golang/prometheus/promhttp"
|
"github.com/prometheus/client_golang/prometheus/promhttp"
|
||||||
@ -14,7 +14,7 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
stdhttp "net/http"
|
||||||
"net/http/cookiejar"
|
"net/http/cookiejar"
|
||||||
"net/url"
|
"net/url"
|
||||||
"sync"
|
"sync"
|
||||||
@ -28,7 +28,7 @@ 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
|
Client *http.Client
|
||||||
Exports chan interface{}
|
Exports chan interface{}
|
||||||
|
|
||||||
metrics *metrics.Metrics
|
metrics *metrics.Metrics
|
||||||
@ -47,7 +47,7 @@ type Geziyor struct {
|
|||||||
// 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: http.NewClient(),
|
||||||
Opt: opt,
|
Opt: opt,
|
||||||
Exports: make(chan interface{}),
|
Exports: make(chan interface{}),
|
||||||
requestMiddlewares: []RequestMiddleware{
|
requestMiddlewares: []RequestMiddleware{
|
||||||
@ -104,10 +104,10 @@ func (g *Geziyor) Start() {
|
|||||||
log.Println("Scraping Started")
|
log.Println("Scraping Started")
|
||||||
|
|
||||||
// Metrics
|
// Metrics
|
||||||
metricsServer := &http.Server{Addr: ":2112"}
|
metricsServer := &stdhttp.Server{Addr: ":2112"}
|
||||||
if g.Opt.MetricsType == metrics.Prometheus {
|
if g.Opt.MetricsType == metrics.Prometheus {
|
||||||
go func() {
|
go func() {
|
||||||
http.Handle("/metrics", promhttp.Handler())
|
stdhttp.Handle("/metrics", promhttp.Handler())
|
||||||
metricsServer.ListenAndServe()
|
metricsServer.ListenAndServe()
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
@ -141,7 +141,7 @@ func (g *Geziyor) Start() {
|
|||||||
|
|
||||||
// Get issues a GET to the specified URL.
|
// 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 *Response)) {
|
||||||
req, err := http.NewRequest("GET", url, nil)
|
req, err := stdhttp.NewRequest("GET", url, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Request creating error %v\n", err)
|
log.Printf("Request creating error %v\n", err)
|
||||||
return
|
return
|
||||||
@ -153,7 +153,7 @@ func (g *Geziyor) Get(url string, callback func(g *Geziyor, r *Response)) {
|
|||||||
// Opens up a new Chrome instance, makes request, waits for 1 second to render HTML DOM and closed.
|
// 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.
|
// 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 *Response)) {
|
||||||
req, err := http.NewRequest("GET", url, nil)
|
req, err := stdhttp.NewRequest("GET", url, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Request creating error %v\n", err)
|
log.Printf("Request creating error %v\n", err)
|
||||||
return
|
return
|
||||||
@ -163,7 +163,7 @@ func (g *Geziyor) GetRendered(url string, callback func(g *Geziyor, r *Response)
|
|||||||
|
|
||||||
// Head issues a HEAD to the specified URL
|
// 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 *Response)) {
|
||||||
req, err := http.NewRequest("HEAD", url, nil)
|
req, err := stdhttp.NewRequest("HEAD", url, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Request creating error %v\n", err)
|
log.Printf("Request creating error %v\n", err)
|
||||||
return
|
return
|
||||||
@ -265,7 +265,7 @@ func (g *Geziyor) doRequestChrome(req *Request) (*Response, error) {
|
|||||||
|
|
||||||
if err := chromedp.Run(ctx,
|
if err := chromedp.Run(ctx,
|
||||||
network.Enable(),
|
network.Enable(),
|
||||||
network.SetExtraHTTPHeaders(network.Headers(internal.ConvertHeaderToMap(req.Header))),
|
network.SetExtraHTTPHeaders(network.Headers(http.ConvertHeaderToMap(req.Header))),
|
||||||
chromedp.ActionFunc(func(ctx context.Context) error {
|
chromedp.ActionFunc(func(ctx context.Context) error {
|
||||||
chromedp.ListenTarget(ctx, func(ev interface{}) {
|
chromedp.ListenTarget(ctx, func(ev interface{}) {
|
||||||
switch ev.(type) {
|
switch ev.(type) {
|
||||||
@ -299,10 +299,10 @@ func (g *Geziyor) doRequestChrome(req *Request) (*Response, error) {
|
|||||||
req.URL, _ = url.Parse(res.URL)
|
req.URL, _ = url.Parse(res.URL)
|
||||||
|
|
||||||
response := Response{
|
response := Response{
|
||||||
Response: &http.Response{
|
Response: &stdhttp.Response{
|
||||||
Request: req.Request,
|
Request: req.Request,
|
||||||
StatusCode: int(res.Status),
|
StatusCode: int(res.Status),
|
||||||
Header: internal.ConvertMapToHeader(res.Headers),
|
Header: http.ConvertMapToHeader(res.Headers),
|
||||||
},
|
},
|
||||||
Body: []byte(body),
|
Body: []byte(body),
|
||||||
Meta: req.Meta,
|
Meta: req.Meta,
|
||||||
|
@ -46,7 +46,7 @@ func TestQuotes(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func quotesParse(g *geziyor.Geziyor, r *geziyor.Response) {
|
func quotesParse(g *geziyor.Geziyor, r *geziyor.Response) {
|
||||||
r.DocHTML.Find("div.quote").Each(func(i int, s *goquery.Selection) {
|
r.HTMLDoc.Find("div.quote").Each(func(i int, s *goquery.Selection) {
|
||||||
// Export Data
|
// Export Data
|
||||||
g.Exports <- map[string]interface{}{
|
g.Exports <- map[string]interface{}{
|
||||||
"number": i,
|
"number": i,
|
||||||
@ -59,7 +59,7 @@ func quotesParse(g *geziyor.Geziyor, r *geziyor.Response) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
// Next Page
|
// Next Page
|
||||||
if href, ok := r.DocHTML.Find("li.next > a").Attr("href"); ok {
|
if href, ok := r.HTMLDoc.Find("li.next > a").Attr("href"); ok {
|
||||||
g.Get(r.JoinURL(href), quotesParse)
|
g.Get(r.JoinURL(href), quotesParse)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -72,13 +72,14 @@ func TestAllLinks(t *testing.T) {
|
|||||||
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) {
|
||||||
g.Exports <- []string{r.Request.URL.String()}
|
g.Exports <- []string{r.Request.URL.String()}
|
||||||
r.DocHTML.Find("a").Each(func(i int, s *goquery.Selection) {
|
r.HTMLDoc.Find("a").Each(func(i int, s *goquery.Selection) {
|
||||||
if href, ok := s.Attr("href"); ok {
|
if href, ok := s.Attr("href"); ok {
|
||||||
g.Get(r.JoinURL(href), g.Opt.ParseFunc)
|
g.Get(r.JoinURL(href), g.Opt.ParseFunc)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
Exporters: []geziyor.Exporter{&exporter.CSVExporter{}},
|
Exporters: []geziyor.Exporter{&exporter.CSVExporter{}},
|
||||||
|
MetricsType: metrics.Prometheus,
|
||||||
}).Start()
|
}).Start()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -97,7 +98,7 @@ func TestStartRequestsFunc(t *testing.T) {
|
|||||||
g.Get("http://quotes.toscrape.com/", g.Opt.ParseFunc)
|
g.Get("http://quotes.toscrape.com/", g.Opt.ParseFunc)
|
||||||
},
|
},
|
||||||
ParseFunc: func(g *geziyor.Geziyor, r *geziyor.Response) {
|
ParseFunc: func(g *geziyor.Geziyor, r *geziyor.Response) {
|
||||||
r.DocHTML.Find("a").Each(func(_ int, s *goquery.Selection) {
|
r.HTMLDoc.Find("a").Each(func(_ int, s *goquery.Selection) {
|
||||||
g.Exports <- s.AttrOr("href", "")
|
g.Exports <- s.AttrOr("href", "")
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
package internal
|
package http
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
@ -3,6 +3,7 @@ package geziyor
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"github.com/PuerkitoBio/goquery"
|
"github.com/PuerkitoBio/goquery"
|
||||||
|
"github.com/geziyor/geziyor/http"
|
||||||
"github.com/geziyor/geziyor/internal"
|
"github.com/geziyor/geziyor/internal"
|
||||||
"log"
|
"log"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
@ -53,10 +54,10 @@ func duplicateRequestsMiddleware(g *Geziyor, r *Request) {
|
|||||||
|
|
||||||
// defaultHeadersMiddleware sets default request headers
|
// defaultHeadersMiddleware sets default request headers
|
||||||
func defaultHeadersMiddleware(g *Geziyor, r *Request) {
|
func defaultHeadersMiddleware(g *Geziyor, r *Request) {
|
||||||
r.Header = internal.SetDefaultHeader(r.Header, "Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8")
|
r.Header = http.SetDefaultHeader(r.Header, "Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8")
|
||||||
r.Header = internal.SetDefaultHeader(r.Header, "Accept-Charset", "utf-8")
|
r.Header = http.SetDefaultHeader(r.Header, "Accept-Charset", "utf-8")
|
||||||
r.Header = internal.SetDefaultHeader(r.Header, "Accept-Language", "en")
|
r.Header = http.SetDefaultHeader(r.Header, "Accept-Language", "en")
|
||||||
r.Header = internal.SetDefaultHeader(r.Header, "User-Agent", g.Opt.UserAgent)
|
r.Header = http.SetDefaultHeader(r.Header, "User-Agent", g.Opt.UserAgent)
|
||||||
}
|
}
|
||||||
|
|
||||||
// delayMiddleware delays requests
|
// delayMiddleware delays requests
|
||||||
@ -83,7 +84,7 @@ func metricsRequestMiddleware(g *Geziyor, r *Request) {
|
|||||||
// parseHTMLMiddleware parses response if response is HTML
|
// parseHTMLMiddleware parses response if response is HTML
|
||||||
func parseHTMLMiddleware(g *Geziyor, r *Response) {
|
func parseHTMLMiddleware(g *Geziyor, r *Response) {
|
||||||
if !g.Opt.ParseHTMLDisabled && r.isHTML() {
|
if !g.Opt.ParseHTMLDisabled && r.isHTML() {
|
||||||
r.DocHTML, _ = goquery.NewDocumentFromReader(bytes.NewReader(r.Body))
|
r.HTMLDoc, _ = goquery.NewDocumentFromReader(bytes.NewReader(r.Body))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -12,7 +12,7 @@ import (
|
|||||||
type Response struct {
|
type Response struct {
|
||||||
*http.Response
|
*http.Response
|
||||||
Body []byte
|
Body []byte
|
||||||
DocHTML *goquery.Document
|
HTMLDoc *goquery.Document
|
||||||
Meta map[string]interface{}
|
Meta map[string]interface{}
|
||||||
Request *Request
|
Request *Request
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user