2013-01-14 2 views
0

약 200,000 개의 엔티티 목록이 있으며 각 엔티티에 대해 특정 RESTful API를 쿼리하고 JSON 형식으로 저장된 모든 200,000 개의 엔티티로 끝내야합니다. txt 파일. 순진한 방법으로 200,000 개의 엔티티 목록을 살펴보고 하나씩 쿼리하고, 반환 된 JSON을 목록에 추가하고, 완료되면 텍스트 파일을 모두 보냅니다. 같은 뭔가 : API에 20 만 쿼리가 약 10 시간 정도 걸릴 것 같은많은 수의 쿼리를 텍스트 파일에 작성하기

from apiWrapper import api 
from entities import listEntities #list of the 200,000 entities 
a=api() 
fullEntityList=[] 
for entity in listEntities: 
fullEntityList.append(a.getFullEntity(entity)) 

with open("fullEntities.txt","w") as f: 
    simplejson.dump(fullEntityList,f) 

는 분명히 이것은, 신뢰할 수 없습니다, 그래서 그것을 파일에 기록하기지기 전에 뭔가 오류가 발생합니다 같아요. 올바른 방법은 청크로 작성하는 것이지만 구현 방법은 확실하지 않습니다. 어떤 아이디어? 또한 데이터베이스로는이 작업을 수행 할 수 없습니다. 그들이에서 와서

답변

2

SQLite 데이터베이스에 작성하는 것이 좋습니다. 이것은 내 자신의 작은 웹 스파이더 응용 프로그램을위한 방법입니다. 키를 쉽게 쿼리 할 수 ​​있고 이미 검색 한 키를 확인할 수 있기 때문입니다. 이렇게하면 응용 프로그램을 쉽게 끝낼 수 있습니다. 특히 다음 주에 1000 개의 새로운 항목을 추가하면됩니다.

응용 프로그램을 처음부터 "복구"하도록 설계하십시오. 예상치 못한 예외 (예 : 네트워크 정체로 인한 시간 초과)가 발생하면 처음부터 다시 시작하지 않아도되지만 아직 성공적으로 검색하지 못한 쿼리 만 다시 시작해야합니다. 200.000 개의 쿼리에서 99.9 %의 가동 시간은 200 개의 실패를 예상해야한다는 것을 의미합니다!

공간 효율성과 성능면에서 zlib로 json을 압축하여 데이터베이스 blob에 덤프하기 전에 압축 형식을 사용하는 것이 좋습니다.

스파이더가 동시에 여러 호스트에서 실행되지 않는 한 SQLite가 좋은 선택입니다. 단일 애플리케이션의 경우 sqlite가 완벽합니다.

1

쉬운 방법은 'a' (추가) 모드에서 파일을 열고 그들에게 하나 하나를 작성하는 것입니다.

더 나은 방법은 작업 대기열을 사용하는 것입니다. 이렇게하면 a.getFullEntity 호출을 작업자 스레드로 생성하고 결과가 돌아 왔을 때 결과를 처리하거나 실패한 경우 다시 시도를 예약 할 수 있습니다. Queue을 참조하십시오.

+0

작업 대기열에서 약간 확장 할 수 있습니까? 어떤 모듈을 사용해야합니까? 문서에 대한 링크? – leonsas

+1

링크가 이미 위에 있습니다 ... 그리고 모듈은'대기열' – wim

0

필자는 파일 쓰기를 수행하는 별도의 스레드를 사용하고 Queue을 사용하여 모든 엔티티의 기록을 유지합니다. 처음 시작했을 때, 나는 이것이 5 분 안에 끝날 것이라고 생각했지만, 조금 더 힘들어졌다. simplejson과 제가 알고있는 다른 모든 라이브러리는 부분 쓰기을 지원하지 않으므로 목록의 한 요소를 나중에 쓸 수없고 나중에 다른 것을 추가 할 수 없습니다. 따라서 수동으로이 문제를 해결하기 위해 [, , 및 파일에 별도로 ]을 입력 한 다음 각 엔티티를 개별적으로 덤프하십시오. (나는 당신의 API를 가지고 있지 않는 한) 그것을 확인 할 수있는없이

, 당신은 시도 할 수 :

import threading 
import Queue 
import simplejson 
from apiWrapper import api 
from entities import listEntities #list of the 200,000 entities 

CHUNK_SIZE = 1000 

class EntityWriter(threading.Thread): 
    lines_written = False 
    _filename = "fullEntities.txt" 

    def __init__(self, queue): 
     super(EntityWriter, self).__init() 
     self._q = queue 
     self.running = False 

    def run(self): 
     self.running = True 
     with open(self._filename,"a") as f: 
      while True: 
       try: 
        entity = self._q.get(block=False) 
        if not EntityWriter.lines_written: 
         EntityWriter.lines_written = True 
         f.write("[") 
         simplejson.dump(entity,f) 
        else: 
         f.write(",\n") 
         simplejson.dump(entity,f) 
       except Queue.Empty: 
        break 
     self.running = False 

    def finish_file(self): 
     with open(self._filename,"a") as f: 
      f.write("]") 


a=api() 
fullEntityQueue=Queue.Queue(2*CHUNK_SIZE) 
n_entities = len(listEntities) 
writer = None 
for i, entity in listEntities: 
    fullEntityQueue.append(a.getFullEntity(entity)) 
    if (i+1) % CHUNK_SIZE == 0 or i == n_entities-1: 
     if writer is None or not writer.running: 
      writer = EntityWriter(fullEntityQueue) 
      writer.start() 
writer.join() 
writer.finish_file() 

을이 스크립트 목록을 통해 여전히

주요 루프 반복을 무엇 엔티티에 대한 전체 정보를 얻습니다. 그 후 각 엔티티가 이제 대기열에 저장됩니다. EntityWriter-Thread는 1000 개의 엔티티마다 (그리고 목록의 끝에서) 주 스레드와 병렬로 실행됩니다.이 EntityWriter getQueue에서 찾아 원하는 출력 파일로 덤프합니다.

JSON을 목록으로 만들려면 몇 가지 추가 로직이 필요합니다. 위에 언급 한대로 , ,]을 수동으로 작성합니다. 결과 파일은 다시로드 할 때 원칙적으로 simplejson에 의해 이해되어야합니다.

관련 문제