其他分享
首页 > 其他分享> > Go语言实战-爬取整部小说

Go语言实战-爬取整部小说

作者:互联网

Go语言的魅力这里就不多说了,这接上代码?

先简单介绍下爬虫用到的第三方库:

  1. github.com/gocolly/colly:轻量而又强大的爬虫框架;
  2. github.com/PuerkitoBio/goquery:Go语言版的jQuery;
  3. github.com/mozillazg/request:一个简单、优雅的请求库。

 

接下来,根据小说名和作者爬取笔趣阁整部小说,并保存在TXT文本文件中。

笔趣阁:https://www.xbiquge.la/modules/article/waps.php

小说首页:

 

找到每章小说的下一章链接: 

 

 

以上几个链接为爬虫的关键点,colly框架需要startURL来启动爬虫,需要nextURL来驱动爬虫往下爬取。

详细代码:

package main

import (
	"fmt"
	"github.com/PuerkitoBio/goquery"
	"github.com/gocolly/colly"
	"github.com/gocolly/colly/extensions"
	"github.com/mozillazg/request"
	"io"
	"log"
	"net"
	"net/http"
	"os"
	"strconv"
	"strings"
	"time"
)

func main() {
	startUrl, err := findStartUrl("圣墟", "辰东")
	if err != nil {
		log.Fatal(err)
	}
	println(startUrl)

	crawl(startUrl, "test.txt")
}

// findStartUrl 根据小说名和作者找到 colly 的 startUrl
func findStartUrl(name string, author string) (string, error) {

	var targetUrl string
	var Err error

	// request请求库常规用法,用来发起请求,获取响应
	c := new(http.Client)
	req := request.NewRequest(c)
	req.Headers = map[string]string{
		"Accept-Encoding": "gzip,deflate,sdch",
		"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
	}
	req.Data = map[string]string{
		"searchkey": name,
	}
	url := "https://www.xbiquge.la/modules/article/waps.php"
	resp, _ := req.Post(url)
	defer resp.Body.Close()  // Don't forget close the response body

	html, err := resp.Text()
	if err != nil {
		Err = err
		log.Fatal(err)
	}

	// goquery 常规用法
	dom, _ := goquery.NewDocumentFromReader(strings.NewReader(html))
	dom.Find("tr").EachWithBreak(func(i int, s *goquery.Selection) bool {
		_title := s.Find("a").Eq(0).Text()
		_author := s.Find("td").Eq(2).Text()
		// 找到匹配的链接
		if _title == name && _author == author {
			url, exists := s.Find("a").Eq(0).Attr("href")
			if !exists {
				Err = nil
				log.Fatal(exists)
			}
			targetUrl = url
		}
		return true
	})

	// 根据小说的首页,获取第一个章节的页面的链接
	resp2, err2 := req.Get(targetUrl)
	if err2 != nil {
		log.Fatalln("get start url request fail...")
	}
	text, _ := resp2.Text()
	dom2, _ := goquery.NewDocumentFromReader(strings.NewReader(text))
	startUrl, ok:= dom2.Find("#list").Find("a").Eq(0).Attr("href")
	if !ok {
		log.Fatalln("get start url fail...")
	}

	return "https://www.xbiquge.la" + startUrl, Err
}

// 爬取小说内容并写入text文件
func crawl(startUrl string, fileName string) {
	c := colly.NewCollector(
		// allowed domain
		colly.AllowedDomains("www.xbiquge.la", "www.coursera.org"),
		//colly.AllowURLRevisit(),
	)

	// 使用随机user-agent
	extensions.RandomUserAgent(c)

	// HTTP 的配置
	c.WithTransport(&http.Transport{
		Proxy: http.ProxyFromEnvironment, // 使用代理
		DialContext: (&net.Dialer{
			Timeout:   30 * time.Second, // 超时时间
			KeepAlive: 30 * time.Second, // keepAlive 超时时间
			DualStack: true,
		}).DialContext,
		MaxIdleConns:          100,              // 最大空闲连接数
		IdleConnTimeout:       90 * time.Second, // 空闲连接超时
		TLSHandshakeTimeout:   10 * time.Second, // TLS 握手超时
		ExpectContinueTimeout: 1 * time.Second,
	})

	// 设置请求信息
	c.OnRequest(func(r *colly.Request) {
		r.Headers.Set("Host", "www.xbiquge.la")
		r.Headers.Set("X-Requested-With", "XMLHttpRequest")
		r.Headers.Set("Referer", "https://www.xbiquge.la/13/13959/24775996.html")
		r.Headers.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3314.0 Safari/537.36 SE 2.X MetaSr 1.0")

		log.Println("Visiting", r.URL)
	})

	c.OnResponse(func(r *colly.Response) {
		log.Println("response received", r.StatusCode)
	})

	// 解析章节内容 并 写入文本文件
	c.OnHTML("div[id='content']", func(e *colly.HTMLElement) {
		// title
		title := e.DOM.ParentsUntil("body").Find(".bookname h1").Text() + "\n\n"

		println(title)

		// content
		e.DOM.Find("p").Remove()
		content := e.DOM.Text()
		str := title + content

		writeToText(str, fileName)
	})

	// 翻页
	c.OnHTML("a[href]", func(e *colly.HTMLElement) {
		e.DOM.EachWithBreak(func(i int, s *goquery.Selection) bool {
			if "下一章" == s.Text() {
				baseUrl, _ := s.Attr("href")
				url := "https://www.xbiquge.la" + baseUrl

				c.Visit(url)
				return false
			}
			return true
		})
	})

	c.OnError(func(r *colly.Response, err error) {
		log.Println("error:", r.StatusCode, err)
	})

	c.Visit(startUrl)

}

// 将字符串,写入文本文件
func writeToText(str string, fileName string) {
	wd, _ := os.Getwd()
	filePath := wd + "/src/fiction-master/file/" + fileName  // 存放小说的TXT文件路径
	f, err := os.OpenFile(filePath, os.O_APPEND|os.O_WRONLY, 0644)  // 以追加方式写入
	if err != nil {
		log.Fatal(err)
	}
	defer func() {
		if err = f.Close(); err != nil {
			log.Fatal(err)
		}
	}()
	_, err = fmt.Fprintln(f, str)
	if err != nil {
		log.Fatal(err)
	}
}

 最终截图:

 整部小说也就是几分钟的事

 

Go语言的魅力远不止于此,本人也是刚学习Go语言,且行且探索

记录一下一个特殊的时间2021年5月1日

 

标签:func,string,err,爬取,整部,Go,Find,colly,log
来源: https://blog.csdn.net/qq_36034503/article/details/117637038