Last active
April 27, 2019 08:48
-
-
Save xnuk/4a7e7424435408b8f90f000db4dbb32e to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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)) | |
) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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)) | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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