Picture Factory-仕組み パート1

私のプロジェクトの技術的な部分について、おそらく批評のために、または誰かが自分のために何かを描くために、少しお話ししたいと思います。



チャレンジ。 工場でできることは何ですか?



画像の追加:



クライアント部分で検索:



タスクがあります。 何を使用しますか?





くも



「スパイダー」は、異なる「小さな」(安価な)vpsサーバー上にあります。 ほとんど無料のトラフィックを受信するためにこれを行うことが決定されました。さらに、「ソース」が私たちを禁止する場合もあります。



スパイダー階層:



p0is0n @ localhost:〜/ developer / arx-images $ ls -la ./spiders/

base.py

indexers.py

ソース



-Base.pyには、コンテンツを操作するための基本クラスが含まれています。 すべてのクラスはtwisted.application.service.Serviceを継承します

-indexers.pyは、すべてのスパイダーの直接起動です。

-ソース自体には「ソース」のパーサーが含まれ、ファイル名はサイトの名前とほぼ一致しています(便宜上)。



p0is0n @ localhost:〜/ developer / arx-images $ ls -la ./spiders/sources/

bikewalls.py

carwalls.py

...

...



すべてのパーサーは、 base.pyで宣言されたBaseIndexerクラスを継承します



サンプルcarwalls.pyソースコード(現状のままのコード)
import sys import random import re import os from itertools import cycle, chain from types import ListType, TupleType from pprint import pprint from cStringIO import StringIO from hashlib import md5 from twisted.python.log import msg, err from twisted.internet.defer import Deferred, DeferredQueue, inlineCallbacks, returnValue from core.queues import httpQueue from core.utils import sleep, get_best_resolution from core.constants import DEBUG from core.models import ImageModel, contextModel from core.html import normalize, normalize_title from base import sources, BaseIndexer class CarwallsIndexer(BaseIndexer): """CarwallsIndexer class""" name = 'Carwalls.com' charset = 'utf8' index = 'http://www.desktopmachine.com/' source = sources.add(4) pages = cycle(chain(*( ('?p={0}'.format(page) for page in xrange(0, 500, 18)), ))) reFindImagesList = re.compile(u'<a href=([\S]+framepic\.php\?id=\d+&size=[\S]+)[^>]+>2560\s*x\s*1600<\/a>', re.S).findall reFindTitle = re.compile(u"<title>(.+?)2560\s*x\s*1600 wallpaper<\/title>", re.S).search reFindPhoto = re.compile(u'<td colspan=2>\s*<img src=([\S]+\/pics\/[\S]+2560x1600\.(?:jpg))>\s*<\/td>', re.S).search @inlineCallbacks def _findImages(self): self._stats.page = self.pages.next() # Request result = yield httpQueue.request(url=self.getAbsoluteUrl(self._stats.page)) result = result.decode(self.charset, 'ignore') if not result: raise ValueError('Wow! Empty result') # Count images count = 0 for url in self.reFindImagesList(result): # Sleep (yield self.sleepWithFireOnServiceStop(self.sleepValue, self.sleepSplit)) # Try find images msg('Spider', self.name, 'findImages, try', url) if self.loop == -1: returnValue(None) try: result = yield httpQueue.request(url=self.getAbsoluteUrl(url)) result = result.decode(self.charset, 'ignore') except Exception, e: msg('Spider', self.name, 'findImages request error', url) err(e) # Stats self._stats.errors.http += 1 # Skip continue title = self.reFindTitle(result) image = self.reFindPhoto(result) title = title and title.group(1) or None image = image and image.group(1) or None if not title or not image: msg('Spider', self.name, 'findImages wrong title or image', repr((title, image))) # Skip continue # Make item try: item = (yield self._makeItem(title=title, url=url.split('&size').pop(0))) except Exception, e: msg('Spider', self.name, 'findImages make item error') err(e) # Skip continue url = image if not item['url']: msg('Spider', self.name, 'findImages wrong url', repr(item['url'])) # Skip continue if not item['categories']: # Set default categories item['categories'].extend((103, 112)) # Translate to list item['categories_names'] = list( item['categories_names']) # Sleep (yield self.sleepWithFireOnServiceStop(self.sleepValue, self.sleepSplit)) if self.loop == -1: returnValue(None) msg('Spider', self.name, 'findImages, try', url) # Create file result = self._makeFile() try: (yield httpQueue.request(url=self.getAbsoluteUrl(url), file=result)) except Exception, e: msg('Spider', self.name, 'findImages request error', url) err(e) if hasattr(result, 'delete') and not result.delete: # Delete file if is temporary os.unlink(result.name) # Stats self._stats.errors.http += 1 # Skip continue finally: result.close() try: item.update(image=result) # if DEBUG: # pprint(item) self.imageQueuePut(item) except Exception, e: msg('Spider', self.name, 'findImages create error') err(e) # Skip continue returnValue(count)
      
      







起動はindexers.pyで行われ、すべてが非常に簡単です:



 from twisted.application.service import Application, MultiService from core import constants from sources.carwalls import CarwallsIndexer from sources.bikewalls import BikewallsIndexer application = Application("ARX-Images Indexers") services = MultiService() services.setServiceParent(application) services.addService(CarwallsIndexer()) services.addService(BikewallsIndexer())
      
      





「ソース」がすべてのカテゴリとキーワードとともに画像を抽出した後、 BaseIndexer.imageQueuePutメソッドが呼び出されます 。これは、さらなる処理のために「ローカル」キューに情報を追加します。



処理は、すべてのフィールド(カテゴリ、キーワード、タイトル)のチェックで構成されます。 次に、データベース(またはメインキュー)内のイメージの存在を確認する要求が送信され、結果が否定の場合、イメージはメインサーバー上のキューに送信されます。



メインサーバーで、受信した情報が処理されます。キーワードを解析し、画像に関する情報(色、サイズ)を抽出し、サムネイルを作成して、画像をメインデータベースに追加します。



画像の存在を確認する



この部分は簡単ではありませんでした。 画像の比較のために、pHashアルゴリズムが基礎として採用されました( Habrの記事 )。



ハッシュ関数
 def getImageHash(image): cdef unsigned int lefts, row, i cdef unsigned long long bits cdef list results = [] if not isinstance(image, Image.Image): image = Image.open(image) image = image.resize((128, 128)) image = image.filter(ImageFilter.Kernel( (5, 5), ( 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1 ), 8, 0 )) image = ImageOps.grayscale(image) image = image.resize((16, 16)).convert('P', dither=Image.NONE) lefts = (sum(image.getdata()) / HASH_BITS) datas = image.getdata() for i in xrange(0, 256, 32): bits = int(''.join('1' if row > lefts else '0' for row in islice(datas, i, i + 32)), 2) # Add to results results.append(bits) return tuple(results)
      
      





ハッシュを保存するために、いくつかの機能を持つサーバーが作成されました。

-ハッシュを追加

-除去

-ハッシュで「類似」を検索



検索は、単純なブルートフォースアルゴリズム 、つまり通常の検索に基づいています。



すべての「重い」サーバー機能はCythonで書かれています。



PS誰かが興味を持っているなら、私は続編を書きます。 プロジェクト自体: picsfab.com



All Articles