Category Archives: PROGRAM

Go言語で画像ダウンローダを書いてみた

先ほどのGo言語で皆大好き htpdate を書いてみたに続いて画像をダウンロードするスクリプトを書いてみました。
PerlやRuby、Pythonほどスクレイピングをするライブラリは充実していないものの”goquery”というライブラリがGo言語でソコソコ使えそうだったので使用してみました。

ライブラリ(goquery)のインストール
# go get github.com/PuerkitoBio/goquery

ソースコード(事情は下に書きますが殴り書きです)
# vi downloader.go

package main

import (
  "os"
  "fmt"
  "path"
  "net/url"
  "net/http"
  "io/ioutil"
  "github.com/PuerkitoBio/goquery"
)

func GetImgUrl(base string) []*url.URL {
  var src_url_list []*url.URL

  doc, _ := goquery.NewDocument(base)
  doc.Find("img").Each(func(_ int, s *goquery.Selection) {
    src, exists := s.Attr("src")
    if exists {
      base, _ := url.Parse(base)
      srcs, _ := url.Parse(src)
      src_url_list = append(src_url_list, base.ResolveReference(srcs))
    }
  })

  return src_url_list
}

func DownloadFiles(url_list []*url.URL) {
  for id, url := range url_list {
    raw_url := url.String()

    _, filename := path.Split(raw_url)
    filepath := path.Join("download", filename)

    response, err := http.Get(raw_url)
    body, err := ioutil.ReadAll(response.Body)

    if err != nil {
      fmt.Println(err)
    }

    file, err := os.OpenFile(filepath, os.O_CREATE|os.O_WRONLY, 0666)

    if err != nil {
      fmt.Println(err)
    }

    file.Write(body)
    file.Close()

    fmt.Printf("[%d]%s %s\n", id, raw_url, filename)
  }
}

func main() {
  url := "http://akiba-pc.watch.impress.co.jp/"
  url_list := GetImgUrl(url)

  DownloadFiles(url_list)

}

ダウンロード先のディレクトリの作成
# mkdir download

スクリプトの実行
# go run downloader.go

[0]http://akiba-pc.watch.impress.co.jp/include/common/p01/images/logo/ah.l.png ah.l.png
[1]http://akiba-pc.watch.impress.co.jp/include/common/p01/images/global-nav/gn_headline.png gn_headline.png
[2]http://akiba-pc.watch.impress.co.jp/include/common/p01/images/global-nav/gn_clw.png gn_clw.png
[3]http://akiba-pc.watch.impress.co.jp/include/common/p01/images/global-nav/gn_pcw.png gn_pcw.png
[4]http://akiba-pc.watch.impress.co.jp/include/common/p01/images/global-nav/gn_dcw.png gn_dcw.png
[5]http://akiba-pc.watch.impress.co.jp/include/common/p01/images/global-nav/gn_ah.png gn_ah.png
~~~以下省略~~~

実は、Go言語の”channel”や”goroutine”など並列処理に関する実装方法について勉強するつもりで殴り書きしてたのですが、今日は時間がなさそうなので書いたところまでメモがてらに記事を書いていたりします(汗

Go言語で皆大好き htpdate を書いてみた

PCやサーバの時間合わせにntpdateを通常用いますが、80番と443番しか外部接続を許されないネットワークだとNTPが使えない事があります。そんな時にNTPの代替としてWEBサーバの時間を元にマシンの時間を合わせるコマンドが”htpdate”です。

仕組はHTTPのレスポンスヘッダに含まれる”Date”を元にソコソコ正確な時間を取得してOSの時間を設定します。
とても単純明快で清々しいくらいです。

例えばこのサーバだと、このようなレスポンスヘッダが帰ってきます。
この中の「Date: Wed, 08 Apr 2015 11:35:10 GMT」がサーバの時間となります。

HTTP/1.1 200 OK
Date: Wed, 08 Apr 2015 11:35:10 GMT
Server: Apache/2.2.3 (CentOS)
Last-Modified: Fri, 29 Jun 2012 10:40:46 GMT
ETag: "16e07e-5-4c39a14ab6780"
Accept-Ranges: bytes
Content-Length: 5
Connection: close
Content-Type: text/html

# vi htpdate.go

package main

import (
  "os"
  "fmt"
  "time"
  "syscall"
  "net/http"
)

// HTTPサーバへアクセスしてヘッダーからアクセス日時を取得する関数
func get_http_date(url string) (string, error) {
  response, err := http.Get(url)
  if err != nil { return "", err }

  access_time := response.Header.Get("Date")
  return access_time, nil
}

// 引数として受け取ったUNIXタイムをOSの時間に設定する関数
func settime(sec int64, usec int64) error {
  tv := syscall.Timeval{ Sec: sec, Usec: usec }

  return os.NewSyscallError("settimeofday", syscall.Settimeofday(&tv))
}

func main() {
  if len(os.Args) > 1 {
    // とりあえずHTTPサーバを1個だけ引数に受け取る
    host_name  := os.Args[1]
    target_url := "http://" + host_name

    // HTTPサーバからアクセス日時を取得
    access_time, err := get_http_date(target_url)
    if err != nil {
      fmt.Fprintln(os.Stderr, "Failed to access the HTTP server.")
      os.Exit(1)
    }

    // 取得した時間をパーズする
    server_time, err := time.Parse(time.RFC1123, access_time)
    if err != nil {
      fmt.Fprintln(os.Stderr, "Time of format is not a RFC1123.")
      os.Exit(1)
    }

    // 取得した時間をOSの時間として設定する
    if err := settime(server_time.Unix(), 0); err != nil {
      fmt.Fprintln(os.Stderr, "Failed to set the date and time.")
      os.Exit(1)
    }

    fmt.Println(time.Now())
    os.Exit(0)
  } else {
    fmt.Fprintln(os.Stderr, "Please set the HTTP server to argument.")
    os.Exit(1)
  }
}

実行してみると時間が設定される事が分かるはずです。
# go run htpdate.go orsx.net

2015-04-08 21:05:18.000046645 +0900 JST

Proxyなどは対応していませんがGo言語を勉強するうえで良い教材になりそうな気がしたので簡単に実装してみました。

[HTTP Time Protocol / htpdate] Webプロキシを経由して時刻を同期するの巻 – TrippyBoyの愉快な日々
Introduction | HTTP Time Protocol

RC-S620/SをFeliCaカードに成りきらせて好きなIDmで応答するようにする

IMG_3999
※ ArduinoとRC-S620/Sの接続については(ArduinoでRELET(FeliCa電子マネー残高照会機)モドキを作ろう)で図を描いているのでそちらを参考にしてください。

ライブラリの拡張

公式で提供されているArduino用ライブラリを拡張する必要があります。

RCS620S.h 29行目付近に追加

int tginit(const uint8_t* idm);

RCS620S.cpp 80行目付近に追加

int RCS620S::tginit(const uint8_t* idm)
{
    uint8_t  response[RCS620S_MAX_RW_RESPONSE_LEN];
    uint16_t responseLen;

    /* TgInitTarget command parts */
    uint8_t command[RCS620S_MAX_RW_RESPONSE_LEN] = {0x00};
    uint8_t param_1[ 9] = {0xd4, 0x8c, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x40};
    uint8_t param_2[10] = {0xff};

    /* Make TgInitTarget command */
    memcpy(&command[ 0], param_1,  9);
    memcpy(&command[ 9], idm,      8);
    memcpy(&command[17], param_2, 10);
    memcpy(&command[27], idm,      8);

    /* Execute TgInitTarget */
    rwCommand(command, 41, response, &responseLen);

    return 1;
}

スケッチの作成

Arduino側のスケッチ

#include <RCS620S.h>
#include <string.h>

RCS620S rcs620s;

void setup()
{
  int ret;

  Serial.begin(115200);
  ret = rcs620s.initDevice();
  while (!ret) {}
}

void loop()
{
  int ret;
  
  // SET IDm
  ret = rcs620s.tginit((const uint8_t*)"\x08\x07\x06\x05\x04\x03\x02\x01");
  rcs620s.rfOff();
}

Python版 ぴくぴくダウンローダーを作成している夢を見た

完全オープンソースにするか悩みどころですが取りあえずモチベーション維持のためにもちょくちょくソースコードを公開していこうと思います。

単純にダウンロードするなら簡単なんですが、アレもできてコレもできてとなると中々大変なんですよね(汗

GUIを作るのがかなりしんどいので取りあえず、いつも通りCUI版を作ってGUIを上からかぶせる感じで行こうと思います。
実はTideSDKを使う予定だったけど暗礁に乗り上げてます。誰かアドバイスをください。
※ 毎回GUI後付でやってるけどうまく行っている気がしない・・・

今回の目標
・Pythonを触ったことがない人でも処理をおおまかに理解できるよう書く(そもそも自分がPythonド素人)
・Java版の時みたいに糞長いソースコードを力押しで書かない(今後の保守性を再優先)
・Perl版の時みたいに正規表現のスクランブルにしない(あれは黒歴史)

# vi PixivBrowser.py

# -*- coding:utf-8 -*-
#################################################################
#
# Pixivのブラウザエミュレータ
#
#  Pixivの接続情報を保持する
#  UA設定やProxy設定を組み込むかもしれない
# 
# 【制作開始日時】 2014/06/25
# 【   製作者   】 orbit
# 【  動作環境  】 Python 2.7 (Win, Mac, Linuxで動くのが理想)
#
# 【   更新日   】
#    2014/06/25    ログイン処理に関する部分を作成 
#                  seleniumを使うか悩んだがPixivへの負荷を考慮
#
#    2014/06/27    認証を行うメソッドの名前を変更
#
#################################################################
import mechanize

# Pixivのブラウザエミュレータを担当するクラス
class PixivBrowser(object):
  def __init__(self):

    # Pixivのログインページ
    self.loginurl = 'https://www.secure.pixiv.net/login.php'
    # Pixivのユーザーページ
    self.userpage = 'http://www.pixiv.net/mypage.php'

    # ブラウザエミュレータのインスタンス生成
    self.browser = mechanize.Browser()

    # 当然検索エンジンよけを無視
    self.browser.set_handle_robots(False)

  # Pixivの認証を行う
  def auth(self, username, password):
    # ユーザ情報
    self.username = username # ユーザー名
    self.password = password # パスワード

    # ログインページを開く
    self.browser.open(self.loginurl)
    
    # ユーザ名とパスワードを指定してログイン
    self.browser.select_form(nr=1)
    self.browser["pixiv_id"] = self.username
    self.browser[  "pass"  ] = self.password
    self.browser.submit()

    # ログインに成功した確認する
    res = self.browser.response() # レスポンスを受け取る
    url = res.geturl()            # 現在のURLを取得する

    # ログイン後にユーザページにいないのはログイン失敗
    if(url != self.userpage):
      # ログインに失敗した場合はどうしようもないので例外を投げる
      raise PixivBrowserException("login failed. please check you are account.")

    # ログインページのHTMLを取得
    html = self.browser.response().read()

    # 問題なくログインできたらドヤ顔でログインページのHTMLを返す
    return html

  # ブラウザエミュレータを返す
  def getBrowser(self):
    return self.browser

# PixivBrowserのプログラム内で処理継続が不能になったら呼ばれる例外クラス
class PixivBrowserException(Exception):
  def __init__(self, str):
    self.str = str   # strはraise文から受け取る引数

  def __str__(self): # エラーメッセージ
    return '%s' % (self.str)

## 以下、デバッグ用の処理 ##
if __name__ == '__main__':
  pb = PixivBrowser()
  pb.auth('**************', '**************')

# vi PixivSearch.py

# -*- coding:utf-8 -*-
#################################################################
#
# Pixivの絵画(絵・漫画)を検索・ページングするプログラム
#
#  検索で並び順の変更に対応するかもしれない
# 
# 【制作開始日時】 2014/06/27
# 【   製作者   】 orbit
# 【  動作環境  】 Python 2.7 (Win, Mac, Linuxで動くのが理想)
#
# 【   更新日   】
#    2014/06/27    検索とページングを行う部分を作成
#                  絵・漫画・小説の他にうごくイラストをサポート
#
#################################################################

# Pixivの絵画(絵・漫画)を検索するクラス
class PixivSearch(object):
  def __init__(self, browser):
    ## ページ情報 ##
    # Pixivのユーザーページ
    self.userpage = 'http://www.pixiv.net/mypage.php'
    # 検索ページベース
    self.pixibass = 'http://www.pixiv.net'
    
    # 並び順
    self.pagesort = '&order=date_d'
    
    # ページ番号
    self.pagenumb = 1;
    
    ##  ブラウザ  ##
    self.browser   = browser
  
  # 通常の検索(絵・漫画混在) 今回、仕様的に使うか微妙
  def search(self, keyword):
    # 検索キーワード
    self.keyword = keyword
    
    # 検索パラメータをつける
    self.searcurl = self.pixibass + '/search.php?word=' + keyword + self.pagesort + '&p='
    
    # ページ番号を付加して検索ページを開く
    self.browser.open(self.searcurl + str(self.pagenumb))
    
    # 検索結果ページのHTMLを取得
    html = self.browser.response().read()
    
    # 検索結果ページのHTMLを返す
    return html
  
  # 絵の検索
  def searchImage(self, keyword):
    # 検索キーワード
    self.keyword = keyword
    
    # 検索パラメータをつける
    self.searcurl = self.pixibass + '/search.php?word=' + keyword + '&manga=0' + self.pagesort + '&p='
    
    # ページ番号を付加して検索ページを開く
    self.browser.open(self.searcurl + str(self.pagenumb))
    
    # 検索結果ページのHTMLを取得
    html = self.browser.response().read()
    
    # 検索結果ページのHTMLを返す
    return html
  
  # 漫画の検索
  def searchComic(self, keyword):
    # 検索キーワード
    self.keyword = keyword
    
    # 検索パラメータをつける
    self.searcurl = self.pixibass + '/search.php?word=' + keyword + '&manga=1' + self.pagesort + '&p='
    
    # ページ番号を付加して検索ページを開く
    self.browser.open(self.searcurl + str(self.pagenumb))
    
    # 検索結果ページのHTMLを取得
    html = self.browser.response().read()
    
    # 検索結果ページのHTMLを返す
    return html
  
  # うごくイラストの検索
  def searchUgoku(self, keyword):
    # 検索キーワード
    self.keyword = keyword
    
    # 検索パラメータをつける
    self.searcurl = self.pixibass + '/search.php?word=' + keyword + '&type=ugoira' + self.pagesort + '&p='
    
    # ページ番号を付加して検索ページを開く
    self.browser.open(self.searcurl + str(self.pagenumb))
    
    # 検索結果ページのHTMLを取得
    html = self.browser.response().read()
    
    # 検索結果ページのHTMLを返す
    return html
  
  # 小説の検索
  def searchNovel(self, keyword):
    # 検索キーワード
    self.keyword = keyword
    
    # 検索パラメータをつける
    self.searcurl = self.pixibass + '/novel/search.php?s_mode=s_tag&word=' + keyword + self.pagesort + '&p='
    
    # ページ番号を付加して検索ページを開く
    self.browser.open(self.searcurl + str(self.pagenumb))
    
    # 検索結果ページのHTMLを取得
    html = self.browser.response().read()
    
    # 検索結果ページのHTMLを返す
    return html
  
  # ページ番号を前進
  def nextPage(self):
    # ページ番号をインクリメントする
    self.pagenumb = self.pagenumb + 1
    
    # 次のページ番号を返す
    return self.pagenumb
  
  # ページ番号を後進 今回、仕様的に使うか微妙
  def backPage(self):
    # ページ番号をデクリメントする
    self.pagenumb = self.pagenumb - 1
    
    # 次のページ番号を返す
    return self.pagenumb
  
  # 現在のページ番号を得る
  def getPage(self):
    # 現在のページ番号を返す
    return self.pagenumb
  
  # 現在のページ番号を変更する
  def setPage(self, page):
    # ページ番号を書き換える
    self.pagenumb = page
    
    # 現在のページ番号を返す
    return self.pagenumb

# PixivSearchのプログラム内で処理継続が不能になったら呼ばれる例外クラス
class PixivSearchException(Exception):
  def __init__(self, str):
    self.str = str   # strはraise文から受け取る引数

  def __str__(self): # エラーメッセージ
    return '%s' % (self.str)

## 以下、デバッグ用の処理 ##
if __name__ == '__main__':
  from PixivBrowser import *

  pb = PixivBrowser()
  pb.auth('**************', '**************')
  browser = pb.getBrowser()

  ps = PixivSearch(browser)
  #print ps.search("ミク")
  #print ps.searchImage("ミク")
  #print ps.searchComic("ミク")
  #print ps.searchUgoku("ミク")
  #print ps.searchNovel("ミク")
  ps.setPage(100)
  ps.nextPage()
  print ps.search("ミク")

進捗状態
次は検索結果のHTMLを構文解析するクラスと作品ごと情報を保持するクラスを作成する予定
ID検索の部分に入るまでもう少し時間がかかりそうな予感

Python (mechanize + BeautifulSoup) でPixivにログインしてタイトルタグの中身を取得する夢を見た

PythonでPixivにログインしてタイトルタグの中身を取得する夢を見たのでメモしておきます

# -*- coding:utf-8 -*-
import mechanize
import BeautifulSoup

login_page = 'https://www.secure.pixiv.net/login.php'

username = "**************"
password = "**************"

br = mechanize.Browser()
br.set_handle_robots(False)

# ログインページを開く
br.open(login_page)

# ユーザ名とパスワードを指定してログイン
br.select_form(nr=1)
br["pixiv_id"] = username
br[  "pass"  ] = password
br.submit()

# マイページを取得
html = br.response().read()

# スクレイピング
soup = BeautifulSoup.BeautifulSoup(html)
recipe_title = soup.find("title")
print(recipe_title.string)

br.close()

Twitter フォロワー同期(フォロー専用)Bot Perl 新API対応(改弐)

TwitterのAPIがすべてSSLになり403 forbiddenと表示されるようになったので対策してみました。

#!/usr/bin/perl
use utf8;
use strict;
use warnings;

# モジュール使用宣言
use Encode;
use FindBin;
use YAML::Tiny;
use Array::Diff;
use Net::Twitter;

# 現在のパスから見て設定ファイルを読み込み
my $config = (YAML::Tiny->read($FindBin::Bin . '/config.yml'))->[0];

# OAuth認証
my $twitter = Net::Twitter->new(
        traits           => ['API::RESTv1_1', 'OAuth'],
        consumer_key     => $config->{'consumer_key'},
        consumer_secret  => $config->{'consumer_secret'},
        apiurl           => 'https://api.twitter.com/1.1',
        searchapiurl     => 'https://api.twitter.com/1.1',
        legacy_lists_api => 0
);

$twitter->access_token($config->{'access_token'});
$twitter->access_token_secret($config->{'access_token_secret'});

# 認証失敗時の処理
die('Auth failed:'.$config->{'username'}) unless ( $twitter->authorized ) ;

# ユーザー名を含むユーザー情報を取得
my $own_id = $twitter->verify_credentials->{id};

my $nextc = -1; # paging default.
my @following_id_list; # outgo

# APIの仕様?から一度に100人までしか取得できないから0が返ってくるまでdoブロックをループ
do{
        # パラメータcursorは前回取得したフォローイングまでの番号が入っている
        my $following_list = $twitter->friends_ids({ id=>$own_id, cursor => $nextc });
        $nextc = $following_list->{next_cursor};
        # 配列からフォローイングのidを取得
        foreach my $id (@{ $following_list->{ids} }){
                push(@following_id_list, $id); # 後で比較するためにフォローイングを配列に保管
        }
}while($nextc!=0);
# 文字昇順でソート
@following_id_list = sort @following_id_list;

$nextc = -1;
my @followers_id_list; # income

# APIの仕様?から一度に100人までしか取得できないから0が返ってくるまでdoブロックをループ
do{
        # パラメータcursorは前回取得したフォロワーまでの番号が入っている
        my $followers_list = $twitter->followers_ids({ id=>$own_id, cursor => $nextc });
        $nextc = $followers_list->{next_cursor};
        # 配列からフォロワーのidを取得
        foreach my $id (@{ $followers_list->{ids} }){
                push(@followers_id_list, $id); # 後で比較するためにフォロワーを配列に保管
        }
}while($nextc!=0);
# 文字昇順でソート
@followers_id_list = sort @followers_id_list;

# 差分を取得(フォローイング)
my $diff_following = Array::Diff->diff(\@following_id_list, \@followers_id_list);

# 差分を取得(フォロワー)
my $diff_followers = Array::Diff->diff(\@followers_id_list, \@following_id_list);

# リムった人をリム返し
foreach my $delid_following (@{ $diff_following->{deleted} }){
        eval{$twitter->destroy_friend({user_id => $delid_following})};
        # プライベートだとエラーになる人がいるので確認用
        print $delid_following . "\n" if($@);
}

# フォローした人をフォロー返し
foreach my $delid_followers (@{ $diff_followers->{deleted} }){
        eval{$twitter->create_friend({user_id => $delid_followers})};
        # プライベートだとエラーになる人がいるので確認用
        print $delid_followers . "\n" if($@);
}

Perl SSH接続 Net::SSH2 使い方 CentOS5

PerlでSSHへ接続したくなったのでNet::SSH2を使ってみた
Net::SSH2を使うにはlibssh2-develを使用するらしいのでインストール
# yum -y install libssh2-devel

cpanを使ってNet::SSH2をインストール
# cpan -i Net::SSH2
# cpan -i Net::OpenSSH::Compat::SSH2

実装例
SSHでサーバにログインしてpublic_htmlに移動し、lsコマンドを実行する

#!/usr/bin/perl
use strict;
use warnings;
# SSHのバナーを表示したい場合
#use Net::OpenSSH::Compat::SSH2 qw(:supplant);
use Net::SSH2;

# バッファーサイズ
use constant BUFLEN => 512;

my $host = ""; # 接続先ホスト
my $user = ""; # ユーザ名
my $pass = ""; # パスワード

my ($len, $buf);

my $ssh2 = Net::SSH2->new();

$ssh2->connect($host) or die "$!";

if( $ssh2->auth_password($user,$pass)) {
    my $chan = $ssh2->channel();
    $chan->blocking(0);
    $chan->shell();

    $chan->write("cd public_html\n");
    select(undef,undef,undef,0.2);
    print $buf while ($len = $chan->read($buf, BUFLEN));

    $chan->write("ls -al ./\n");
    select(undef,undef,undef,0.2);
    print $buf while ($len = $chan->read($buf, BUFLEN));

    $chan->close();
}

これは色々使えそうですね

参考サイト
Perl Tips | Perl で、ssh でリモートのサーバにアクセスしてファイルをアップロード(SCP)する方法 (Net::SSH2)
自分イノベーション – 気まぐれ勉強メモ Net::SSH2を使ってみた
Net::SSH2と公開鍵
A little demo for Net::SSH2

Twitter フォロワー同期(フォロー専用)Bot Perl 新API対応(改)

アカウントがプライベート設定のフォロワーがいると403 forbiddenになって処理が止まるようなので一応対策しました
手動で実行することで対象のID(数字)を確認出来ます

#!/usr/bin/perl
use utf8;
use strict;
use warnings;

# モジュール使用宣言
use Array::Diff;
use Net::Twitter::Lite::WithAPIv1_1;
use YAML::Tiny;
use Encode;
use FindBin;

# 現在のパスから見て設定ファイルを読み込み
my $config = (YAML::Tiny->read($FindBin::Bin . '/config.yml'))->[0];
# OAuth認証
my $twitter = Net::Twitter::Lite::WithAPIv1_1->new(
        traits => ['API::REST', 'OAuth'],
        consumer_key => $config->{'consumer_key'},
        consumer_secret => $config->{'consumer_secret'}
);
$twitter->access_token($config->{'access_token'});
$twitter->access_token_secret($config->{'access_token_secret'});

# 認証失敗時の処理
die('Auth failed:'.$config->{'username'}) unless ( $twitter->authorized ) ;

# ユーザー名を含むユーザー情報を取得
my $own_id = $twitter->verify_credentials->{id};

my $nextc = -1; # paging default.
my @following_id_list; # outgo

# APIの仕様?から一度に100人までしか取得できないから0が返ってくるまでdoブロックをループ
do{
        # パラメータcursorは前回取得したフォローイングまでの番号が入っている
        my $following_list = $twitter->friends_ids({ id=>$own_id, cursor => $nextc });
        $nextc = $following_list->{next_cursor};
        # 配列からフォローイングのidを取得
        foreach my $id (@{ $following_list->{ids} }){
                push(@following_id_list, $id); # 後で比較するためにフォローイングを配列に保管
        }
}while($nextc!=0);
# 文字昇順でソート
@following_id_list = sort @following_id_list;

$nextc = -1;
my @followers_id_list; # income

# APIの仕様?から一度に100人までしか取得できないから0が返ってくるまでdoブロックをループ
do{
        # パラメータcursorは前回取得したフォロワーまでの番号が入っている
        my $followers_list = $twitter->followers_ids({ id=>$own_id, cursor => $nextc });
        $nextc = $followers_list->{next_cursor};
        # 配列からフォロワーのidを取得
        foreach my $id (@{ $followers_list->{ids} }){
                push(@followers_id_list, $id); # 後で比較するためにフォロワーを配列に保管
        }
}while($nextc!=0);
# 文字昇順でソート
@followers_id_list = sort @followers_id_list;

# 差分を取得(フォローイング)
my $diff_following = Array::Diff->diff(\@following_id_list, \@followers_id_list);

# 差分を取得(フォロワー)
my $diff_followers = Array::Diff->diff(\@followers_id_list, \@following_id_list);

# リムった人をリム返し
foreach my $delid_following (@{ $diff_following->{deleted} }){
        eval{$twitter->destroy_friend({user_id => $delid_following})};
        # プライベートだとエラーになる人がいるので確認用
        print $delid_following . "\n" if($@);
}

# フォローした人をフォロー返し
foreach my $delid_followers (@{ $diff_followers->{deleted} }){
        eval{$twitter->create_friend({user_id => $delid_followers})};
        # プライベートだとエラーになる人がいるので確認用
        print $delid_followers . "\n" if($@);
}

シーザー暗号 ROT13 Perl 復号

ありきたりですがPerlで書いてみました

#!/usr/bin/perl
print sub{$_[0]=~tr/A-Za-z/ZA-Yza-y/
for(1..$_[1]);$_[0]}->($s=<>,$k=<>);

文字列 + 改行 + ずらす回数 + 改行 で実行できます

結構短く書けるものですね

Perl LWP::UserAgentを使ったHTTPS接続

PerlでLWP::UserAgentを使い、HTTPSサイトへ接続を行いコンテンツを取得してくるプログラムを書く必要が出てきて、色々調べて書いたのですが情報が錯綜していたりで結構ハマったのでソースコードを載せておきます。
今回わけあったりでコンストラクタ側でProxyの情報を保持していなかったりしますが、そこはお好きに改変してお使いください。

#########################################################
#
# LWP::UserAgentを使ったHTTPS接続
#
#########################################################
use utf8;
use strict;
use warnings;

{
    package ProxyWebGet;

    use LWP::UserAgent;

    # コンストラクタ
    sub new {
        my ($class, @args) = @_;
        my %args = ref $args[0] eq 'HASH' ? %{$args[0]} : @args;
        my $self = {%args};

        # オプション項目
        $self->{ac_timeout} ||= 10;    # プロキシ接続時のタイムアウト(秒)

        return bless $self , $class;
    }

    # ホストとポート番号に対してProxy接続を行う
    # 引数にホストとポート番号と接続先URLを指定する
    sub proxy_connect {
        my $self = shift;
        my $proxy_host = shift; # ホストを受け取る
        my $proxy_port = shift; # ポート番号を受け取る
        my $target_url = shift; # 接続先URL

        # ホストとポート番号をLWP::UserAgentで利用できる形式に整形する
        my $http_proxy = 'http://' . $proxy_host . ':' . $proxy_port;

        $ENV{PERL_NET_HTTPS_SSL_SOCKET_CLASS} = 'Net::SSL';
        $ENV{PERL_LWP_SSL_VERIFY_HOSTNAME}    = 0;
        $ENV{HTTPS_PROXY}   = $http_proxy;
        $ENV{HTTPS_VERSION} = 3;

        # LWP::UserAgentのインスタンスの生成
        my $ua = LWP::UserAgent->new(
            timeout => $self->{ac_timeout} # オプションにタイムアウト時間を指定
        );

        # ヘッダーにUTF-8を含む場合エラーが出るため解析しない
        $ua->parse_head(0);

        # 整形したProxyを指定
        $ua->proxy(['http'], $http_proxy);

        # 指定されたURLへ接続する
        # FTP等のプロトコルに接続を行った場合
        # コネクションが維持されるため強制的に破棄する
        my $res = "";
        eval{
            local $SIG{ALRM} = sub{die "timeout"};
            alarm($self->{ac_timeout});   # タイマー設定
            $res = $ua->get($target_url); # URLへ接続しコンテンツを取得
            alarm(0);                     # タイマー解除
        };

        # ステータスコードが正常である場合、コンテンツを返す
        my $result = $res->is_success() ? $res->content() : "";

        return $result;
    }
}

{
    # sub main
    my $pwg = ProxyWebGet->new();

    print $pwg->proxy_connect(
              '000.000.000.000',
              '0000',
              'https://ja.wikipedia.org/wiki/'
          );
}