4

ここからオッズをかき集めようとしています。

現在、次のスパイダーで結果をログに記録しようとしています:

def parse(self, response):         
   log.start("LogFile.txt", log.DEBUG);

   hxs = HtmlXPathSelector(response)
   sites = hxs.select('//div[@class="fb_day_type_wrapper"]')

   items = []
   for site in sites:
       siteAddress = urlparse.urljoin(response.url, site.extract())
       self.log('Found category url: %s' % siteAddress)

これはエントリのみをログに記録します: この市場は現在利用できません.... オッズを含む他の要素ではありません。

私はいくつかの異なるセレクターを試しましたが、うまくいきませんでした。要素の内部に入ろうとすると、div[@class="fb_day_type_wrapper"]何も返されないように見えます。スクレイピーシェルを使用しても同じ結果が得られます。

4

1 に答える 1

6

このサイトでは、javascript を使用してデータ テーブルを生成しています。js でレンダリングされた html ページを取得できる、scrapyjsスプラッシュなどの代替手段がいくつかあります。1 ページだけスクレイピングする必要がある場合は、Selenium を使用する方がよい場合があります。

それ以外の場合は、ハードコア モードに移行し、データを使用してサイトで何が起こっているかをリバース エンジニアリングする必要がある場合があります。その方法をお教えします。

まず、 を起動してscrapy shell、Web ページを探索できるようにします。

scrapy shell http://www.paddypower.com/football/football-matches/premier-league

注:私はpython 2.7.4、ipython 0.13.2、およびscrapy 0.18.0を使用しています。

ブラウザで「Crystal Palace v Fulham」のソースを検索すると、その参照を持つ JavaScript コードがあることがわかります。<script>ブロックは次のようになります。

document.bodyOnLoad.push(function() {
    lb_fb_cpn_init(
        "",
        "html",
        "MR_224",
        {category: 'SOCCER',

この要素をシェルで検索します。

In [1]: hxs.select('//script[contains(., "lb_fb_cpn_init")]')
Out[1]: [<HtmlXPathSelector xpath='//script[contains(., "lb_fb_cpn_init")]' data=u'<script type="text/javascript">\n/* $Id: '>]

引数をlb_fb_cpn_init調べると、探しているデータが次の形式で引数として渡されていることがわかります。

[{names: {en: 'Newcastle v Liverpool'}, ...

実際には、そのような 3 つの引数があります。

In [2]: hxs.select('//script[contains(., "lb_fb_cpn_init")]').re('\[{names:')
Out[2]: [u'[{names:', u'[{names:', u'[{names:']

したがって、それらすべてを抽出します。多くの正規表現を使用していることに注意してください。

In [3]: js_args = hxs.select('//script[contains(., "lb_fb_cpn_init")]').re(r'(\[{names:(?:.+?)\]),')

In [4]: len(js_args)
Out[4]: 3

ここでの考え方は、javascript コード (リテラル オブジェクト) を python コード (dict) に解析したいということです。ただし、そのためにはjson.loads、js コードは有効な json オブジェクトである必要があります。つまり、フィールド名と文字列が で囲まれている必要があります""

そうしていきます。最初に、JavaScript リストとして引数を 1 つの文字列に結合します。

In [5]: args_raw = '[{}]'.format(', '.join(js_args))

次に、フィールド名を囲み、一重引用符を二重引用符"" 置き換えます。

In [6]: import re

In [7]: args_json = re.sub(r'(,\s?|{)(\w+):', r'\1"\2":', args_raw).replace("'", '"')

re.subJavaScript コードには単一のand/orで置き換えるのが簡単ではないパターンが含まれている可能性があるため、これはすべてのケースで常に機能するとは限りません.replace

JavaScript コードを json オブジェクトとして解析する準備が整いました。

In [8]: import json

In [9]: data = json.loads(args_json)

In [10]: len(data)
Out[10]: 3

ここでは、イベント名とオッズだけを探しています。dataコンテンツを見て、それがどのように見えるかを確認できます。

幸いなことに、データには相関関係があるようです。

In [11]: map(len, data)
Out[11]: [20, 20, 60]

dictフィールドを使用して、3 つから1 つを作成することもできev_idます。直接的な相関関係があり、イベントごとに 3 つのアイテムが含まれているdata[0]と仮定します。これは、次の方法で簡単に確認できます。data[1]data[2]

In [12]: map(lambda v: v['ev_id'], data[2])
Out [12]:
[5889932,
 5889932,
 5889932,
 5889933,
 5889933,
 5889933,
 ...

いくつかの python-fu を使用して、レコードをマージできます。

In [13]: odds = iter(data[2])

In [14]: odds_merged = zip(odds, odds, odds)

In [15]: data_merged = zip(data[0], data[1], odds_merged)

In [16]: len(data_merged)
Out[16]: 20

最後に、データを収集します。

In [17]: get_odd = lambda obj: (obj['names']['en'], '/'.join([obj['lp_num'], obj['lp_den']]))

In [18]: event_odds = []

In [19]: for event, _, odds in data_merged:
   ....:     event_odds.append({'name': event['names']['en'], 'odds': dict(map(get_odd, odds)), 'url': event['url']})
   ....:     

In [20]: event_odds
Out[20]: 
[{'name': u'Newcastle v Liverpool',
  'odds': {u'Draw': u'14/5', u'Liverpool': u'17/20', u'Newcastle': u'3/1'},
  'url': u'http://www.paddypower.com/football/football-matches/premier-league-matches/Newcastle%2dv%2dLiverpool-5889932.html'},
 {'name': u'Arsenal v Norwich',
  'odds': {u'Arsenal': u'3/10', u'Draw': u'9/2', u'Norwich': u'9/1'},
  'url': u'http://www.paddypower.com/football/football-matches/premier-league-matches/Arsenal%2dv%2dNorwich-5889933.html'},
 {'name': u'Chelsea v Cardiff',
  'odds': {u'Cardiff': u'10/1', u'Chelsea': u'1/4', u'Draw': u'5/1'},
  'url': u'http://www.paddypower.com/football/football-matches/premier-league-matches/Chelsea%2dv%2dCardiff-5889934.html'},
 {'name': u'Everton v Hull',
  'odds': {u'Draw': u'10/3', u'Everton': u'4/9', u'Hull': u'13/2'},
  'url': u'http://www.paddypower.com/football/football-matches/premier-league-matches/Everton%2dv%2dHull-5889935.html'},
 {'name': u'Man Utd v Southampton',
  'odds': {u'Draw': u'3/1', u'Man Utd': u'8/15', u'Southampton': u'11/2'},
  'url': u'http://www.paddypower.com/football/football-matches/premier-league-matches/Man%2dUtd%2dv%2dSouthampton-5889939.html'},
 ...

ご覧のとおり、Web スクレイピングは非常に難しい (そして楽しい!) 場合があります。すべては、Web サイトがデータを表示する方法に依存します。ここでは、Selenium を使用するだけで時間を節約できますが、大規模な Web サイトをスクレイピングしようとしている場合、Selenium は Scrapy に比べて非常に遅くなります。

また、サイトのコードが頻繁に更新されるかどうかも考慮する必要があります。その場合、js コードのリバース エンジニアリングにより多くの時間を費やすことになります。その場合、scrapyjsスプラッシュなどのソリューションがより適切なオプションになる可能性があります。

最後のコメント:

  • これで、データの抽出に必要なすべてのコードが揃いました。これをスパイダー コールバックに統合し、アイテムをビルドする必要があります。
  • 使用しないでくださいlog.start。設定LOG_FILE(コマンドライン引数: --set LOG_FILE=mylog.txt) を使用します。
  • .extract()常にリストを返すことを思い出してください。
于 2013-10-18T05:52:07.460 に答える