Scrapy의 기본구조

  • Spider는 어떤 웹 사이트들을 어떠한 규칙에 의거하여 크롤링할 것인지 명시하고, 각각의 웹 페이지의 어떤 부분을 스크래핑할 것인지 등을 일괄적으로 명시하는 클래스이다. 즉, 웹크롤링과 웹스크래핑 규칙을 설정하는 핵심요소
  • Spider 상에 웹 페이지 상의 어느 부분을 스크래핑할 것인지 명시하고자 할 때, 특정 HTML 요소를 간편하게 선택할 수 있도록 하는 메커니즘을 scrapy에서는 Selector 클래스로 구현하였다.
    Selector를 사용하면 CSS 선택자를 직접 사용하여 특정 HTML 요소를 선택할 수도 있으나, HTML 상에서의 특정 요소를 선택하는 데 특화된 언어인 'XPath'를 사용하는 것이 더 권장된다. HTML과 CSS의 기초를 잘 알고 있다면, XPath는 몇 개의 예제를 따라서 작성하는 것만으로 금방 학습할 수 있습니다. 만약 XPath에 대해 좀 더 자세하게 배우고 싶다면, 다음의 링크를 참조하시길 바란다.
    http://www.w3schools.com/xsl/xpath_intro.asp
  • Item은 scrapy에서 기본 제공하는 자료구조 클래스입니다. 새로운 Item 클래스를 정의하고 여기에 우리가 수집하고자 하는 정보들을 명시하면, Spider 상에서 실제 스크래핑을 수행한 결과물을, 파일형태로 저장할 때, item을 매개로 사용해서, 간편하게 관리할 수 있다.
  • Item pipeline 클래스를 새로 정의하고 여기에 각 Item들을 어떻게 처리할 것인지 명시하면, 해당 규칙에 의거하여 데이터를 가공하거나 혹은 외부 파일로 간편하게 저장할 수 있다.
  • Spider나 Item pipeline 등이 어떻게 동작하도록 할 지에 대한 세부적인 설정 사항Settings 상에 명시한다
    거의 모든 웹 사이트에서는 크롤링을 수행하는 크롤러 봇들의 행동을 제한하고자 robots.txt라는 파일을 게시한다.
    https://www.flearning.net/robots.txt 처럼, robots.txt에는, 크롤러들이 ~어떻게 행동하라~ 라고 명시되어있다.
    예를 들어, 우리가 제작한 Spider가 robots.txt 파일에 명시된 규칙을 따를 것인지, 혹은 무시할 것인지 등을 Settings 상에서 설정할 수도 있습니다. 서버에서 차단당하기 전에, 완급조절도 Settings상에서 설정가능하다.



Scrapy shell과 크롬 개발자 도구를 사용하여 웹 스크래핑 체험하기

1. Anacond Prompt 에서 activate py27로 파이썬 2.7가상환경을 활성화 시킨 상태로 만들자.(scrapy는 py3버전을 지원하지않는다)
activate py27

2. 지정한 웹 페이지를 스크래핑한 결과를 인터랙티브하게 확인할 수 있도록 하는 기능인 scrapy shell을 실행해보자.
scrapy shell http://gall.dcinside.com/board/lists/?id=oriental_medicine

image

3. 웹사이트의 제목을 가져오기 위해서,  페이지 소스보기를 눌러서 어떤 계층을 타고내려왔는지 찾아야하는데,
   크롬에서 제공하는 개발자 도구[F12]를 사용해서 더 간편하게 할 수 있다.
   개발자도구에서 [Elements]를 클릭하면 html소스를 볼 수 있고,  [좌측상단 마우스]아이콘 클릭 후 가져올 부분을 클릭하면,
   html 코드가 하이라이트로 표시된다.
image
이 상태에서 하이라이트된 코드를 [우클릭 ] > copy > [ xpath copy]를 선택하여 해당 html코드를 xpath형태로 복사해오게 된다.

4. 다시 scrapy shell 로 돌아와서 , response.xpath(‘’) 함수를 미리 쳐놓고, 작은따옴표 사이에다가 복사한 xpath를 붙혀넣는다.
   이 때, response라는 것은, scrapy shell작동시 적은 url에 웹페이지 요청을 보낸 것에 대한 응답을 담고 있는 변수이다.
    특정요소의 xpath를 xpath함수의 인자로 넣으면, xpath에 부합하는 해당요소를 나타내는 selector 객체를 리턴받을 수 있다.
   ( 디씨인사이드는 js나 ajax로 만든 동적웹페이지라서,,  selector 반환이 안된다. 그래서 stackoverflow로 바꿨다.)
   이 때, data= 이후부분의 정보를 긁어오는 것이다.
image
<Xpath>
xpath(‘’)안에 들어가는 xpath인자에서
// 는 자손요소들을 모두 살펴본다(가장 상위의 html의 )
/ 는 /앞부분의 직계자식 요소만 살펴보는데, /뒷부분의 조건에 맞는 놈을 찾는다.
* 는 모든 종류의 태그를 찾는다.
[@id =”” ] id값 조건을 명시하는 부분이다. 클래스라면 @class=”” 형식으로 준다.
[1] 대괄호는 인덱싱이다. 주의할 점은 xpath안의 html인덱스는 정수 1부터 시작한다!!

5. xpath인자의 마지막에   /text()를 붙히고 xpath함수를 호출하면, 텍스트만 뽑아내서 selector에 담긴
  그 뒤에 extract()함수까지 적용하면 Selector에  담긴 텍스트를 추출해서 표시한다.
response.xpath('//*[@id="question-summary-49154165"]/div[2]/h3/a/text()').extract()
image
원래 selector에는 리스트형식으로 담긴다. 여기서는 제목 1개부분만 추출하였으므로, extract() 추출한 리스트 중 첫번째[0]을 호출한 것과 동일하다.
response.xpath('//*[@id="question-summary-49154165"]/div[2]/h3/a/text()').extract()
response.xpath('//*[@id="question-summary-49154165"]/div[2]/h3/a/text()').extract()[0]

image


6.  이제 제목을 하나하나 가져올 것이 아니라, 전체 리스트로 가져올 방법을 연구해보자.
    개발자 도구의 하이라이트 부분의 더 상위부모방향으로, 마우스로 타고 올라가다 보면, 제목의 위치에서, 하나의 item전체를 정의해주는 <div 요소 class가 있을 것이다. 그것을 기준으로 닫아보면, 각 제목들이 부여되 된 것을 확인할 수 있다.
   나의 경우, question-summary narrow라는 div요소가 각 질문item들을 정의해주었다.
image

7. 그러면, 처음에 제목을 가진 xpath로 돌아가서, /text()부분을 제거하고, 다시 살펴보자.
//*[@id="question-summary-49154165"]/div[2]/h3/a/
이제  뒷부분부터 단위를 줄여가면서 response.xpath()를 호출하여 data='부분에 해당 div요소(question-summary narrow클래스)가 걸릴 때까지 줄여가보자. 나같은 경우, 뒷부분에서부터 줄여서 갈 수 있는 div의 위치가 div[2]까지 밖에 없다.
response.xpath('//*[@id="question-summary-49154165"]/div[2] ‘)
image


8. 이제 내가 선택한 제목은 2번째글의 제목이므로, div[2]로 인덱싱 되어있다. 인덱싱부분을 지우고 .xpath()를 호출하여,
   전체 질문item 대한 셀렉터리스트를 가져오자. 그리고 가져온 selector리스트를  파이썬 리스트 titles에다가 저장하자.
( 나는ㅠㅠ xpath 자체가,, class 뿐만아아니라 id에 글번호가 표시되어있고, xpath 카피시,, id로 검색되게된다.)
(이 때, 사이트를 바꾸고, id대신 class로 검색하게 했더니—> 모든 질문item에 대한 글 제목까지 한꺼번에 나왔다.)
scrapy shell “https://stackoverflow.com/questions
image
response.xpath('//*[@class="question-summary"]/div[2]/h3/a')
titles = response.xpath('//*[@class="question-summary"]/div[2]/h3/a')


9. titles에 인덱싱을 해서 첫번째를 한번 뽑아보자.
title = titles[0]
title
image

10. 이제 원래는 titles리스트는 각 item전체의 리스트고, 진짜 title까지 내려가줘야한다
     나는 id대신 class로 검색하고, 자식요소들 다 가지고 있으니까, 바로 각 item들의 제목 리스트가 되었다.
    원래대로라면 명시하는 방법은 .xpath(‘./’)함수로 ( 맨처음 글제목을 긁어올 때 들어간)자식요소를 줄 수 있다. 나는 맨 마지막 /text()를 주면 된다.
    이 때,  쩜/로 기존 xpath에 이어줘야한다.
    title.xpath('./text()')
image
extract()로 data(텍스트)부분만 추출해보자.
title.xpath('./text()').extract()
image


11. 이제 반복문을 통해 첫번째만이 아니라. titles 전체에서 제목만 뽑아내자

for title in titles :
    ...:     print title.xpath('./text()'
    ...:     ).extract()
image


글 1개의 제목 요소 찾아서 /text() 로 확인 –>xpath뒷부분 제거하여 item 1개의 div요소 –> 인덱싱을 제거하여 전체 item selector 추출 –>  리스트  titles에 담기-> for문을 통해 각 글 1개의 제목까지 자식요소 복구 후  .xpath( './ 자식요소 / text() ' ).extract()로 다뽑기 or []인덱싱하기

+ Recent posts