2017-08-05 8 views
0

私はこれで本当に頑張っています。フォーラムスレッドのスクレイピング:どのようにCSSのマージン属性からフォローアップ関係を計算するのですか?

私の掻き出しジョブの対象サイトは古いスタイルのフォーラムで、各スレッドは<div>タグ内にあり、各投稿は<p>タグ内にあります。フォローアップポストには、左マージンが20pxインデントされてその関係が示されます。

<div> 
    <p style="margin:2px 0 17px 0px; width:705px"><a href="./6368972.html" class="post">original post</a>other stuff</p> 
    <p style="margin:2px 0 2px 20px; width:683px"><a href="./6368973.html" class="post">reply post</a>other stuff</p> 
    <p style="margin:2px 0 2px 40px; width:661px"><a href="./6368974.html" class="post">reply post</a>other stuff</p> 
    ... 
</div> 

私は効果的なアルゴリズムのためにあなたの助けを必要とし、フォローアップの関係、除くタイトル、日時、ニックネームなど、など、ここで多くのINFOMATIONを抽出することができています。基本的に私は、その投稿が以前の投稿に対する返信であることを知る必要があります。

私のアイテムには、フォローアップ関係のフィールドが含まれています。すなわち、 reply_to = scrapy.Field() ここにreply_to投稿のURLを格納する必要があります。

Iは、各ポストのための左マージンの値を抽出することができ:(スレッドであり、すなわち、どのように多くの総ポスト)私はDIVの長さを計算することができる margin = int(div.css('p::attr(style)').re('.* (\d+)px;.*'))

しかし、実際に、私はここから行くかもしれない方法が分からない...

はあなたのすべてをありがとう!あなたは、いくつかの正規表現にスタイル属性を一致させるためにre:test XPath式を使用することができます

1---------------------  # left margin = 0px; original post 
2 -------------------  # left margin = 20px; reply to post 1 
3 -----------------  # left margin = 40px; reply to post 2 
4 -------------------  # left margin = 20px; reply to post 1, not 3 
5 -----------------  # left margin = 40px; reply to post 4, not 2 
6  ---------------  # left margin = 60px; reply to post 5 
+0

正確に何をしたいのかはっきりしません。提供された 'HTML'ソースについての希望出力を共有できますか? – Andersson

答えて

1

これは未テストですが、うまくいくかもしれない:

parent = list() 
for p in div.xpath('./p'): 
    post = dict() 
    # do whatever extraction from post here -- title, datetime etc. 
    # post['title'] = p.xpath(...) 
    # ... 
    post['url'] = p.xpath('./a/@href').extract_first() 

    post['reply_to'] = parent.pop() if len(parent) else None 
    margin = int(p.xpath('./@style').re_first('.* (\d+)px;.*')) 

    next_p = p.xpath('./following-sibling::p[1]') 
    if next_p: 
     next_margin = int(next_p.xpath('./@style').re_first('.* (\d+)px;.*')) 
     if next_margin > margin: 
      # next post is a reply to this post 
      if post['reply_to']: 
       parent.append(post['reply_to']) 
      parent.append(post['url']) 
     elif next_margin == margin: 
      # next post is a reply to direct parent post 
      parent.append(post['reply_to']) 
     else: 
      # next post if a reply to some distant parent post 
      for _ in range((margin - next_margin)/20 - 1): 
       parent.pop() 

    yield post 

基本的に、それはあなたがスレッドツリーの下に行くと、親記事へのリンクを保存するためにスタックを使用しています。この方法では、ツリーを前後に検索して、現在の返信がどの投稿であるかを調べる必要はありませんが、各ノードを1回だけ訪問することができます(常に、次の兄弟も見ているので2回)。

XPathと正規表現では簡単に行うことができますが、これをサポートしていないXPath 1.0のみを使用していると思います。私が間違っているなら私を訂正してください。

+0

も正直言って私は 'for _ in range((margin-next_margin)/ 20-1)のアンダースコア' _を理解しません: '... –

+0

はまさに私の魅力のように働いていました探している!このコードを必要とするあなたのためのマイナーフィックス、 '((margin-next_margin)/ 20-1)'を整数にキャストします。また、少なくとも私がscrapyシェルでこれを試しても、何か別のものにyield postを変更する必要があるかもしれません。コードはまだ私のクローラファイルに追加されていません。 @TomášLinhartありがとう! –

+0

@JindanZhouアンダースコアについて - 私は数回ループする必要がありますが、私は 'range 'が生成する特定の値には興味がありません。下線は変数の有効な識別子であり、このユースケースのサーバーでは何らかのダミーとして認識されます。より詳しい説明は、[here](https://hackernoon.com/understanding-the-underscore-of-python-309d1a029edc)を参照してください。 –

1

>[1]: sel.xpath('//p[re:test(@style,"margin[^;]+20px")]').extract() 
<[1]: ['<p style="margin:2px 0 2px 20px; width:683px"><a href="./6368973.html" class="post">reply post</a>other stuff</p>'] 

「// P [再:テスト(@style、 "マージン[^;] 20ピクセル")] 「内訳:

//p - @style属性はmargin.+20px正規表現に一致するかどうかをテスト - 任意の<p>ノード
[re:test(@style,"margin.+20px")]を選択します。

+0

これは本当に有望なように見えます。その間に –

関連する問題