こんにちは高知工科大Advent Calendar 2015の7日目担当です.
今回は就活っぽいお話をします.
就活のことはよくわからないのでイメージで書いていきます.
就活の時って時事ネタでニュースのことを聞かれるイメージが有ります.
ただ自分は新聞を購読していませんしテレビも持っていませんなのでニュースを手に入れるとしたら
インターネットニュースになります.
意識高い学生になるべく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)
これで取得するための準備は完了しました.
基本的に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アタックになりかねないので注意しましょう.
クローラー作るのは良いですが程々にね.