AWSを使った「なろう」WebAPIの作成

「蜘蛛ですが何か?」が面白いと妻から聞いたので「なろう」のWebAPIをAWSで作ってみました。

構成

CloudFront=>APIGateway=>Lambda=>SocksProxy=>なろうサイト

  1. CloudFront
    • キャッシュサーバ SocksProxyを経由したために遅くなったレスポンスの改善&なろうサイトのサーバに負荷をかけないため設置
    • キャッシュに載せるには MethodをGetにする必要あり
  2. APIGateway
    • HttpRequest受け口
    • path paramter を取得してeventへセットしてくれるよう設定
  3. Lambda
    • リクエスト処理&html取得&Parse&整形&Response返却
    • nodejsで作成
    • httpリクエストはaxiosを使用
    • axiosでSocksProxyを使うためsocks-proxy-agentを使用
    • html parseはcheeroを使用(JQueryのようにSelectorでElementの取得が可能)
    • ResponseHeaderにAccess-Control-Allow-Originを追加することでcors制限を回避
  4. SocksProxy
    • なろうサイトがAWSIPアドレス帯からの接続を拒否しているための迂回
    • 今回はPIA VPNにくっ付いていたSocksProxyを使用
  5. なろうサイト
    • タイトルと本文を取得

リクエストサンプル

path parameterに本のIDと章を渡すとJSONでtitleとcontents(行単位のarray)が返却されます。

下記は蜘蛛ですが何かのの一つ目がJSONで返却されるサンプル(そのうち止めます)

curl https://d1oy3e79o7znce.cloudfront.net/fastread/n7975cr/1

上記URLに対応するなろうサイトのURLはこちら。

https://ncode.syosetu.com/n7975cr/1/

Lambdaのコードはこんな感じ

const axios = require('axios')
const cheerio = require('cheerio')
const SocksProxyAgent = require('socks-proxy-agent');
const bookBaseUrl = 'https://ncode.syosetu.com/';
const proxyHost = '**********';
const proxyPort = '****';
const proxyOptions = `socks5://${proxyHost}:${proxyPort}`;
const httpsAgent = new SocksProxyAgent(proxyOptions);

let response;

exports.lambdaHandler = async (event, context) => {
    try {
        let bookId = event.pathParameters.bookId;
        let chapterId = event.pathParameters.chapterId;
        let chapter = await exports.getChapter(bookId, chapterId);
        response = {
            'statusCode': 200,
            'body': JSON.stringify({
                title: chapter.title,
                contents: chapter.contents
            }),
            'headers': {
                'Access-Control-Allow-Origin': '*'
            }
        }
    } catch (err) {
        console.log(err);
        return err;
    }

    return response
};

exports.getChapter = async (bookId, chapterId)=> {
  const url = exports.makeChapterUrl(bookId, chapterId);
  let resp = await axios.get(url, {httpsAgent: httpsAgent});
  const $ = cheerio.load(resp.data);
  let title = $('p.novel_subtitle').text();
  let contents = [];

  for(let i = 1; ; i++ ){
    if ($(`#L${i}`).length == 0){
      break;
    }
    contents.push($(`#L${i}`).text());
  }
  return {
    title: title,
    contents: contents
  };
}

exports.makeChapterUrl = (bookId, chapterId)=> {
  return `${bookBaseUrl}${bookId}/${chapterId}/`;
}

template.yamlのpath paramter設定部分

          Properties:
            Path: /fastread/{bookId}/{chapterId}

参考文献 qiita.com

github.com

github.com

www.privateinternetaccess.com

github.com