다른 공지 파싱 예제


이번에는 조금 더 복잡한 페이지를 파싱해보도록 하겠습니다. 예제로 사용했던 '동국대학교 공지사항'보다 조금 더 복잡한 '동국대학교 각 대학별 공지사항'을 파싱해보겠습니다. 동국대학교 공과대학 홈페이지입니다.

먼저 주소는 (http://engineer.dongguk.edu/bbs/board.php?bo_table=en6_1&page=1)입니다.

테이블 찾기

'페이지 소스 보기'로 원하는 데이터가 어디에 있는지 찾아보겠습니다. 이 페이지도 데이터들은 역시나 테이블에 있습니다. 하지만 일반 공지사항과 다르게 구조가 조금 다릅니다. 그래서 "조금 더 복잡한 소스 파싱하기"라고 이름을 붙였습니다. 보통은 하나의 학교에서 여러 게시판을 만들면 구조를 깔끔하게 통일시키는게 좋은데 말이죠. =ㅅ= 마음에 안듭니다.

table tag를 검색하면 총 세개가 나옵니다. 그리고 마지막에 있는

이 제가 원하던 테이블입니다. 시작점은 이 곳으로 하겠습니다.

@notices = data.css('table')[2].css('tr')

데이터 찾기

table tag안에 데이터는 tr tag안에 있습니다. 그러나 이번 역시 각각 tr tag에도 공지알림용 공지사항이 있고 일반 공지가 있습니다. 이를 구분하는 방법은 첫 번째 td tag의 span tag안에 있는 값이 숫자인지로 판단할 수 있습니다.

if notice.css('td')[0].css('span').text.strip =~ /\A\d+\z/

세부 데이터 찾기

이번에 수집할 데이터는 제목,링크,작성자,조회수,작성일 입니다. 각각을 wr_title, wr_link, wr_writer, wr_hit, wr_created_on 라고 하겠습니다.

제가 이번 예제를 굳이 만든 이유는 tag라고 해서 모두 다 같은 tag가 아니라는 것을 알려드리고 싶어서 입니다. 페이지 소스를 보면 html tag, td tag, tr tag같은 기본 tag도 있지만 strong tag, nobr tag같이 꾸며주는 일시적인 태그들도 있습니다. 하지만 이런 것들까지 노코기리가 구분해주지는 않습니다. 큰 틀만 구조적으로 의미가 있다고 인식을 하는 것 같습니다. 그래서 tag가 아니라 그냥 String으로 처리가 됩니다.

<td align="left" style="word-break:break-all;">
  <nobr style="display:block; overflow:hidden;">
    <a href="../bbs/board.php?bo_table=en6_1&amp;wr_id=338&amp;page=1">2015-2학기 학점포기안내문</a>
    <img src="../skin/board/basic_notice/img/icon_file.gif" align="absmiddle">
  </nobr>
</td>

위 소스를 보면 안의 "2015-2학기 학점포기안내문" String을 가져오려면, td >> nobr >> a >> string 이렇게 접근을 해야합니다. 좀 길고 복잡해 보입니다. 하지만 노코기리에게 이 형태는 td >> a, img 이렇게 밖에 안보입니다. 즉, nobr은 무시해도 된다는 의미입니다. 단순히 CSS 형태에서 style을 정의해놓은 것에 불과하기 때문에 노코기리에게는 의미있는 태그가 아닙니다.그렇기 때문에 위 내용에서 "2015-2학기 학점포기안내문" 을 뽑으려면 다음과 같이 하면 됩니다. notice가 td tag까지 접근했다고 했을 때

notice.css('td')[1].css('a').text.strip

이는 irb로 확인해보면 이유를 알 수 있습니다. 각각을 nobr tag로 tbody tag로 table tag로 img tag로 파싱한 것들을 쫙 찾아보면 원하는 내용이 있는 것과 없는 것들이 있습니다. 위의 notice.css('td')[1].css('a').text.strip을 notice.css('td')[1].css('nobr a').text.strip이라고 해서 찾으면, 결과 배열 값이 [] 즉, 아무것도 나타나지 않는 것을 알 수 있습니다. 없는 객체을 찾으라고 명령한 샘이 되는 겁니다.

그 다음은 href의 링크를 가져온다고 해보겠습니다.

"http://engineer.dongguk.edu/" + notice.css('td')[1].css('a')[0]['href'].strip.sub(/../,"")

좀 복잡해 보이죠? 이 전 예제와 href안의 링크를 뽑는 방법은 같습니다. 하지만 이번에는 전체 링크가 아니라, 상대 경로를 제공하고 있기 때문에 /../를 없애버리고 앞에 "http:xxx" 부분을 추가해서 링크를 뽑았습니다. sub 메소드의 의미는 구글링으로 사용법을 찾길 바랍니다.


전체적으로 이전 예제와 다르게 css 메소드가 여러번 사용된 것을 볼 수 있습니다. 이전에 css 메소드로 뽑은 값을 다시 css 메소드로 파싱하여 가져오고 싶을 때는 계속 위와 같이 뒤에 붙여주면 됩니다. 왼쪽부터 순서대로 파싱이 적용됩니다.

//작성자 부분
<td align="center" width="110">
  <nobr style="display:block; overflow:hidden; width:105px;">
    <!--<img src="../skin/board/basic_notice/img/1.gif" align="absmiddle">--> 
      <font class="event"> 
        <span class="member">공과대학</span>
      </font>
  </nobr>
</td>

//조회수 부분
<td width="50">
  <span style="font:normal 11px tahoma; color:#777777;"> 1047 </span>
</td>

//작성일 부분
<td width="80">
  <span style="font:normal 11px tahoma; color:#888888;">
    <strong> </strong> 
      2015-09-01 
  </span>
</td>

즉 위에 있는 nobr tag나 부분이나 strong tag같은 부분은 무시하고, 큰 그림인 td, tr, span만 보고 접근하면 쉽게 풀립니다. 이런식으로 적용한 소스를 살펴보면 아래와 같습니다.

url = "http://engineer.dongguk.edu/bbs/board.php?bo_table=en6_1&page=1"

data = Nokogiri::HTML(open(url))
@notices = data.css('table')[2].css('tr')
@notices.each do |notice|
  if notice.css('td')[0].css('span').text.strip =~ /\A\d+\z/
    Engineer.create(
      :wr_title => notice.css('td')[1].css('a').text.strip,
      :wr_link => "http://engineer.dongguk.edu/" + notice.css('td')[1].css('a')[0]['href'].strip.sub(/../,""),
      :wr_writer => notice.css('td')[2].css('span').text.strip,
      :wr_hit => notice.css('td')[3].css('span').text.strip,
      :wr_created_on => notice.css('td')[4].css('span').text.strip
      )
  else
    next
  end
end

튜토리얼을 잘 보셨다면, 위 소스의 의미가 무엇인지 금방 이해하실 수 있을거라고 생각하여 추가 설명은 생략하겠습니다.

results matching ""

    No results matching ""