Skip to content

Instantly share code, notes, and snippets.

@xnuk
Last active April 27, 2019 08:48
Show Gist options
  • Save xnuk/4a7e7424435408b8f90f000db4dbb32e to your computer and use it in GitHub Desktop.
Save xnuk/4a7e7424435408b8f90f000db4dbb32e to your computer and use it in GitHub Desktop.
tsconfig.json:
compilerOptions:
baseUrl: '.'
outDir: dist
rootDirs:
- src
- app
- modulize
module: commonjs
target: es6
lib:
- es2017
- es2018
- esnext
sourceMap: false
allowJs: true
moduleResolution: node
strict: true
alwaysStrict: true
noUnusedLocals: true
noImplicitThis: true
noUnusedParameters: true
noImplicitReturns: true
forceConsistentCasingInFileNames: true
suppressImplicitAnyIndexErrors: true
package.json:
name: kobis
version: 1.0.0
author: Xnuk Shuman
license: BSD-3-Clause
scripts:
run: ts-node wow.ts
dependencies:
node-html-parser: ^1.1.15
devDependencies:
'@types/node': ^11.13.7
ts-node: ^8.1.0
typescript: ^3.4.5
#!/usr/bin/env ruby
require 'yaml'
require 'json'
DIR = File.dirname __FILE__
CONFIG_PATH = ARGV[0] || File.join(DIR, 'config.yaml')
abort unless File.exists? CONFIG_PATH
YAML.safe_load(File.read CONFIG_PATH).to_h.each do |key, value|
File.write(
File.join(DIR, key),
JSON.pretty_generate(value, 'indent' => 2) + "\n",
)
end
import { get } from 'http'
import { stringify } from 'querystring'
const firefox = 'Mozilla/5.0 (X11; Linux x86_64; rv:67.0) Gecko/20100101 Firefox/67.0'
export const request = (url: string, query?: {[key: string]: string}) =>
new Promise<string>((ok, err) =>
get(
url + (query ? '?' + stringify(query) : ''),
{ headers: { 'User-Agent': firefox } },
res => {
res.setEncoding('utf8')
let data: string = '';
res.on('data', ch => data += ch)
res.on('end', () => ok(data))
}
).on('error', e => err(e))
)
import { parse, HTMLElement, TextNode } from 'node-html-parser'
import { request } from './http'
export interface Theater {
code: string
name: string
city: string
area: string
}
const zipWith = <A, B, T>(a: A[], b: B[], func: (a: A, b: B) => T): T[] =>
Array.from({length: a.length > b.length ? b.length : a.length}, (_, i) => func(a[i], b[i]))
const getText = (v: HTMLElement | TextNode): string => {
if (v instanceof TextNode) {
return v.rawText
}
return v.childNodes
.filter(z => z instanceof TextNode)
.map(z => z.rawText).join(' ')
}
const parser = (html: string): Theater[] => {
const el = parse(html, {
script: false,
style: false,
lowerCaseTagName: false,
pre: false
})
if (el instanceof TextNode) return []
const table = el.querySelector('table.tbl_exc')
const ths = table.querySelector('thead')
.querySelectorAll('th')
.map(getText)
const trs = table.querySelector('tbody')
.querySelectorAll('tr')
.map(v => v.querySelectorAll('td').map(getText))
return trs.map(row =>
Object.assign({}, ...zipWith(ths, row, (k, v) => ({[k]: v})))
).filter(row => row.영업상태 === '영업').map(row => ({
code: row.영화상영관코드,
name: row.영화상영관명,
city: row.광역단체,
area: row.기초단체,
}))
}
const url = 'http://www.kobis.or.kr/kobis/business/mast/thea/findTheaterInfoListXls.do'
export const download = async () => JSON.stringify(parser(await request(url)))
if (require.main === module) {
download().then(v => console.log(v))
}
import { Theater } from './theaters'
import { request } from './http'
import { readFileSync } from 'fs'
interface Schedule {
screen: string
movie: string
code: string
timetable: string[]
}
// const trace = <T>(a: T): T => (console.log(a), a)
const url = 'http://www.kobis.or.kr/kobis/business/mast/thea/findSchedule.do'
const searchMethod = (from: string, to: string) => {
if (!from) {
throw new Error('')
}
return from.includes(to)
}
const schedule = async (theater: string, yyyymmdd: string): Promise<Schedule[]> => {
const data = JSON.parse(await request(url, {theaCd: theater, showDt: yyyymmdd}))
return data.schedule.map(({scrnNm, movieNm, movieCd, showTm}: any) => ({
screen: scrnNm,
movie: movieNm || '',
code: movieCd,
timetable: showTm.split(',')
}))
}
const search = (theater: ReadonlyArray<Theater>) =>
async (areaname: string, name: string, yyyymmdd: string) => {
const theaters = theater.filter(({area, city}) =>
area && searchMethod(area, areaname) || city && searchMethod(city, areaname)
)
const schedule_by_theater = await Promise.all(
theaters.map(v =>
schedule(v.code, yyyymmdd).then(
z => ({ schedule: z, theater: v }),
e => {
throw Error(e);
}
)
)
)
return schedule_by_theater.map(s => {
if (!s) return null
const schedules = s.schedule.filter(v => searchMethod(v.movie, name))
if (schedules.length <= 0) return null
return {theater: s.theater, schedule: schedules}
}).filter((v: {theater: Theater, schedule: Schedule[]} | null): v is Exclude<typeof v, null> => v != null)
}
const prettyPrint = ({name, city, area}: Theater, schedules: Schedule[]): string => `
${area} ${city} ${name}
${schedules.map(({screen, movie, timetable}) =>`
${movie} (${screen}): ${timetable.map(t => t.replace(/([0-9]{2})$/, ':$1')).join(' ')}
`.trim()).join('\n')}
`.trim()
;(async () => {
if (require.main === module) {
const inst = search(JSON.parse(readFileSync('./theaters.json', 'utf8')))
console.log((await inst('강남구', '미성년', '20190427')).map(({theater, schedule}) => prettyPrint(theater, schedule)).join('\n\n'))
}
})()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment