Skip to content

Latest commit

 

History

History
147 lines (110 loc) · 4.24 KB

extra.md

File metadata and controls

147 lines (110 loc) · 4.24 KB

很多时候,我也会在社区里面看一些机会,但是天天都是那几个动作:

打开社区a,b,c,点击招聘板块,看看最新的帖子。

为什么不让看帖自动化呢?

我只会 JS 和 Go,Go 语言更适合这个工作,况且我出差用的小米笔记本都有2核4线程,不利用一下真是太傻了。

首先是 main 函数

func main() {
	results := make(chan *Result)

	var wg sync.WaitGroup
	wg.Add(len(sites))

	for _, site := range sites {
		matcher, ok := matchers[site.resType]
		if !ok {
			matcher = matchers["default"]
		}

		go func(matcher Matcher, url string) {
			err := doMatch(matcher, url, results)
			if err != nil {
				log.Println(err)
			}
			wg.Done()
		}(matcher, site.url)
	}

	go func() {
		wg.Wait()

		close(results)
	}()

	display(results)
}

如果你不会 Go 的话,这里 go 关键字,chan 关键字还有 sync.WaitGroup 大约就是会帮助你创建新的线程,同步结果。

我这里有一个 results 的 channel 同步结果,wg 指示搜索帖子的线程的结束。 然后我遍历了我要访问的社区链接,并且对社区返回的结果做解析。还有一个线程负责同步所有结果,最后在命令行输出结果。

针对不同的网站要有不同的解析方案。所以这就有了 matcher 接口,定义如下:

type Matcher interface {
	match(reader io.Reader) ([]*Result, error)
}

matcher 接收的参数是 io.Reader,大约就相当于 JS 里面可以传任意参数吧,或者说就是最灵活的写法之一了。

对于 cnode 社区这样有提供 restful 接口的,自然是要解析 json 了。

type CNodeTopic struct {
	Title string `json:"title"`
	CreateAt time.Time `json:"create_at"`
	Content string `json:"content"`
}

type CNodeResp struct {
	Success bool `json:"success"`
	Data []CNodeTopic `json:"data"`
}

type CNodeJSON struct {}

cnode 的每一个话题有好几个属性,我就只挑我要的了。

然后是解析:

func (CNodeJSON) match(reader io.Reader) ([]*Result, error) {
	resp, err := ioutil.ReadAll(reader)
	if err != nil {
		return nil, err
	}
	cnodeResp := CNodeResp{}
	if err = json.Unmarshal(resp, &cnodeResp); err != nil {
		return nil, err
	}

	if !cnodeResp.Success || cnodeResp.Data == nil {
		return nil, fmt.Errorf("no response")
	}

	ret := make([]*Result, 0)

	for _, topic := range cnodeResp.Data {
		if time.Since(topic.CreateAt).Nanoseconds() - time.Hour.Nanoseconds() * 24 * dayLimit > 0 {
			continue
		}

		ret = append(ret, &Result{title: topic.Title, email: emailRe.FindString(topic.Content), content:topic.Content})
	}

	return ret, nil
}

各种日常解析,出错就 return。如果一切都正常,那么自然是要判断发帖时间。是新帖子就加入到结果当中,没啥可说的。

但是大多数网站可没有 cnode 那么方便了,必须解析 html。

所以我就拿我 studygolang.com 举个栗子。 首先必须引用 goquery,它是一个类似 jQuery 或者说是更像 Node 里面的 cheerio 的工具,不用这个的话就要自己递归搜索 html 节点了。。。 go get "github.com/PuerkitoBio/goquery"

然后是解析:

type StudyGolangHTML struct {}

func (StudyGolangHTML) match(reader io.Reader) ([]*Result, error) {
	doc, err := goquery.NewDocumentFromReader(reader)
	if err != nil {
		return nil, err
	}

	ret := make([]*Result, 0)

	doc.Find(".topic").Each(func(i int, selection *goquery.Selection) {
		abbr := selection.Find("abbr")
		timeStr, _ := abbr.Attr("title")
		t, err := time.Parse("2006-01-02 15:04:05", timeStr)
		if err != nil {
			return
		}
		if time.Since(t).Nanoseconds() - time.Hour.Nanoseconds() * 24 * dayLimit > 0 {
			return
		}

		link := selection.Find(".title a")

		ret = append(ret, &Result{title: link.Text(), email: "", content:link.AttrOr("href", "")})
	})

	return ret, nil
}

studygolang.com 的每一个主题的节点都可以用 .topic 选中,时间在 abbr 标签中,而标题和链接都在 .title a 下。

如果你有自己想要搜索的网站,比如 rust-china,kotlin-china 什么的,一般都还是 json 或者 html 的解析,应该不会很难适配。