Scrapy:データを収集してデータベースに保存します

はじめに



サイトから情報を収集するためのこのフレームワークに興味がありました。 スクレイピーに関する出版物はありましたが、ロシア語には詳細な情報が十分にないため、私の経験についてお話ししたいと思います。



挑戦する



  1. 申請者のリストoreluniver.ru/abits?src=all_postupilのあるページに移動します。 次に、各リンクをたどり、到着した応募者と獲得したポイントに関するデータを収集します。
  2. ページ自体から、募集された専門分野のデータを収集します。
  3. すべての結果をデータベースに保存する


解決策



この問題を解決するために、Python 2.7、Scrapy 1.1 Sqlalchemy 1、Sqliteを使用しました。 ドキュメントの説明に従ってすべてをインストールしました。 この記事では、スパイダー自体の作成に関する同じ場所でのロシア語でのインストールについても説明しています。 それは私が得たものです。



プロジェクト構造:



\クモ

\クモ\ __ init__.py

\クモ\ abiturlist.py

\クモ\ SpecSpider.py

__init__.py

items.py

pipelines.py

settings.py


Items.pyファイル
from scrapy.item import Item, Field class SpecItem(Item): spec = Field() SpecName = Field() class GtudataItem(Item): family = Field() name = Field() surname = Field() spec = Field() ball = Field() url = Field() pagespec = Field()
      
      





スパイダークラスは、申請者のリストについてここで説明されています。



abiturlist.pyファイル
 from scrapy.spiders import CrawlSpider, Rule from scrapy.linkextractors import LinkExtractor from scrapy.loader.processors import TakeFirst, Identity from scrapy.loader import ItemLoader from scrapy.selector import HtmlXPathSelector, Selector from gtudata.items import GtudataItem class AbiturLoader(ItemLoader): default_output_processor = Identity() class AbiturlistSpider(CrawlSpider): name = "abiturlist" allowed_domains = ["oreluniver.ru"] start_urls = ["http://oreluniver.ru/abits?src=all_postupil"] rules = ( Rule(LinkExtractor(allow=('spec_id=04.03.01')), callback='parse_item'), ) def parse_item(self, response): hxs = Selector(response) all = hxs.xpath("//tr[position()>1]") pg_spec = hxs.xpath("//div[@class='page-content']/b/div/text()").extract()[0].strip() for fld in all: Item = GtudataItem() FIO = fld.xpath("./td[2]/p/text()").extract()[0].split() Item['family'] = FIO[0] Item['name'] = FIO[1] Item['surname'] = FIO[2] Item['spec'] = fld.xpath("./td[last()]/p/text()").extract()[0] ball = fld.xpath("string(./td[3]/p)").extract()[0] Item['ball'] = ball Item['url'] = response.url Item['pagespec'] = pg_spec yield Item
      
      





ここでは、専門のリストを収集するためのスパイダークラスについて説明します。 Xpathはフィールドを定義するために使用されます。 専門番号とその名前を区別するために、スライスを使用します。専門番号は9文字です。



SpecSpider.pyファイル
 from scrapy.spiders import CrawlSpider, Rule from scrapy.linkextractors import LinkExtractor from scrapy.selector import HtmlXPathSelector, Selector from gtudata.items import SpecItem class SpecSpider(CrawlSpider): name = "speclist" allowed_domains = ["oreluniver.ru"] start_urls = ["http://oreluniver.ru/abits?src=all_postupil"] # /abits?src=all_postupil rules = ( Rule(LinkExtractor(allow = ('src=all_postupil')), callback='parse_item'), ) def parse_item(self, response): hxs = Selector(response) all = hxs.xpath('//a[contains(@href, "spec_id")]/text()').extract() # print 'test' for fld in all: txt = fld.strip() Item = SpecItem() Item['SpecName'] = txt[9:] Item['spec'] = txt[:8] yield Item
      
      





Items.pyファイルで収集されたデータのいくつかのクラスを作成する可能性に注意したいと思います。 私の場合:





結果をデータベースに保存する



pipelines.pyファイルには、データを保存する方法が記述されています。 特定のテーブル構造を持つsqliteデータベースを作成するために、sqlalchemyを使用しました。



まず、declarative_base()クラスのインスタンスを作成し、そこからクラスを継承して、見つかった情報を格納するデータベーステーブルを記述します。 これらは、専門分野のリストを保存するためのSpecTableクラスと、応募者のデータを保存するためのDataTableです。



各クラスで、__ tablename__属性を設定します。 これは、データベース内のテーブルの名前です。 次に、フィールドを設定します。



 id = Column(Integer, primary_key=True)
      
      





idは整数の主キーです。



その他のフィールド、たとえば専門番号:



 spec = Column(String)
      
      





__init __()メソッドで、テーブルのフィールドに入力します。



GtudataPipelineクラスは、データベースを操作するプロセスを記述します。 初期化中に、プロジェクトフォルダー内のデータベースファイルの可用性を確認します。 ファイルが欠落している場合は、指定された構造を持つデータベースを作成します。



 Base.metadata.create_all(self.engine)
      
      





process_itemメソッドでは、データベースへの実際の保存について説明します。 クラスのどのインスタンスがアイテムであるかを確認します。 これに応じて、2つのテーブルのいずれかを記入します。 これを行うには、DataTableクラスとSpecTablクラスのインスタンスを作成します。



 dt = DataTable(item['family'],item['name'], item['surname'], item['spec'], item['ball'], item['url'], item['pagespec']) dt = SpecTable(item['spec'],item['SpecName'])
      
      





格納されたデータの一意性を確保するために(テーブル内の申請者が繰り返される場合があります)、fio属性を使用します。 これは、次の行で要素が形成されるセットです。



 fio = item['family'] + item['name'] + item['surname']
      
      





そのような申請者がデータベースにある場合、レコードは保存されません。



 if fio not in self.fio: dt = DataTable(item['family'],item['name'], item['surname'], item['spec'], item['ball'], item['url'], item['pagespec']) self.fio.add(fio) self.session.add(dt)
      
      





新しいエントリを追加します。



 self.session.add(dt)
      
      





スパイダーを開いたら、セッションを作成します。



  def open_spider(self, spider): self.session = Session(bind=self.engine)
      
      





クモを閉じると、変更が完了します。



  def close_spider(self, spider): self.session.commit() self.session.close()
      
      





結果は次のとおりです。



Pipelines.pyファイル
 from sqlalchemy.ext.declarative import declarative_base from sqlalchemy import create_engine, Table, Column, Integer, String, MetaData, ForeignKey from sqlalchemy.orm import Session import os from gtudata.items import SpecItem, GtudataItem from scrapy.exceptions import DropItem Base = declarative_base() class SpecTable(Base): __tablename__ = 'specdata' id = Column(Integer, primary_key=True) spec = Column(String) spectitle = Column(String) def __init__(self, spec, spectitle): self.spec= spec self.spectitle = spectitle def __repr__(self): return "<Data %s, %s>" % (self.spec, self.spectitle) class DataTable(Base): __tablename__ = 'gtudata' id = Column(Integer, primary_key=True) family = Column(String) name = Column(String) surname = Column(String) spec = Column(String) ball = Column(Integer) url = Column(String) pagespec = Column(String) def __init__(self, family, name, surname, spec, ball, url, pagespec): self.family = family self.name = name self.surname = surname self.spec = spec self.ball = ball self.url = url self.pagespec = pagespec def __repr__(self): return "<Data %s, %s, %s, %s, %s, %s, %s>" % \ (self.family, self.name, self.surname, self.spec, self.ball, self.url, self.pagespec) class GtudataPipeline(object): def __init__(self): basename = 'data_scraped' self.engine = create_engine("sqlite:///%s" % basename, echo=False) if not os.path.exists(basename): Base.metadata.create_all(self.engine) self.fio = set() def process_item(self, item, spider): if isinstance(item, GtudataItem): fio = item['family'] + item['name'] + item['surname'] if fio not in self.fio: dt = DataTable(item['family'],item['name'], item['surname'], item['spec'], item['ball'], item['url'], item['pagespec']) self.fio.add(fio) self.session.add(dt) elif isinstance(item, SpecItem): dt = SpecTable(item['spec'],item['SpecName']) self.session.add(dt) return item def close_spider(self, spider): self.session.commit() self.session.close() def open_spider(self, spider): self.session = Session(bind=self.engine)
      
      





プロジェクトのあるフォルダーで実行します。



 scrapy crawl speclist scrapy crawl abiturlist
      
      





そして結果が得られます。 プロジェクトのフルバージョンはGitHubに投稿されています



ソースのリスト



  1. スクレイピードキュメンテーション
  2. Scrapyでデータを収集する
  3. Sqlalchemy



All Articles