Go分布式爬虫学习笔记(十二)

生活中,最使人疲惫的往往不是道路的遥远,而是心中的郁闷;最使人痛苦的往往不是生活的不幸,而是希望的破灭;最使人颓废的往往不是前途的坎坷,而是自信的丧失;最使人绝望的往往不是挫折的打击,而是心灵的死亡。所以我们要有自己的梦想,让梦想的星光指引着我们走出落漠,走出惆怅,带着我们走进自己的理想。

导读:本篇文章讲解 Go分布式爬虫学习笔记(十二),希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com,来源:原文

12_四种文本处理

获取澎拜新闻内容

package main

import (
	"fmt"
	"io"
	"net/http"
)

func main() {
	url := "https://www.thepaper.cn/"
	resp, err := http.Get(url)
	if err != nil {
		fmt.Println("fetcjh url error:%v", err)
		return
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		fmt.Println("Error status code:%v", resp.StatusCode)
		return
	}

	body, err := io.ReadAll(resp.Body)
	if err != nil {
		fmt.Println("read content failed:%v", err)
		return
	}

	fmt.Println("body:", string(body))

}

字符串

  • Go 语言提供了 strings 标准库用于字符处理函数。如下所示,在标准库 strings 包中,包含字符查找、分割、大小写转换、修剪(trim)、计算字符出现次数等数十个函数。go1.20.2 strings doc

    // 判断字符串s 是否包含substr 字符串
    func Contains(s, substr string) bool
    // 判断字符串s 是否包含chars 字符串中的任一字符
    func ContainsAny(s, chars string) bool
    // 判断字符串s 是否包含符文数r
    func ContainsRune(s string, r rune) bool
    // 将字符串s 以空白字符分割,返回一个切片
    func Fields(s string) []string
    // 将字符串s 以满足f(r)==true 的字符分割,返回一个切片
    func FieldsFunc(s string, f func(rune) bool) []string
    // 将字符串s 以sep 为分隔符进行分割,分割后字符末尾去掉sep
    func Split(s, sep string) []string
    
  • strconv提供类型转化的函数

    // 字符串转换为十进制整数
    func Atoi(s string) (int, error)
    // 字符串转换为某一进制的整数,例如八进制、十六进制
    func ParseInt(s string, base int, bitSize int) (i int64, err error)
    // 整数转换为字符串
    func Itoa(i int) string
    // 某一进制的整数转换为字符串,例如八进制整数转换为字符串
    func FormatInt(i int64, base int) string
    

字符编码

  • 在 Go 语言中,字符串是默认通过 UTF-8 的形式编码的。

  • 目前大多数网站都使用 UTF-8 编码,但其实服务器发送过来的 HTML 文本可能拥有很多编码形式。一些国内的网站就会采用 GB2312 的编码方式。

  • 要实现编码的通用性,我们使用官方处理字符集的库:

    go get golang.org/x/net/html/charset
    go get golang.org/x/text/encoding
    
  • R html DetermineEncoding算法

封装编码和获取网页内容

package main

import (
	"bufio"
	"fmt"
	"github.com/funbinary/go_example/pkg/errors"
	"golang.org/x/net/html/charset"
	"golang.org/x/text/encoding"
	"golang.org/x/text/encoding/unicode"
	"golang.org/x/text/transform"
	"io"
	"net/http"
)

func main() {
	//url := "https://www.thepaper.cn/"
	url := "https://www.chinanews.com.cn/"
	body, err := Fetch(url)

	if err != nil {
		fmt.Println("read content failed:%v", err)
		return
	}

	fmt.Println(string(body))
}

func Fetch(url string) ([]byte, error) {
	var e encoding.Encoding
	resp, err := http.Get(url)

	if err != nil {
		return nil, errors.Wrap(err, "fetch url error")
	}

	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		return nil, errors.Errorf("Error status code:%v", resp.StatusCode)
	}

	r := bufio.NewReader(resp.Body)
	e, err = DeterminEncoding(r)
	utf8r := transform.NewReader(r, e.NewDecoder())
	return io.ReadAll(utf8r)
}

func DeterminEncoding(r *bufio.Reader) (encoding.Encoding, error) {
	bytes, err := r.Peek(1024)
	if err != nil {
		return unicode.UTF8, err
	}

	e, _, _ := charset.DetermineEncoding(bytes, "")
	return e, nil
}

正则表达式

正则表达式是一种描述文本内容组成规律的表示方式,它可以描述或者匹配符合相应规则的字符串。

用途

  • 校验数据的有效性
  • 搜索
  • 替换特定模式的字符串

标准

  • POSIX

    • BRE

    • ERE

    • Linux 和 Mac 在原生集成 GNU 套件(例如 grep 命令)时,遵循了 POSIX 标准,并弱化了 GNU BRE 和 GNU ERE 之间的区别。

    • GNU BRE 和 GNU ERE 的差别主要体现在一些语法字符是否需要转义上

      # BRE
      > echo  "addf" | grep  'd\{1,3\}'
      # ERE 需要使用-E参数
      > echo  "addf" | grep -E 'd{1,3}'
      
  • PCRE

    • perl演进

    • 大部分高级语言,包括Go使用

    • grep要使用 -P

      # 使用 ERE 标准
      > echo  "11d23a" | grep -E '[[:digit:]]+' 
      
      # 使用 PCRE 标准
      > echo  "11d23a" | grep -P '\d+'
      
    • R Perl regular expressions

获取推荐页面标题

​[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-aKEIRy1k-1679758861177)(https://assets.b3logfile.com/siyuan/1658627274984/assets/image-20230325230448-leqouxs.png)]​​

image

package main

import (
	"bufio"
	"fmt"
	"github.com/funbinary/go_example/pkg/errors"
	"golang.org/x/net/html/charset"
	"golang.org/x/text/encoding"
	"golang.org/x/text/encoding/unicode"
	"golang.org/x/text/transform"
	"io"
	"net/http"
	"regexp"
)

// var headerRe = regexp.MustCompile(`<div class="news_li"[\s\S]*?<h2>[\s\S]*?<a.*?target="_blank">([\s\S]*?)</a>`)
var headerRe = regexp.MustCompile(`<div class="small_cardcontent__BTALp"[\s\S]*?<h2>([\s\S]*?)</h2>`)

func main() {
	url := "https://www.thepaper.cn/"
	body, err := Fetch(url)

	if err != nil {
		fmt.Println("read content failed:%v", err)
		return
	}
	matches := headerRe.FindAllSubmatch(body, -1)
	for _, m := range matches {
		fmt.Println("fetch card news:", string(m[1]))
	}
}

func Fetch(url string) ([]byte, error) {
	var e encoding.Encoding
	resp, err := http.Get(url)

	if err != nil {
		return nil, errors.Wrap(err, "fetch url error")
	}

	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		return nil, errors.Errorf("Error status code:%v", resp.StatusCode)
	}

	r := bufio.NewReader(resp.Body)
	e, err = DeterminEncoding(r)
	utf8r := transform.NewReader(r, e.NewDecoder())
	return io.ReadAll(utf8r)
}

func DeterminEncoding(r *bufio.Reader) (encoding.Encoding, error) {
	bytes, err := r.Peek(1024)
	if err != nil {
		return unicode.UTF8, err
	}

	e, _, _ := charset.DetermineEncoding(bytes, "")
	return e, nil
}

Xpath

XPath 定义了一种遍历 XML 文档中节点层次结构,并返回匹配元素的灵活方法。而 XML 是一种可扩展标记语言,是表示结构化信息的一种规范。例如,微软办公软件 Words 在 2007 之后的版本的底层数据就是通过 XML 文件描述的。HTML 虽然不是严格意义的 XML,但是它的结构和 XML 类似。

R: htmlquery xpath语法

获取推荐页面标题

package main

import (
	"bufio"
	"bytes"
	"fmt"
	"github.com/antchfx/htmlquery"
	"github.com/funbinary/go_example/pkg/errors"
	"golang.org/x/net/html/charset"
	"golang.org/x/text/encoding"
	"golang.org/x/text/encoding/unicode"
	"golang.org/x/text/transform"
	"io"
	"net/http"
)

// xpath
func main() {
	url := "https://www.thepaper.cn/"
	body, err := Fetch(url)

	if err != nil {
		fmt.Println("read content failed:%v", err)
		return
	}
	doc, err := htmlquery.Parse(bytes.NewBuffer(body))
	if err != nil {
		fmt.Println("htmlquery.Parse failed:%v", err)
	}
	nodes := htmlquery.Find(doc, `//div[@class="small_cardcontent__BTALp"]//h2`)
	for _, node := range nodes {
		fmt.Println("fetch card news:", node.FirstChild.Data)
	}
}

func Fetch(url string) ([]byte, error) {
	var e encoding.Encoding
	resp, err := http.Get(url)

	if err != nil {
		return nil, errors.Wrap(err, "fetch url error")
	}

	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		return nil, errors.Errorf("Error status code:%v", resp.StatusCode)
	}

	r := bufio.NewReader(resp.Body)
	e, err = DeterminEncoding(r)
	utf8r := transform.NewReader(r, e.NewDecoder())
	return io.ReadAll(utf8r)
}

func DeterminEncoding(r *bufio.Reader) (encoding.Encoding, error) {
	bytes, err := r.Peek(1024)
	if err != nil {
		return unicode.UTF8, err
	}

	e, _, _ := charset.DetermineEncoding(bytes, "")
	return e, nil
}

CSS选择器

CSS(层叠式样式表)是一种定义 HTML 文档中元素样式的语言。在 CSS 文件中,我们可以定义一个或多个 HTML 中的标签的路径,并指定这些标签的样式。在 CSS 中,定义标签路径的方法被称为 CSS 选择器。

CSS 选择器考虑到了我们在搜索 HTML 文档时常用的属性。我们前面在 XPath 例子的中使用的 div[@class=“news_li”],在 CSS 选择器中可以简单地表示为 div.news_li。这是一种更加简单的表示方法。

R: CSS 选择器的语法 goquery

package main

import (
	"bufio"
	"bytes"
	"fmt"
	"github.com/PuerkitoBio/goquery"
	"github.com/funbinary/go_example/pkg/errors"
	"golang.org/x/net/html/charset"
	"golang.org/x/text/encoding"
	"golang.org/x/text/encoding/unicode"
	"golang.org/x/text/transform"
	"io"
	"net/http"
)

// xpath
func main() {
	url := "https://www.thepaper.cn/"
	body, err := Fetch(url)

	if err != nil {
		fmt.Println("read content failed:%v", err)
		return
	}
	doc, err := goquery.NewDocumentFromReader(bytes.NewBuffer(body))
	if err != nil {
		fmt.Println("htmlquery.Parse failed:%v", err)
	}
	doc.Find("div.small_cardcontent__BTALp h2").Each(func(i int, selection *goquery.Selection) {
		title := selection.Text()
		fmt.Println("fetch card:", i, " ", title)
	})

}

func Fetch(url string) ([]byte, error) {
	var e encoding.Encoding
	resp, err := http.Get(url)

	if err != nil {
		return nil, errors.Wrap(err, "fetch url error")
	}

	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		return nil, errors.Errorf("Error status code:%v", resp.StatusCode)
	}

	r := bufio.NewReader(resp.Body)
	e, err = DeterminEncoding(r)
	utf8r := transform.NewReader(r, e.NewDecoder())
	return io.ReadAll(utf8r)
}

func DeterminEncoding(r *bufio.Reader) (encoding.Encoding, error) {
	bytes, err := r.Peek(1024)
	if err != nil {
		return unicode.UTF8, err
	}

	e, _, _ := charset.DetermineEncoding(bytes, "")
	return e, nil
}

思考题

在类似如下的日志文件中,包含了很多订单号的信息,即 order_id 后面的一串数字。我们可以用什么方式,将 order_id 后面的订单 ID 都像下面这样打印出来?(提示:用 grep 就可以做到)

2022-11-22  name=versionReport||order_id=3732978217343693673||trace_id=XXX
2022-11-22  name=versionReport||order_id=3732978217343693674||trace_id=XXX
grep -oP 'order_id=\d+'|grep -oP '\d+'
grep -oE 'order_id=\d+' | grep -oE '\d+' 


「此文章为3月Day12学习笔记,内容来源于极客时间《Go分布式爬虫实战》,强烈推荐该课程!/推荐该课程」

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/137592.html

(0)
飞熊的头像飞熊bm

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!