apache2.2系 キャッシュさせたくないときの設定
Header set Cache-Control "no-cache, no-store, must-revalidate,max-age=0,post-check=0,pre-check=0,false" Header set Pragma "no-cache"
apache2.2系 POSTをリダイレクトしたい
307リダイレクトすれば良い
RewriteEngine On RewriteRule ^/hoge/fuga$ /hoge/hoge [R=307,L]
ついにC言語のプロジェクトへメインプログラマとして(C言語初心者)
C言語プロジェクト。しかもチューニング系。
やばし。マクロとかまだぜんぜんわかってない。まぁ、なんとかするでしょ。私なら
20時間ぐらいGStreamerというライブラリについて右往左往した。
Macはなんかフル機能つかえないっぽい。VMのWindowsでも使えないっぽい。
結局DAW用のWin7マシンにVS2017Community入れてTeamViewer経由できちんと動いた。
再生とスクリーンショットを取れたところまで。
なんだかムダな努力を積み重ねた気がするよ。終末吹っ飛んで辛い。
どうやってC言語まなんで行こうか思案中
Cを学ぶIDEはEclipseで良いですかね。。。補完効くのかしら?効かないとやだなぁ。
正規表現 AND
(?=.*lemon)(?=.*apple)
は下記のすべての行にマッチ
lemon, apple, grape, orange apple, lemon, grape, orange grape, apple, lemon, orange
いやー知らなかったよー。
Golangでスクレイピング(Rubyの225倍速)
Go速い。並列処理速い。
rubyの実装だと15分かかっていた処理が4.5秒へ。ヤバすぎるぜGo!!
もう、本当にrubyは書きやすいシェルスクリプトぐらいの立ち位置で良いんじゃないかな。
テストデータ作る処理とかだけで。
他はPythonとかGoで書くと良いとおもいましたよ。
スクレイピングはいかに効率を上げるかが重要ですが、Goの並行処理の仕組みは使いやすさも含めてすごい良いです。
ともかく速さは命。
package main /* 実装方針 とりまDB登録とかしちゃうのはやめておいて、 logger作るのはやめておいて JSONから設定読み込んで出力するとか goqueryで一回叩いてみるとか、その辺を2017/06/10はするところまで チャンネルとかデータ構造とかは2017/06/11から少しずつやっていけばよろし。 DB以外の部分はとりあえず出来た。 めっちゃ速いwwww rubyだと900秒 goだと4.5秒 */ import ( "encoding/json" f "fmt" "github.com/PuerkitoBio/goquery" "net/url" "os" "os/exec" "path/filepath" "regexp" "strings" "sync" ) const StartURL = "http://youtubeanisoku1.blog106.fc2.com/" const Configfile = "config.json" const ( ANISOKUTOP int = iota KOUSINPAGE KOBETUPAGE HIMADOSEARCH HIMADOVIDEO ) type Config struct { DonloadDir string `json:"downloaddir"` DBFILE string `json:"dbfile"` TITLEREGEXP string `json:"title_regexp"` } var cfg Config type JOB struct { JOBKIND int URL string TITLE string EPISODE string } func (job *JOB) Dispacher() { switch job.JOBKIND { case ANISOKUTOP: wg.Add(1) go job.AnisokuTop() case KOUSINPAGE: wg.Add(1) go job.KousinPage() case KOBETUPAGE: wg.Add(1) go job.KobetuPage() case HIMADOSEARCH: wg.Add(1) go job.HimadoSearch() case HIMADOVIDEO: wg.Add(1) go job.HimadoVideo() default: } } // トップページのスクレイピング func (job *JOB) AnisokuTop() { defer wg.Done() doc, err := goquery.NewDocument(job.URL) if err != nil { f.Println("url scraping fail:", job.URL) return } doc.Find(".Top_info div ul li a").Each(func(_ int, s *goquery.Selection) { title, _ := s.Attr("title") if strings.Contains(title, "更新状況") { u, _ := s.Attr("href") JobCh <- &JOB{KOUSINPAGE, u, "", ""} } }) } // 更新ページのスクレイピング func (job *JOB) KousinPage() { defer wg.Done() doc, err := goquery.NewDocument(job.URL) if err != nil { f.Println("kousinpage error", job.URL) return } doc.Find("ul[type='square'] li a").Each(func(_ int, s *goquery.Selection) { href, _ := s.Attr("href") if href == "" { return } title, _ := s.Attr("title") if title != "" { return } JobCh <- &JOB{KOBETUPAGE, href, "", ""} }) } // 個別ページのスクレイピング func (job *JOB) KobetuPage() { defer wg.Done() doc, err := goquery.NewDocument(job.URL) if err != nil { f.Println("kobetupage error", job.URL) return } var title string doc.Find("title").Each(func(_ int, s *goquery.Selection) { title = s.Text() title = cleanupValue(title) }) if !TitleRegexp.MatchString(title) { return } _, ok := TitleMap[title] if ok { return } else { TitleMap[title] = true } //f.Println("DO:", title) found := false doc.Find("a").Each(func(_ int, s *goquery.Selection) { if found { return } href, _ := s.Attr("href") if !strings.Contains(href, "himado.in") { return } //一つ見つかればOK if !found { JobCh <- &JOB{HIMADOSEARCH, href, title, ""} found = true } }) } // ひまわり検索ページ func (job *JOB) HimadoSearch() { defer wg.Done() doc, err := goquery.NewDocument(job.URL) if err != nil { f.Println("himadosearch error", job.URL) return } count := 0 doc.Find(".thumbtitle a[rel='nofollow']").Each(func(_ int, s *goquery.Selection) { if count > 2 { return } href, _ := s.Attr("href") if href == "" { return } href = "http://himado.in" + href // 再取得制御 _, exist := PageMap[href] if exist { return } PageMap[href] = true episode, _ := s.Attr("title") if episode == "" { return } episode = cleanupValue(episode) JobCh <- &JOB{HIMADOVIDEO, href, job.TITLE, episode} count++ }) } // ひまわりビデオページ func (job *JOB) HimadoVideo() { defer wg.Done() doc, err := goquery.NewDocument(job.URL) if err != nil { f.Println("himadoVideo error", job.URL) return } mediaUrl := "" doc.Find("script").Each(func(_ int, s *goquery.Selection) { text := s.Contents().Text() if text == "" { return } texta := strings.Split(text, "\n") for _, l := range texta { if strings.Contains(l, "var movie_url") { l = strings.TrimSpace(l) l = strings.Replace(l, "var movie_url = '", "", -1) l = strings.Replace(l, "';", "", -1) u, err := url.PathUnescape(l) if err == nil { mediaUrl = u } break } } }) if mediaUrl == "" { return } fp := makeFilePath(job.TITLE, job.EPISODE) if !FileIsExists(fp) { return } job.DownloadVideo(mediaUrl) } // ビデオダウンロード func (job *JOB) DownloadVideo(url string) { err := os.MkdirAll(makeFileDirPath(job.TITLE), 0777) if err != nil { f.Println("ディレクトリ作成失敗") return } fp := makeFilePath(job.TITLE, job.EPISODE) cmd := "curl -# -L " + url + " | ffmpeg -threads 4 -y -i - -vcodec copy -acodec copy '" + fp + "' &" f.Println(cmd) exec.Command("sh", "-c", cmd).Start() } // ディレクトリを確認 func makeFileDirPath(title string) string { return filepath.Join(cfg.DonloadDir, title) } // ファイルパスを作成 func makeFilePath(title string, episode string) string { return filepath.Join(cfg.DonloadDir, title, episode+".mp4") } // ファイル存在確認 func FileIsExists(filename string) bool { _, err := os.Stat(filename) return err != nil } // 値をきれいにする func cleanupValue(s string) string { s = strings.Replace(s, "★ You Tube アニ速 ★", "", -1) s = strings.Replace(s, ":", ":", -1) s = strings.Replace(s, "第", "", -1) s = strings.Replace(s, "話", ":", -1) s = strings.Replace(s, ".", "", -1) s = strings.Replace(s, " ", "", -1) s = strings.Replace(s, " ", "", -1) s = strings.Replace(s, "#", "", -1) s = strings.Replace(s, "(", "", -1) s = strings.Replace(s, ")", "", -1) s = strings.Replace(s, "/", "", -1) s = strings.Replace(s, "(", "", -1) s = strings.Replace(s, ")", "", -1) s = strings.Replace(s, "+", "+", -1) s = strings.Replace(s, "[720p]", "", -1) s = strings.Replace(s, "高画質", "", -1) s = strings.Replace(s, "QQ", "", -1) s = strings.Replace(s, "?", "?", -1) s = strings.Replace(s, "[", "", -1) s = strings.Replace(s, "]", "", -1) return s } // 確認済み管理マップ var TitleMap map[string]bool = make(map[string]bool) var PageMap map[string]bool = make(map[string]bool) var TitleRegexp *regexp.Regexp // JOBチャネル var JobCh chan *JOB = make(chan *JOB) // WaitGroup var wg sync.WaitGroup = sync.WaitGroup{} // コンフィグを読み出す func loadConfig() (*Config, error) { fh, err := os.Open(Configfile) if err != nil { return nil, err } defer fh.Close() err = json.NewDecoder(fh).Decode(&cfg) TitleRegexp = regexp.MustCompile(cfg.TITLEREGEXP) return &cfg, err } // JOBチャネルのレシーバー func receiver(ch chan *JOB) { for { job := <-ch job.Dispacher() } } // 本体 func Run() int { // コンフィグ読み出し cfg, err := loadConfig() if err != nil { f.Println("config load error", err) return 1 } f.Println(cfg) go receiver(JobCh) // 初期キック JobCh <- &JOB{ANISOKUTOP, StartURL, "", ""} wg.Wait() return 0 } func main() { retcode := Run() os.Exit(retcode) }
Golang コードを書き始めた
リファレンスを参照しながら少しずつ作りはじめてる
スクレイピング系のプログラムを作ることにした。
とりま、main.goだけでいく。今のところ。structの分離とかはおいおいやっていけば良いかなと。
rubyで書いていたコードのgo移植なので細かいdomの確認とかはrubyのものが流用できるので結構純粋にGolangだけの取り組みなので良い題材かなと
初めてのGoのプログラミングだけど1時間ぐらいで骨子が見えてきているのはなかなか良いペースだと自画自賛
やっぱ書かないと身につかないね。
package main /* 実装方針 とりまDB登録とかしちゃうのはやめておいて、 logger作るのはやめておいて JSONから設定読み込んで出力するとか goqueryで一回叩いてみるとか、その辺を2017/06/10はするところまで チャンネルとかデータ構造とかは2017/06/11から少しずつやっていけばよろし。 =>ちょっとやりはじめてる */ import ( f "fmt" "os" "strings" "encoding/json" "github.com/PuerkitoBio/goquery" ) const StartURL = "http://youtubeanisoku1.blog106.fc2.com/" const Configfile = "config.json" const ( KOUSINPAGE int = iota KOBETUPAGE HIMADOSEARCH HIMADOVIDEO ) type Config struct { DonloadDir string `json:"downloaddir"` DBFILE string `json:"dbfile"` TITLEREGEXP string `json:"title_regexp"` } var cfg Config type JOB struct { JOBKIND int URL string } // チャンネル var JobCh chan *JOB // コンフィグを読み出す func loadConfig() (*Config, error) { f, err := os.Open(Configfile) if err != nil { return nil, err } defer f.Close() err = json.NewDecoder(f).Decode(&cfg) return &cfg, err } // ディスパッチャー func dispatcher(ch <-chan *JOB) { for { job := <-ch f.Println(job) } } // 本体 func Run() int { // コンフィグ読み出し cfg, err := loadConfig() if err != nil { f.Println("config load error", err) return 1 } f.Println(cfg) // JOBのチャンネル JobCh = make(chan *JOB) go dispatcher(JobCh) // 初期キック doc, err := goquery.NewDocument(StartURL) if err != nil { f.Println("url scraping fail:", StartURL) return 1 } doc.Find(".Top_info div ul li a").Each(func(_ int, s *goquery.Selection) { title, _ := s.Attr("title") if strings.Contains(title, "更新状況") { url, _ := s.Attr("href") JobCh <- &JOB{KOUSINPAGE, url} } }) return 0 } func main() { retcode := Run() os.Exit(retcode) }
みんなのGo言語読んだ
さらっと流してよんだ。実際にものをつくってから立ち帰ってベスト・プラクティスを知るためにもう一度読めば良い本だと思う。逆引きまでは行かないまでも良い網羅感あり。
さぁ、プログラムを組もう。楽しもう。そろそろウズウズしてきてるよ。
- 作者: 松木雅幸,mattn,藤原俊一郎,中島大一,牧大輔,鈴木健太,稲葉貴洋
- 出版社/メーカー: 技術評論社
- 発売日: 2016/09/09
- メディア: 大型本
- この商品を含むブログ (3件) を見る