- 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>
195 lines
8.3 KiB
YAML
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"
|