API 인증 장벽 없이 CQC 케어 레지스터를 스크랩한 방법

발행: (2026년 5월 29일 AM 06:21 GMT+9)
5 분 소요
원문: Dev.to

출처: Dev.to

API 인증 장벽

CQC는 API를 api.service.cqc.org.uk 로 이전하고 베어러‑토큰 인증을 추가했습니다. 개발자 포털에 등록하고 애플리케이션을 만든 뒤, 모든 요청에 Authorization: Bearer <token> 헤더를 포함해야 합니다. 이는 엔터프라이즈 사용 사례에 치명적인 문제는 아니지만, 사용자가 CQC에 먼저 등록해야 액터를 실행할 수 있기 때문에 데이터 제품에 마찰을 추가합니다.

이전 API 베이스(api.cqc.org.uk/public/v1)는 이제 HTTP 403을 반환합니다 – 완전히 차단된 상태입니다.

오픈‑데이터 파일 구조

CQC는 매월 HSCA_Active_Locations.ods 라는 오픈‑데이터 파일을 공개합니다. 이 파일은 영국(≈ 56 000) 내 모든 활성 규제 시설을 담은 23 MB 규모의 OpenDocument 스프레드시트입니다. 파일은 무료이며 인증이 필요 없고, Open Government Licence 하에 배포됩니다. URL은 날짜가 포함되어 매달 바뀌지만, 투명성 페이지에서는 항상 최신 버전으로 연결됩니다.

접근 방법

  1. 투명성 페이지를 스크랩해 현재 ODS URL을 찾는다.
  2. 파일을 다운로드한다.
  3. 파싱하고, 행을 필터링한 뒤 결과를 푸시한다.

API도 없고 인증 장벽도 없습니다.

ODS 파싱 과제

ODS 파일(.ods)은 XML을 담은 ZIP 아카이브입니다. Node.js에서 이를 파싱하기 위한 표준 도구는 SheetJS(xlsx 패키지, v0.18.5 – 마지막 Apache 2.0 릴리즈)입니다.

시트 선택

워크북에는 README, HSCA_Active_Locations, Dual_Registration_Locations 세 개의 시트가 들어 있습니다. SheetJS는 기본적으로 첫 번째 시트(README)를 사용하며, 이 시트는 34행밖에 없습니다. 아래 코드는 행 수가 가장 많은 시트를 선택합니다:

let bestSheet = null;
let bestCount = 0;

for (const name of workbook.SheetNames) {
    const sheet = workbook.Sheets[name];
    const probe = XLSX.utils.sheet_to_json(sheet, { header: 1 });
    if (probe.length > bestCount) {
        bestCount = probe.length;
        bestSheet = sheet;
    }
}

컬럼 이름 처리

컬럼 이름은 Title Case와 공백으로 구성됩니다(예: Location Local Authority, Location Latest Overall Rating). 서드파티 문서에 나와 있는 snake_case와 다릅니다. 여러 변형을 시도해 보는 헬퍼 함수를 사용하면 향후 이름이 바뀌어도 파서를 견고하게 유지할 수 있습니다:

function col(row, ...candidates) {
    for (const name of candidates) {
        if (row[name] !== undefined) return row[name];
    }
    return undefined;
}

날짜 처리

워크북을 { cellDates: true } 옵션으로 읽으면 날짜가 JavaScript Date 객체로 반환되고, 좌표는 문자열로 반환됩니다. 두 값 모두 처리 단계에서 정규화합니다.

const workbook = XLSX.read(buffer, { type: 'buffer', cellDates: true });

보너스: 서비스 유형 및 사용자 밴드 추출

ODS 파일에는 122개의 컬럼이 있으며, 그 중 약 60개는 특정 서비스 유형 및 사용자 밴드에 대한 불리언 플래그(Y/null)입니다(예: Service type - Care home service with nursing). 각 컬럼을 일일이 매핑하는 대신, 활성화된 컬럼을 배열로 추출합니다:

function extractTagged(row, prefix) {
    return Object.entries(row)
        .filter(([key, val]) => key.startsWith(prefix) && (val === 'Y' || val === true))
        .map(([key]) => key.slice(prefix.length));
}

결과 출력

{
  "serviceTypes": ["Care home service without nursing"],
  "serviceUserBands": ["Dementia", "Older People"]
}

결과

이 액터는 두 가지 모드를 지원합니다:

  • searchLocations – 지역, 평점, 서비스 유형으로 필터링.
  • lookupProviders – 특정 제공자 ID에 해당하는 모든 위치를 조회.

매 실행 시 23 MB ODS 파일을 로드하고 파싱하는데 약 10–15초가 소요되며, 이후 메모리 내에서 필터링합니다. 프록시, 브라우저 자동화, 안티‑봇 처리 없이 동작합니다.

Apify Store에서 이용 가능: CQC Care Register Scraper

0 조회
Back to Blog

관련 글

더 보기 »

SiteRows 예시 #1:

빠른 알림: SQL을 사용해 웹 콘텐츠를 스크랩할 수 있습니다, 마치 데이터베이스를 쿼리하듯이. FREE 계정을 만들면 자동화 기능과 더 높은 사용량을 이용할 수 있습니다.

첫 포스트: 짧은 전기

Introduction Hello, my name is Jay. Growing up, I wanted to follow in my dad's footsteps and become an engineer—and I did, just not in the way I originally exp...