저번 1편에서 설명한 환경설정이 제대로 되었다면 이제 코딩만 하면 된다. 나 또한 회사 지인의 부탁으로 크롤러를 만들어 보았다. 

프로젝트를 생성하게 되면 몇개의 폴더와 파일들이 생기는데 적절히 설정해주고 로직이 필요한 부분에 코딩을 해주면 된다.


우선 프로젝트를 생성하기 위해서는 아래의 커맨드를 입력한다.(저번에 설명했었나?)

scrapy startproject townpage





프로젝트명을 townpage로 지정하였기 때문에 townpage라는 폴더가 생성되고 그안에 아래와 같은 파일들이 생성되어 있을것이다.

townpage

 |

 ----  scrapy.cfg   ---> 프로젝트정보를 가지고 있음

 ---- /townpage   프로젝트 폴더

            |

            ----- items.py      크롤링할 데이타 모델

            ----- pipelines.py  크롤링 결과를 파일로 저장할때 필요한 룰(규칙) 정의

            ----- setting.py item과piplines과의 설정 및 각종? 설정에 필요

            ----- /spiders   크롤링할 거미들을 모아놓을 폴더

                       |

                       ----- townpage.py (크롤링을 위한 거미. 처음에 생성되지 않으므로 만들어야함.)


이것저것 생성이 되었지만 실제로 코딩이 필요한것은 /spiders 폴더 밑에 거미들이고 나머지는 설정정도의 레벨에서 충분하다.




그럼 하나씩 소스를 보자.

#items.py


# -*- coding: utf-8 -*-


# Define here the models for your scraped items

#

# See documentation in:

# http://doc.scrapy.org/en/latest/topics/items.html


import scrapy

from scrapy.item import Item, Field


class TownpageItem(scrapy.Item):

shopname = Field()

comment = Field()

address1 = Field()

address2 = Field()

tel = Field()

map_url = Field()

request_num = Field()

column_num = Field()


TownpageItem이라는 클래스를 만들고 정보를 수집하고 싶은 항목들을 아래와 같이 정의해준다. 주의할 것은 소스를 utf-8로 지정해줄것과 import를 잊지않고 추가해주는것 정도.





#Pipelines.py


# -*- coding: utf-8 -*-


# Define your item pipelines here

#

# Don't forget to add your pipeline to the ITEM_PIPELINES setting

# See: http://doc.scrapy.org/en/latest/topics/item-pipeline.html


from scrapy.exceptions import DropItem

import codecs

import json


class JsonWriterPipeline(object):


    def __init__(self):     

self.file = codecs.open("items.json", "wb", encoding="utf-8")


    def process_item(self, item, spider):

line = json.dumps(dict(item), ensure_ascii=False) + "\n"

self.file.write(line)

return item


출력될 데이타를 어떻게 출력할 것인지를 정의 하는 곳이다. 내 경우는 문자가 깨지는 현상이 있어서 utf-8로 출력되도록 소스를 좀 손을 보았다. 각각의 환경에 맞추어 정의 해주어야 하므로 스택플로우에 도움을 받도록 하자.






#settings.py


# -*- coding: utf-8 -*-


# Scrapy settings for townpage project

#

# For simplicity, this file contains only the most important settings by

# default. All the other settings are documented here:

#

#     http://doc.scrapy.org/en/latest/topics/settings.html

#


BOT_NAME = 'townpage'


SPIDER_MODULES = ['townpage.spiders']

NEWSPIDER_MODULE = 'townpage.spiders'

DEFAULT_ITEM_CLASS = 'townpage.items.TownpageItem'


ITEM_PIPELINES = {'townpage.pipelines.JsonWriterPipeline': 1}

# Crawl responsibly by identifying yourself (and your website) on the user-agent

#USER_AGENT = 'townpage (+http://www.yourdomain.com)'


여기서는 프로젝트에 사용할 아이템(모델)을 정의해주고 파이프라인을 지정해 준다. 중요한건 TownpageItem을 지정해 주는거다.




그리고 여기서 제일 중요한 부분. 크롤링을 해주는 거미이다. 


#townpage.py


# -*- coding: utf-8 -*-


from scrapy.spider import Spider

from scrapy.selector import Selector

from scrapy.http import Request


from ..items import TownpageItem


print "============================="

print "start townpage crawling..."

print "============================="


max_url_count = 10

current_url_count = 1


#크롤링할 URL

domain = u"http://itp.ne.jp"

url_path = u"/tokyo/genre_dir/gourmet/pg/"

param = u"/?sr=1&nad=1&num=50"

url = domain + url_path + u"1" + param

# url = domain + u"/tokyo/genre_dir/gourmet/?sr=1&nad=1&evdc=1&num=50"


class TownpageSpider(Spider):

    name = "townpage"

    allowed_domains = ["itp.ne.jp"]

    start_urls = [url]


    print "start :" + str(start_urls)

    

    def parse(self, response):


        global current_url_count, max_url_count


        #shoplist를 취득

        print "parsing shop pagelink..." + str(current_url_count)


        hxs = Selector(response)


        shops =[]

        #shop정보취득

        shops = hxs.xpath('//div[@class="normalResultsBox"]/article[1]/section[1]')

        print "shops count: " + str(len(shops))

        

        items = []

        row_num = 1

        for shop in shops:

            item = TownpageItem()

            item['shopname'] = shop.xpath('h4[1]/a[1]/text()').extract()

            item['comment'] = shop.xpath('p[1]/text()').extract()

            item['address1'] = shop.xpath('p[2]/text()').extract()

            item['tel'] = shop.xpath('p[3]/b[1]/text()').extract()

            item['map_url'] = = shop.xpath('p[2]/a/@href').extract()

            item['page_num'] = str(current_url_count)

            item['row_num'] = str(row_num)

            row_num = row_num + 1


            yield item

            # items.append(item)


            # print "[shopname] " + str(item['shopname'])

            # print "[comment] "+ str(item['comment'])

        


        if(current_url_count <= max_url_count):

            current_url_count = current_url_count + 1

            next_page = [domain + url_path + str(current_url_count) + param]

            print "------->request url :" + next_page[0]

            yield Request(next_page[0],self.parse)

            

            # yield Request(next_page[0],self.parse)



        # return items



        


로직을 잠깐 설명하자면 시작페이지가 리스트(일람)페이지인데 가게정보를 얻어오고 다음페이지로 URL을 변경한다음 재귀 호출하여 처리하는 방식이다. 간단하니 소스를 보면 금방 이해될 것이다.






자! 이제 크롤링을 해보자.

#Json으로 출력하기

scrapy crawl townpage -o items.json


지인으로부터 CSV파일로 해줬으면 하는 부탁이 있어 검색을 해보니 아니나다를까 간단한 방법이 있었다.

#CSV출력하기

scrapy crawl townpage -o items.csv -t csv


제대로만 했다면 아래와 같은 실행결과 화면을 만날것이다.


파이썬 초보인데다가 웹기술도 그다지 지식이 없어서 삽질정도의 설명이 되고 말았지만 이글이 혹시라도 나와같은 삽질을 줄이는데 도움이 됬으면 한다. 끝~




Posted by 악당잰 트랙백 0 : 댓글 6

댓글을 달아 주세요

  1. addr | edit/del | reply 2015.01.19 12:35

    비밀댓글입니다

    • addr | edit/del BlogIcon 악당잰 2015.01.20 07:49 신고

      크롬이나 파폭으로 HTML소스 보시면서 웹페이지를 분석하면 되는데 정확히 어떤부분이 잘 안되시나요?

  2. addr | edit/del | reply 도와주세요 2015.01.20 22:07

    와 답변 남겨주셔서 정말 감사합니다...
    시작단계인데요... 400에러가 나서 시작도 못하고있어요...아무리 찾아봐도.. 원인을 모르겠어요..
    아래와 같이 코드 짜서.. 일단 돌려보려고하는데요.. 아래와 같이 400에러가나요 ㅠ
    혹시 원인 알려주실수 있을까요? 정말 감사합니다. 복받으실꺼에요..!

    2015-01-20 22:02:52+0900 [scrapy] DEBUG: Telnet console listening on 127.0.0.1:6023
    2015-01-20 22:02:52+0900 [scrapy] DEBUG: Web service listening on 127.0.0.1:6080
    2015-01-20 22:02:52+0900 [heny] DEBUG: Retrying <GET http://m.map.naver.com/siteview.nhn?code=11687099> (failed 1 times): 400 Bad Request
    2015-01-20 22:02:52+0900 [heny] DEBUG: Retrying <GET http://m.map.naver.com/siteview.nhn?code=11687099> (failed 1 times): 400 Bad Request
    2015-01-20 22:02:52+0900 [heny] DEBUG: Retrying <GET http://m.map.naver.com/siteview.nhn?code=11687099> (failed 2 times): 400 Bad Request
    2015-01-20 22:02:52+0900 [heny] DEBUG: Retrying <GET http://m.map.naver.com/siteview.nhn?code=11687099> (failed 2 times): 400 Bad Request
    2015-01-20 22:02:52+0900 [heny] DEBUG: Gave up retrying <GET http://m.map.naver.com/siteview.nhn?code=11687099> (failed 3 times): 400 Bad Request
    2015-01-20 22:02:52+0900 [heny] DEBUG: Crawled (400) <GET http://m.map.naver.com/siteview.nhn?code=11687099> (referer: None)
    2015-01-20 22:02:52+0900 [heny] DEBUG: Ignoring response <400 http://m.map.naver.com/siteview.nhn?code=11687099>: HTTP status code is not handled or not allowed
    2015-01-20 22:02:52+0900 [heny] DEBUG: Gave up retrying <GET http://m.map.naver.com/siteview.nhn?code=11687099> (failed 3 times): 400 Bad Request
    2015-01-20 22:02:52+0900 [heny] DEBUG: Crawled (400) <GET http://m.map.naver.com/siteview.nhn?code=11687099> (referer: None)
    2015-01-20 22:02:53+0900 [heny] DEBUG: Ignoring response <400 http://m.map.naver.com/siteview.nhn?code=11687099>: HTTP status code is not handled or not allowed
    2015-01-20 22:02:53+0900 [heny] INFO: Closing spider (finished)
    2015-01-20 22:02:53+0900 [heny] INFO: Dumping Scrapy stats:



    # -*- coding:euc-kr -*
    from scrapy.spider import BaseSpider
    from scrapy.selector import HtmlXPathSelector
    from scrapy.item import Item, Field

    class Website(Item):
    name = Field()
    description = Field()
    url = Field()

    class DmozSpider(BaseSpider):
    name = "heny"
    allowed_domains = ["map.naver.com"]
    start_urls = [
    "http://m.map.naver.com/siteview.nhn?code=11687099",
    "http://m.map.naver.com/siteview.nhn?code=11687099",
    ]

    def parse(self, response):
    hxs = HtmlXPathSelector(response)


    sites = hxs.select('//ul[@class="li1 li15" id="commentItems"]/li')
    items = []

    for site in sites:
    item = Website()
    item['name'] = site.select('a/text()').extract()
    item['url'] = site.select('a/@href').extract()
    item['description'] = site.select('text()').re('-\s([^\n]*?)\\n')
    items.append(item)

    return items

    • addr | edit/del BlogIcon 악당잰 2015.01.21 00:29 신고

      저도 파이썬이나 scrapy는 경험이 많이 없어서 도움이 될진 모르겟지만 제가 원인을 찾아본 바로는 서버에서 불필요한 반복 액서스를 방지하기 위해 쿠키값을 체크하여 에러가 나는경우가 있다네요. scrapy에 쿠키값을 false로 설정하는게 있다니 한번 시도해 보세요.

  3. addr | edit/del | reply 이진희 2015.10.19 11:51

    from string import join
    from article.items import ArticleItem

    class ArticleSpider(scrapy.Spider):
    name = "article"
    allowed_domains = ["http://joongang.joins.com"]

    start_urls = ["http://news.joins.com/politics",
    "http://news.joins.com/society",
    "http://news.joins.com/money",]

    def parse(self, response):
    sel = scrapy.Selector(response)
    urls = sel.xpath('//div[@class="bd"]/ul/li/strong[@class="headline mg"]')
    items = []

    for url in urls:
    item = ArticleItem()
    item['url'] = url.xpath('a/@href').extract()
    item['url'] = "http://news.joins.com"+join(item['url'])
    items.append(item['url'])

    for itm in items:
    yield scrapy.Request(itm,callback=self.parse2,meta={'item':item})

    def parse2(self, response):
    item = response.meta['item']
    sel = scrapy.Selector(response)

    articles = sel.xpath('//div[@id="article_body"]')
    items = []
    for article in articles:
    item['article'] = article.xpath('text()').extract()
    items.append(item['article'])
    return items


    안녕하세요! 스크래피 관련 질문드립니다.
    스파이더 코드 부분인데요 parse 함수에서 뉴스기사 url을 클로링하고 items리스트에 저장해서
    parse2에 request로 해당 url을 보내서 parse2함수에서는 기사를 크롤링하는 겁니다. 그런데 두 함수를 따로 구현해서 크롤링해보면 다 스크랩이 되는데 저렇게 한 스파이더에 두가지 함수를 넣어서하면 yield에서 request가 보내지지 않아서 그런지 크롤링이 되지 않더라구요...혹시 이코드의 문제점을 아시겠다면 답변 좀 부탁드립니다.

  4. addr | edit/del | reply 확인요청 2015.11.16 02:31

    Traceback (most recent call last):
    File "C:/Users/KeMi/PycharmProjects/townpage/townpage/spiders/townpage.py", line 7, in <module>
    from ..items import TownpageItem
    ValueError: Attempted relative import in non-package

    이런 에러가 발생하네요. items가 item.py파일에 있는걸 가져오는것 같은데....안되네요

    어떤식으로 수정이 들어가야하나요?

개인적으로 만들 앱에 필요한 데이타를 크롤링해야 해서 뭔가 간단하게 되는게 없을까 구글링 하던중 Scrapy라는 놈을 알게 되었다. 

생각보다 한글자료가 많이 없었기 때문에 혹시 scrapy를 사용하려는 개발자들이 내가 했던 삽질을 되풀이하게 하지 않기 위해 메모형식으로 남겨두려고 한다. 얼마나 도움될지는 모르지만...어쨋뜬!

먼저 첫번째로 환경설정에 대해 설명하고 두번째글에선 간단하게 사용하는 방법에 대하여 설명할까 한다. 

(본인도 파이썬은 리얼초보이므로 내용이 틀릴수 있으므로 저보다 더 잘 아시는 분께서는 지적 부탁드립니다.)


자..그럼..


개인적 사정상 윈도우환경과 맥환경을 동시에 설정을 해보았는데 이글에선 여기선 맥에 대해서만 설명하려고 한다.

윈도우설정하실분들은 구글폭풍검색으로 해결하시길..(본인의 경우 파이썬에 대한 기본지식이 없었던 탓에 윈도우환경설정은 꼬박 하루걸렸다능..

그에비해 맥환경에서는 한시간만에 샘플코드까지 실행성공할 수 있었다. 



기본적으로 아래의 링크(scrapy 공식홈페이지)를 따라 설치하면 된다. (물론 영어임.ㅠ ㅠ )

http://doc.scrapy.org/en/latest/intro/install.html


먼저 맨 상단에 scrapy를 설치하기위한 기본 설치항목들이 쭈욱 나열되있는데 꼭 설치를 해야한다. 이걸 하나라도 빼먹으면 scrapy가 동작하지 않을수 있다.


당연한 얘기지만 우선 파이썬 설치가 필요하다. 그러나 맥에서는 파이선은 2.7버전이 이미 들어가 있으므로 설치할 필요가 없다.

파이썬은 3.x버전까지 나와있는 상태이니 업데이트를 하고 싶은 본능이... 그러나 그건 쓸데없는 오지랍이다. scrapy의 지원버전이 2.7이므로 그냥 두시길..

혹시 모르니 터미널에서 파이썬 버전을 확인해 보자.


python —version


그다음 간단설치를 위한 setup-tool을 설치한다.

아래의 커맨드를 터미널에서 순서대로 실행하면 끝~


curl -O http://peak.telecommunity.com/dist/ez_setup.py

sudo python ez_setup.py

easy_install --help



또하나 설치에 필요한 pip를 설치해야 한다. 다른데에서도 많이 사용하는 모듈이므로 혹시 모르니 설치되어 있는지부터 확인해본다.

pip —version


설치가 안되어 있다면 설치 고고~

easy_install pip


이것도 필요한 라이브러리란다. 간단하게 pip로 설치.

pip install lxml


openssl도 필요하다니 설치한다.

easy_install PyOpenSSL

easy_install PyCrypto


이제 기본준비가 끝났으므로 주인공인 scrapy를 설치한다.

pip install Scrapy




설치 끝!



하지만 제대로 설치가 되어 있는지를 확인해 봐야 하므로 몇가지 scrapy 커맨드를 실행해 봐야 한다.

우선 hoge라는 프로젝트 생성해 보자.

scrapy startproject hoge


hoge라는 폴더가 생기고 몇가지 파일들이 에러없이 생성되면 설치가 제대로 된것이다.



크롤링이 제대로 되는지를 해봐야 하는데 만드는데 시간이 걸리므로 샘플소스를 다운받아서 실행해 보자.

https://github.com/scrapy/dirbot/archive/master.zip



dmoz라는 프로젝트가 만들어져 있는데 해당폴더로 이동하여 아래의 커맨드를 입력해 보자.

scrapy crawl dmoz -o items.json


뭔가 실행이 되고 items.json파일이 생기고 거기에 데이타가 들어가 있다면 crawling이 제대로 된다는 뜻이다.


그럼 이번글은 여기까지...


Posted by 악당잰 트랙백 0 : 댓글 3

댓글을 달아 주세요

  1. addr | edit/del | reply 김선영 2015.06.18 21:35

    안녕하세요.

    웹싸이트 크롤과 관련한 블로그글 잘 보고 갑니다. 제가 이번에 웹싸이트를 크롤을 처음해봅니다만, 위에서 말씀하신대로, scrapy, 파이썬, pip 을 전부 다운을 받아서 실행을 해야 하는 건지요? 파이썬은 다운을 받았습니다만, 다른 2개도 다운을 받아야 크롤이 가능한지요? 아참, 저도 일본에서 유학생활을 하고 있습니다만, 너무 반갑네요^^

    • addr | edit/del BlogIcon 악당잰 2015.06.20 12:54 신고

      네 반갑네요^^. 제 경험으로 말하자면 전부 다 필요했습니다. 실행되는듯하다가 알수없는 에러가 나고 그랬거든요. 아마도 설치환경에 따라 다른것 같습니다.

  2. addr | edit/del | reply 해해 2015.07.05 00:45

    감사합니다!!ㅠㅅㅠ 감동!!