Grab-HTMLドキュメントのDOMツリーを操作するための新しいインターフェース

歴史的遠足



以前、私はすでにHabrでGrab-サイトパーサーを作成するためのフレームワーク、 onetwothreefourについて書いています。 簡単に言えば、Grabは2つのライブラリーの上にある便利なシェルです。ネットワーク用のpycurlと、HTML文書の構文解析用のlxmlです。



lxmlライブラリを使用すると、DOMツリーに対してXPATHリクエストを作成し、有用なプロパティが多数あるElementTreeオブジェクトの形式で結果を取得できます。 数年前、私はいくつかの簡単なメソッドを開発しました。これにより、xpathリクエストをシデからロードされたドキュメントに適用できます。 コードを説明します。



>>> from grab import Grab >>> g = Grab() >>> g.go('http://habrahabr.ru/') <grab.response.Response object at 0x7fe5f7189850> >>> print g.xpath_text('//title')    /  / 
      
      







本質的に、これは次のコードに似ています。



 >>> from urllib import urlopen >>> from lxml.html import fromstring >>> data = urlopen('http://habrahabr.ru/').read() >>> dom = fromstring(data) >>> print dom.xpath('//title')[0].text_content()    /  / 
      
      







xpath_textメソッドの便利さは、Grabを介してロードされたドキュメントに自動的に適用されることです。ツリーを構築する必要はありません。これは自動的に行われます。また、最初の要素を手動で選択する必要もありません。 。 次に、Grabライブラリのすべてのメソッドとその簡単な説明を示します。







恥ずかしくないわけではありません。 Grab.xpathメソッド-選択範囲の最初の要素を返しますが、ElementTreeオブジェクトのxpathメソッドはリスト全体を返します。 人々は繰り返しこの熊手を見つけました。 また、CSSリクエストを処理するためのまったく同じメソッドのセット、つまり grab.css、grab.css_list、grab.css_textなど。ただし、XPATHを支持してCSS式を個人的に拒否しました。 XPATHはより強力なツールであり、多くの場合それを使用するのが理にかなっており、コード内にCSSやXPATH式のごちゃごちゃしたものを見たくありませんでした。



上記の方法には多くの欠点がありました。



まず、要素を選択するためのコードを別の関数に取り込む必要がある場合、Grabオブジェクト全体を渡して、これらの関数を呼び出すようにしたかったのです。 他の方法はありません。Grabオブジェクトを渡すか、xpath_textなどの便利な機能を持たないベアDOMオブジェクトを渡します。



次に、grab.xpathおよびgrab.xpath_list関数の結果は、xpath_textのようなメソッドを持たない素のElementTree要素です。



第三に、これはフレームワーク拡張の問題である可能性が高いですが、Grabオブジェクトの名前空間は上記のメソッドの多くで詰まっています。



ああ、そして4番目。 grab.xpathメソッドとgrab.xpath_listメソッドを使用して見つかった要素のHTMLコードを取得する方法の質問にbyしました。 grabはlxmlの単なるラッパーであり、lxml.deのマニュアルを読むだけでよいことを人々は理解したくありませんでした。



DOMツリーを操作するための新しいインターフェイスは、これらの欠点を解消するように設計されています。 Scrapyフレームワークを使用する場合、以下で説明することはすでにおなじみです。 セレクターについてお話します。



セレクター





セレクター、それは何ですか? これらはElementTree要素のラッパーです。 最初に、ドキュメントのDOMツリー全体がラッパーでラップされます。 ラッパーはルートhtml要素を中心に構築されます。 次に、selectメソッドを使用して、XPATH式を満たす要素のリストを取得できます。このような各要素は、Selectorラッパーで再びラップされます。



セレクターでできることを見てみましょう。 まず、セレクターを作成します



 >>> from grab.selector import Selector >>> from lxml.html import fromstring >>> root = Selector(fromstring('<html><body><h1>Header</h1><ul><li>Item 1</li><li><li>item 2</li></ul><span id="color">green</span>'))
      
      







selectメソッドを使用して選択を行うと、新しいセレクターのリストが取得されます。 インデックスによって目的のセレクタにアクセスできます。最初のセレクタを選択する1つのメソッドもあります。 ElementTree要素に直接アクセスするには、セレクターのノード属性にアクセスする必要があることに注意してください。



 >>> root.select('//ul') <grab.selector.selector.SelectorList object at 0x7fe5f41922d0> >>> root.select('//ul')[0] <grab.selector.selector.Selector object at 0x7fe5f419bed0> >>> root.select('//ul')[0].node <Element ul at 0x7fe5f41a7a70> >>> root.select('//ul').one() <grab.selector.selector.Selector object at 0x7fe5f419bed0>
      
      







見つかったセレクターで利用可能なアクションは何ですか? テキストコンテンツを抽出し、数値コンテンツを見つけ、さらに正規表現を適用することもできます。



 >>> root.select('//ul/li')[0].text() 'Item 1' >>> root.select('//ul/li')[0].number() 1 >>> root.select('//ul/li/text()')[0].rex('(\w+)').text() 'Item'
      
      







インデックスを使用して、最初に見つかったセレクターを参照することに注意してください。 これらのすべてのレコードは、インデックスを指定せずに削減できます。デフォルトでは、最初に見つかったセレクターが使用されます。



 >>> root.select('//ul/li').text() 'Item 1' >>> root.select('//ul/li').number() 1 >>> root.select('//ul/li/text()').rex('em (\d+)').text() '1' >>> root.select('//ul/li/text()').rex('em (\d+)').number() 1
      
      







他に何? セレクターHTMLコードを取得するhtmlメソッド、セレクターの存在を確認するexistsメソッド。 任意のセレクタでselectメソッドを呼び出すこともできます。



 >>> root.select('//span')[0].html() u'<span id="color">green</span>' >>> root.select('//span').exists() True >>> root.select('//god').exists() False >>> root.select('//ul')[0].select('./li[3]').text() 'item 2'
      
      







Grabオブジェクトから直接セレクターを操作する方法は? doc属性を使用して、DOMツリーのルートセレクターにアクセスし、目的の選択にselectメソッドを使用できます。



 >>> from grab import Grab >>> g = Grab() >>> g.go('http://habrahabr.ru/') <grab.response.Response object at 0x2853410> >>> print g.doc.select('//h1').text()   ,      MIT    >>> print g.doc.select('//div[contains(@class, "post")][2]')[0].select('.//div[@class="favs_count"]').number() 60 >>> print g.doc.select('//div[contains(@class, "post")][2]')[0].select('.//div[@class="favs_count"]')[0].html() <div class="favs_count" title=" ,    ">60</div>
      
      







Grabでのセレクターの現在の実装はまだかなり粗雑ですが、新しいインターフェースを理解して評価することはすでに可能であると思います。



Grabのバージョンには、pypiで使用可能なセレクターはまだありません。 セレクターを試してみたい場合は、リポジトリからGrabを配置します: bitbucket.org/lorien/grab 具体的には、セレクターの実装はこちらです



私は会社GrabLabを代表しています -私たちはサイトの解析に従事しており、Grabだけでなく解析も行っています。 あなたの会社がGrabを使用している場合、Grabをニーズに合わせて改良することについて私たちに連絡することができます。



All Articles