공부/스크래핑

[파이썬] 정적 페이지 스크래핑 해보기 - 광명시 청년동 데이터 수집

spine_sunbi 2023. 10. 28. 15:33
반응형

크롤링이냐, 스크래핑이냐 해석의 차이이지만 제가 작성한 코드가 차마 크롤링의 수준까지는 가지 못했기 때문에 크롤링보다는 스크래핑이라는 용어를 선택해서 작성하기로 했습니다. 광명시의 청년 정책 및 정보들을 모아서 뉴스레터를 만들어볼까?라는 조그마한 목표를 가지고 있습니다. 근데 냅다 뉴스레터 시작하는 것보다는 블로그를 통해서 소식들을 공유하고, 사람들의 수요가 있을 때 만들어 보는 걸로 하겠습니다. (아무도 안 궁금하신가요?)

 

광명시에는 '광명시 청년동' 이라는 청년 전용의 공간이 있습니다. (39세? 까지 이용이 가능하다는데 뭐 입장을 통제하는 것도 아니고 누구나 들어가실 순 있겠죠?) 광명시는 나름 청년 친화 도시라고 생각하고 있습니다. 청년동에서는 청년들이 참여할 만한 프로그램들이 있는데요. 이 정보들을 먼저 한 번 수집해 보겠습니다.

 

광명시 청년동 프로그램 스크래핑

import requests
from bs4 import BeautifulSoup

# 청년동 프로그램 신청 - 모집중 탭 (정적 페이지)
response = requests.get("https://gmyouthzone.org/program/01.php?pr=ing")
html = response.text
soup = BeautifulSoup(html, "html.parser")
programs_gmyouth = soup.select(".box")  # 클래스(.), id(#), 자식(>), 태그(이름)

# 수집 시점의 모집중인 프로그램 수
print(len(prgrams_gmyouth))

for program in programs_gmyouth:
    title = program.select_one(".txt_t").text

    # 신청기간 및 진행기간이 클래스나 아이디로 구분되어 있지 않아서 이런식으로 구분함 (둘 다 <span class="date">로 되어있음)
    period_recep = program.select(".date")[0].text
    period_going = program.select(".date")[1].text

    # ../program/01_view.php?no=232&pr=ing&page=1 <<< 주소가 이런식으로 써있어서 ..를 replace함
    link = program.attrs["href"].replace("..", "https://gmyouthzone.org")
    print(title)
    print(link)
    print(period_recep)
    print(period_going)
    print()

[출력]

청년동 합창단 [With]
https://gmyouthzone.org/program/01_view.php?no=150&pr=ing&page=1
신청기간 2023-07-01 ~ 2023-12-31
진행기간 2023-07-29 ~ 2023-12-16

One day class - 실손보험
https://gmyouthzone.org/program/01_view.php?no=241&pr=ing&page=1
신청기간 2023-10-21 ~ 2023-11-09
진행기간 2023-11-16 ~ 2023-11-16

One day Class - 노션 (대학생/직장인)
https://gmyouthzone.org/program/01_view.php?no=235&pr=ing&page=1
신청기간 2023-10-21 ~ 2023-11-07
진행기간 2023-11-14 ~ 2023-11-14

진로집단상담 2기
https://gmyouthzone.org/program/01_view.php?no=242&pr=ing&page=1
신청기간 2023-10-19 ~ 2023-11-03
진행기간 2023-11-08 ~ 2023-11-29

일반청년 마음건강 프로그램
https://gmyouthzone.org/program/01_view.php?no=245&pr=ing&page=1
신청기간 2023-10-20 ~ 2023-11-02
진행기간 2023-11-05 ~ 2023-12-31

끼리끼리 나눠보자 자조모임
https://gmyouthzone.org/program/01_view.php?no=248&pr=ing&page=1
신청기간 2023-10-25 ~ 2023-11-02
진행기간 2023-11-05 ~ 2023-12-31

김장봉사 신청하기
https://gmyouthzone.org/program/01_view.php?no=250&pr=ing&page=1
신청기간 2023-10-26 ~ 2023-11-01
진행기간 2023-11-03 ~ 2023-11-03

인생학교 6기
https://gmyouthzone.org/program/01_view.php?no=240&pr=ing&page=1
신청기간 2023-10-20 ~ 2023-10-31
진행기간 2023-11-01 ~ 2023-12-16

원데이_모루 인형 만들기
https://gmyouthzone.org/program/01_view.php?no=233&pr=ing&page=1
신청기간 2023-10-18 ~ 2023-10-28
진행기간 2023-11-03 ~ 2023-11-04

광명시 청년동의 '프로그램' 페이지를 들어가면, 전체/모집 중/마감이라는 탭이 있습니다. 다행히도 이 세 가지 탭의 url은 다릅니다. 제가 필요한 것은 모집 중인 프로그램의 정보이니 'pr=ing'에 해당하는 주소를 지정했습니다.

response = requests.get("https://gmyouthzone.org/program/01.php?pr=ing")
html = response.text
soup = BeautifulSoup(html, "html.parser")
programs_gmyouth = soup.select(".box")  # 클래스(.), id(#), 자식(>), 태그(이름)

 

프로그램은 신청기간에 따라서 구분됩니다. 따라서 매 번 개수가 달라질 수도 있습니다. 그러기 위해선 스크래핑하는 순간에 몇 개인지 파악을 해야 합니다. 그래서 개발자 도구(F12)를 이용해서 하나의 프로그램을 나타내는 단위를 찾아보았습니다.

 

제가 수집할 정보는 아래의 세 가지입니다.

1. 프로그램 명칭

2. 프로그램 신청기간

3. 프로그램 진행기간

이 세 가지를 포함하는 최소 단위는 class="box"인 a태그입니다.

box의 개수를 세면 총 9개가 출력됩니다. 현재 진행 중인 프로그램 수를 직접 세어봐도 9개입니다. 두 개의 수가 일치하니 다음 작업으로 넘어갑니다.

print(len(prgrams_gmyouth))

[출력]

9

 

9번의 반복을 통해 각 프로그램의 명칭, 신청기간 및 진행기간을 추출해 보겠습니다.

제목에 해당하는 '청년동 합창단 [With]'은 class가 "txt_t"로 a태그 내에 하나입니다. 그러나, 신청기간과 진행기간은 둘 다 class 이름이 'date'로 동일합니다. 이 문제는 인덱스를 이용해서 해결했습니다.

 

select는 결과값을 list 형태로 반환해줍니다. 그렇기 때문에 인덱스를 이용해서 신청기간과 진행기간을 분리할 수 있었습니다. select의 결과값을 직접 확인해 보시고 싶은 분은 주석을 없애고 실행해 보세요.

 

전체 프로그램을 첨부한 그림에서 볼 수 있듯이, 신청기간이 처음으로 작성되고, 그다음이 진행기간입니다. 따라서 신청기간(period_recep)은 [0]으로 인덱스를 지정했고, 진행기간(period_going)은 [1]로 지정했습니다. ('date'라는 클래스명은 신청기간과 진행기간에만 사용되었습니다.)

 

select_one은 제일 처음 나오는 하나만 선택하는 것이고, select는 전부를 선택합니다.

. date를 지정하는 데에 select_one을 사용했다면 원하는 결과를 얻을 수 없습니다. (. date가 2개니까요.) (구멍이 두 개지요)

select를 쓰셔야 하는 점 참고 부탁드립니다.

 

뒤에 붙는. text는 태그나 자질구레한 것들 제외하고, 텍스트 값만을 얻기 위함입니다.

for program in programs_gmyouth:
    title = program.select_one(".txt_t").text

    # 신청기간 및 진행기간이 클래스나 아이디로 구분되어 있지 않아서 이런식으로 구분함 (둘 다 <span class="date">로 되어있음)
    # print(program.select(".date") >>> 주석을 제거하고 확인해 보세요.
    period_recep = program.select(".date")[0].text
    period_going = program.select(".date")[1].text

    # ../program/01_view.php?no=232&pr=ing&page=1 <<< 주소가 이런식으로 써있어서 ..를 replace함
    link = program.attrs["href"].replace("..", "https://gmyouthzone.org")
    print(title)
    print(link)
    print(period_recep)
    print(period_going)
    print()

 

질문이 있으시다면, 댓글 달아주세요. 제가 아는 선에서 답변드리겠습니다.

혹은, 개선할 점을 말씀 주셔도 좋습니다 ㅎㅎ