Files
jhogyuandClaude Fable 5 3d8d90dcdb chore: sync working tree — Stage 1 v.7 reorg, jikji indexes, claim-drafting docs
- Rename "Claude YAML"/"Codex YAML" folders to Claude_YAML/Codex_YAML in Stage_1 v.7
- Add Stage_1 v.7 extension research, results runs, and reference material
- Add/refresh .jikji search indexes (Case_02 and Default_Agent corpora)
- Add per-case-type claim drafting folders and PDFs (청구취지기재방법)
- Add 판례모음 crawling data (08_14, 08_18)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-19 14:07:35 +09:00

195 lines
8.3 KiB
YAML

# CNU 도서관 → lawnb 판례 검색·일괄 다운로드
# (기존 Python/Selenium 크롤러의 DSL 이식판)
#
# 실행 예:
# crawler run scripts/lawnb_cases.yaml -p username=학번 -p password=비밀번호 -p query=손해배상
# 대시보드에서는 잡 생성 시 params로 실제 값을 넣으세요 (YAML에 비밀번호를 저장하지 말 것).
meta:
name: lawnb_case_downloads
description: >
CNU 도서관 로그인 → lawnb 진입 → 판례 검색(사건명/선고일) →
결과 전 페이지의 판례 URL 수집 → 각 판례 원문을 Word로 다운로드
start_url: "https://library.cnu.ac.kr/login"
params:
username: "201610058"
password: "jAqqe8-wewrem-nuwgob"
query: "가등기, 본등기" # 사건명 검색어
date_from: "19900101" # 선고일 시작, YYYYMMDD (예: 20230101) — 비우면 전체 기간
date_to: "20260731" # 선고일 종료, YYYYMMDD
# 사건종류 필터: 헌법/민사/형사/가사/행정/특허/선거 중 선택 (빈 리스트 []면 전체)
case_type: ["민사"]
# 법원등급 필터: 화면 라벨 그대로 나열 (빈 리스트 []면 전체)
# 체크박스 value로도 매치되므로 "대법"/"전합"/"고법"/"지법" 같은 코드값도 사용 가능
court_type: ["대법원", "대법원(전원합의체)", "고등법원", "지방법원"]
limits:
max_pages: 100 # 검색 결과 페이지 수 상한
max_items: 2000 # 수집 판례 수 상한
step_timeout_s: 15
page_load_timeout_s: 60
heal_attempts: 2
output:
format: jsonl
fields: [title, url]
login:
- action: navigate
url: "${meta.start_url}"
- action: type
selector: { css: "input#id", intent: "도서관 로그인 폼의 학번/아이디 입력 필드" }
text: "${username}"
- action: type
selector:
xpath: "//*[@id='login']/fieldset/div[2]/dl[2]/dd/input"
css: "#login input[type=password]"
intent: "도서관 로그인 폼의 비밀번호 입력 필드"
text: "${password}"
- action: click
selector:
xpath: "//*[@id='login']/fieldset/div[2]/p/input"
intent: "도서관 로그인 폼의 로그인 제출 버튼"
- action: sleep
seconds: 3
# 도서관 프록시(outlink)를 거쳐 lawnb로 이동 — 세션 인증이 전달됨
- action: navigate
url: "https://library.cnu.ac.kr/outlink?moduleId=schosite&targetUrl=https://cnu.lawnb.com"
- action: sleep
seconds: 5
# 신규 UI가 뜨면 '기존 화면'으로 전환 (이미 구 UI면 그냥 통과)
- action: if
exists:
xpath: "/html/body/header/div/div/button[1]"
intent: "lawnb 신규 UI 상단의 '기존 화면' 전환 버튼"
then:
- action: click
selector:
xpath: "/html/body/header/div/div/button[1]"
intent: "lawnb 신규 UI 상단의 '기존 화면' 전환 버튼"
- action: sleep
seconds: 3
collect_urls:
- action: navigate
url: "https://academynext-lawnb-com.libra.cnu.ac.kr/Info/ContentMain/Case"
- action: wait_for
selector: { css: "#sSubWord5", intent: "판례 검색 페이지의 사건명 검색어 입력창" }
- action: type
selector: { css: "#sSubWord5", intent: "판례 검색 페이지의 사건명 검색어 입력창" }
text: "${query}"
- action: type
optional: true
selector: { css: "#cDate1", intent: "선고일 검색 범위의 시작일(YYYYMMDD) 입력 필드" }
text: "${date_from}"
- action: type
optional: true
selector: { css: "#cDate2", intent: "선고일 검색 범위의 종료일(YYYYMMDD) 입력 필드" }
text: "${date_to}"
# 사건종류 필터 — params.case_type의 각 항목을 라벨 텍스트/값으로 찾아 체크.
# [not(@checked)] 가드: 이미 켜진 박스는 매치되지 않아 재클릭(해제)을 방지.
- action: foreach
over: case_type
as: ct
return_to_list: false
steps:
- action: click
optional: true
heal: false
timeout_s: 3
selector:
xpath: >-
//input[@name='cType2'][not(@checked)][@value='${ct}'
or normalize-space(following-sibling::text()[1])='${ct}']
| //label[normalize-space(.)='${ct}']//input[@name='cType2'][not(@checked)]
| //input[@name='cType2'][not(@checked)][@id = //label[normalize-space(.)='${ct}']/@for]
intent: "사건종류 필터에서 '${ct}' 항목의 체크박스(미선택 상태)"
# 법원등급 필터 — params.court_type의 각 항목을 라벨 텍스트/값으로 찾아 체크.
- action: foreach
over: court_type
as: court
return_to_list: false
steps:
- action: click
optional: true
heal: false
timeout_s: 3
selector:
xpath: >-
//input[@name='cType3'][not(@checked)][@value='${court}'
or normalize-space(following-sibling::text()[1])='${court}']
| //label[normalize-space(.)='${court}']//input[@name='cType3'][not(@checked)]
| //input[@name='cType3'][not(@checked)][@id = //label[normalize-space(.)='${court}']/@for]
intent: "법원등급 필터에서 '${court}' 항목의 체크박스(미선택 상태)"
- action: click
selector: { css: "#searchBtn", intent: "판례 검색 실행 버튼" }
- action: wait_for
selector: { css: ".currentPage", intent: "검색 결과 하단 페이지네이션의 현재 페이지 번호 표시" }
- action: paginate
next_selector:
css: ".currentPage + a"
intent: "현재 페이지 번호 바로 다음에 오는 이동 링크 (다음 페이지 번호 또는 다음 페이지 묶음)"
stop_if_missing: true
steps:
- action: extract_list
save_as: cases
item_selector:
css: "article ol > li"
xpath: "/html/body/div/div/div[1]/div[2]/article/div[1]/div[4]/ol/li"
intent: "판례 검색 결과 목록에서 판례 한 건에 해당하는 리스트 항목"
fields:
- name: title
selector: { css: "h3 a", intent: "판례 제목 링크의 텍스트" }
- name: url
selector: { css: "h3 a", intent: "판례 상세 페이지로 가는 링크" }
attr: href
absolute_url: true
required: true
collect_data:
- action: foreach
over: cases
as: item
return_to_list: false
steps:
- action: navigate
url: "${item.url}"
- action: sleep
seconds: 2
- action: if
exists:
xpath: "/html/body/div[1]/div[1]/header/div/div/a[3]"
intent: "판례 뷰어 상단 헤더의 저장(다운로드 창 열기) 버튼"
then:
- action: click
selector:
xpath: "/html/body/div[1]/div[1]/header/div/div/a[3]"
intent: "판례 뷰어 상단 헤더의 저장(다운로드 창 열기) 버튼"
# 저장 대화상자의 '링크 표시' 옵션이 켜져 있으면 해제
- action: if
exists:
xpath: "/html/body/div[1]/div[1]/div[3]/div/div[2]/div[2]/div/div[2]/input[1][@checked]"
intent: "저장 대화상자에서 켜져 있는 링크 표시 옵션 체크박스"
then:
- action: click
selector:
xpath: "/html/body/div[1]/div[1]/div[3]/div/div[2]/div[2]/div/div[2]/input[1]"
intent: "저장 대화상자의 링크 표시 옵션 체크박스"
# 파일 형식은 기본값(PDF)으로 그대로 두고 바로 다운로드.
# PDF로 파일이 안 나오면 alt_formats로 MS Word(.doc)를 눌러 재시도.
# (형식 라디오: PDF=0 / HWP=1 / MS Word=2 / TXT=3, value 기준)
- action: download
selector:
xpath: "/html/body/div[1]/div[1]/div[3]/div/div[2]/div[2]/div/div[8]/div/button"
intent: "저장 대화상자의 최종 다운로드 실행(저장) 버튼"
wait_s: 20
alt_formats:
- css: "input[type=radio][value='2']"
intent: "저장 대화상자의 파일 형식 'MS Word(.doc)' 라디오 (PDF 실패 시 대체)"
else:
# 구형 ebook 뷰어(iframe 내부 다운로드)는 DSL이 iframe 전환을
# 지원하지 않아 처리 불가 — 증적 스크린샷만 남기고 다음 판례로
- action: screenshot
name: "ebook_viewer_unsupported"