Skip to content

Instantly share code, notes, and snippets.

@ieee0824
Last active December 7, 2015 09:32
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ieee0824/370a35622b6c330c5015 to your computer and use it in GitHub Desktop.
Save ieee0824/370a35622b6c330c5015 to your computer and use it in GitHub Desktop.

goで読売新聞の新着を取得します

こんにちは高知工科大Advent Calendar 2015の7日目担当です.
今回は就活っぽいお話をします.
就活のことはよくわからないのでイメージで書いていきます.
就活の時って時事ネタでニュースのことを聞かれるイメージが有ります.
ただ自分は新聞を購読していませんしテレビも持っていませんなのでニュースを手に入れるとしたら インターネットニュースになります.
91f95601-4604-48c4-c8cc-701c0cabaaaa
意識高い学生になるべくgo言語を使ってニュースを取得してみようと思います.

どこから取得するか?

今回は読売新聞onlineから取得します.
ニュースには新しさが求められるので新着から取得します.

[読売新聞新着](http://www.yomiuri.co.jp/latestnews/)

やることは簡単です.
rubyのnokogiri使うのと余り変わらないです.
gokogiriを使ってスクレイピングかけてほしい情報を抜き取ってくるだけです.

準備

こういう構造で取得するとします.

type Yomiuri struct {
	Title string    `json:"title"`
	URL   string    `json:"url"`
	Time  time.Time `json:"time"`
	Photo bool      `json:"photo"`
}

まずbodyを取得します.

sourceURL := "http://www.yomiuri.co.jp/latestnews/"
resp, _ := http.Get(sourceURL)
body, body := ioutil.ReadAll(resp.Body)

これで取得するための準備は完了しました.

bodyから情報を抜き取る

基本的にtitle, url, 時間を抜き取るのりはどれも変わりません.
細かいところの整形方法が少しだけ変化します..
以下の様な枠組みで書くことができます.

var ret []string
a := xpath.Compile("XPath")

for _, newData := range newDatas {
  urls, err := newData.Search(a)
  if err != nil {
    return nil, err
  }
  ret = append(ret, urls[0].Content())
}
return ret, nil

それでは実際の判例を見てみましょう.

// URLの一覧取得
func urlSeparation(newDatas []xml.Node) ([]string, error) {
	var ret []string

	a := xpath.Compile("./a/@href")

	for _, newData := range newDatas {
		urls, err := newData.Search(a)
		if err != nil {
			return nil, err
		}
		ret = append(ret, urls[0].Content())
	}
	return ret, nil
}

//titleの一覧取得
func titleSeparation(newDatas []xml.Node) ([]string, error) {
	var ret []string

	a := xpath.Compile("./a/span")
	for _, newData := range newDatas {
		titles, err := newData.Search(a)
		if err != nil {
			return nil, err
		}
		for _, title := range titles {
			newsAndTime := title.Content()
			timePath := xpath.Compile("./span")
			time, err := title.Search(timePath)
			if err != nil {
				return nil, err
			}
			if len(time) != 0 {
				cutstr := time[0].Content()
				ret = append(ret, strings.Trim(newsAndTime, cutstr))
			}
		}
	}

	return ret, nil
}

//時間の取得
func timeSeparation(newDatas []xml.Node) ([]time.Time, error) {
	var ret []time.Time

	a := xpath.Compile("./a/span/span")
	for _, newData := range newDatas {
		times, err := newData.Search(a)
		if err != nil {
			return nil, err
		}
		timeStr := strings.Trim(strings.Trim(times[0].Content(), "("), ")")
		var year int
		var month int
		var day int
		var hour int
		var minute int

		fmt.Sscanf(timeStr, "%d年%d月%d日 %d時%d分", &year, &month, &day, &hour, &minute)
		loc, _ := time.LoadLocation("Asia/Tokyo")
		time := time.Date(year, time.Month(month), day, hour, minute, 0, 0, loc)
		ret = append(ret, time)
	}

	return ret, nil
}

//写真の有無の取得
func photoSeparation(newDatas []xml.Node) ([]bool, error) {
	var ret []bool
	a := xpath.Compile("./a/span[@class='icon-photo']")
	for _, newData := range newDatas {
		icons, err := newData.Search(a)
		if err != nil {
			return nil, err
		}
		if len(icons) == 0 {
			ret = append(ret, false)
		} else {
			ret = append(ret, true)
		}
	}
	return ret, nil
}

ここまで来たら後は簡単ですね.
あとは適当に配列を展開して構造体に押しこむだけです.

func GetYOmiuriNewArrivals() ([]Yomiuri, error) {
	var ret []Yomiuri
	page, err := getYomiuriNewArrivalsPage()
	if err != nil {
		return nil, err
	}

	doc, err := gokogiri.ParseHtml(page)
	if err != nil {
		return nil, err
	}
	defer doc.Free()
	datas, err := getNewsInfo(doc)
	if err != nil {
		return nil, err
	}
	titles, err := titleSeparation(datas)
	if err != nil {
		return nil, err
	}
	urls, err := urlSeparation(datas)
	if err != nil {
		return nil, err
	}
	times, err := timeSeparation(datas)
	if err != nil {
		return nil, err
	}
	phots, err := photoSeparation(datas)
	if err != nil {
		return nil, err
	}

	l := len(datas)
	for i := 0; i < l; i++ {
		y := Yomiuri{titles[i], urls[i], times[i], phots[i]}
		ret = append(ret, y)
	}

	return ret, nil
}

さてこれだけだとあまり面白くないですね.
高知工科大学には学内プロキシが存在していて自作のプログラムはプロキシを突破できません.
なのでこの関数に手を入れてプロキシを突破しましょう.

sourceURL := "http://www.yomiuri.co.jp/latestnews/"
resp, _ := http.Get(sourceURL)
body, body := ioutil.ReadAll(resp.Body)

goでプロキシを使うには以下のように書きます.

tbProxyURL, err := url.Parse("http://hogehoge.com:1234")
if err != nil {
	fatalf("Failed to parse proxy URL: %v\n", err)
}

tbDialer, err := proxy.FromURL(tbProxyURL, proxy.Direct)
if err != nil {
	fatalf("Failed to obtain proxy dialer: %v\n", err)
}

tbTransport := &http.Transport{Dial: tbDialer.Dial}
client := &http.Client{Transport: tbTransport}

req, err := http.NewRequest("GET", targetURL, nil)
if err != nil {
	log.Fatalln(err)
}
ua := "適当なUA"
req.Header.Set("User-Agent", ua)

resp, err := client.Do(req)
if err != nil {
	log.Fatalln(err)
}

簡単でしょ?
ちなみにhttpsプロキシにもsocksプロキシにも使用することができます.
自前でVPNサーバーを作ることもできるでしょう.

注意事項

データを取得するときrequest速度が早過ぎるとDOSアタックになりかねないので注意しましょう.
クローラー作るのは良いですが程々にね.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment