저번 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파일에 있는걸 가져오는것 같은데....안되네요

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