セレクタ¶
When you're scraping web pages, the most common task you need to perform is to extract data from the HTML source. There are several libraries available to achieve this, such as:
- BeautifulSoup は、Pythonプログラマーの間で非常に人気のあるWebスクレイピングライブラリです。HTMLコードの構造に基づいてPythonオブジェクトを作成し、また、不適切なマークアップを適切に処理しますが、遅いという欠点があります。
- lxml は、 ElementTree をベースにしたPythonicなAPIを備えたXML解析ライブラリ(HTMLも解析できます)です。lxmlはPython標準ライブラリの一部ではありません。
Scrapyには、データを抽出するための独自のメカニズムが付属しています。HTMLドキュメントの特定の部分を XPath 式または CSS 式で「選択」するため、これらはセレクタと呼ばれます。
XPath はXML文書内のノードを選択するための言語ですが、HTMLでも使用できます。 CSS はHTMLドキュメントにスタイルを適用するための言語です。スタイルを特定のHTML要素に関連付けるためのセレクタを定義します。
注釈
Scrapy Selectors is a thin wrapper around parsel library; the purpose of this wrapper is to provide better integration with Scrapy Response objects.
parsel is a stand-alone web scraping library which can be used without Scrapy. It uses lxml library under the hood, and implements an easy API on top of lxml API. It means Scrapy selectors are very similar in speed and parsing accuracy to lxml.
セレクタの使用¶
セレクタの構築¶
Response objects expose a Selector
instance
on .selector
attribute:
>>> response.selector.xpath('//span/text()').get()
'good'
Querying responses using XPath and CSS is so common that responses include two
more shortcuts: response.xpath()
and response.css()
:
>>> response.xpath('//span/text()').get()
'good'
>>> response.css('span::text').get()
'good'
Scrapy selectors are instances of Selector
class
constructed by passing either TextResponse
object or
markup as an unicode string (in text
argument).
Usually there is no need to construct Scrapy selectors manually:
response
object is available in Spider callbacks, so in most cases
it is more convenient to use response.css()
and response.xpath()
shortcuts. By using response.selector
or one of these shortcuts
you can also ensure the response body is parsed only once.
But if required, it is possible to use Selector
directly.
Constructing from text:
>>> from scrapy.selector import Selector
>>> body = '<html><body><span>good</span></body></html>'
>>> Selector(text=body).xpath('//span/text()').get()
'good'
Constructing from response - HtmlResponse
is one of
TextResponse
subclasses:
>>> from scrapy.selector import Selector
>>> from scrapy.http import HtmlResponse
>>> response = HtmlResponse(url='http://example.com', body=body)
>>> Selector(response=response).xpath('//span/text()').get()
'good'
Selector
automatically chooses the best parsing rules
(XML vs HTML) based on input type.
セレクタの使用¶
セレクタの使用方法を説明するために、 Scrapyシェル (これは対話式のテストを提供します)とScrapy文書サーバーにあるサンプルページを使用します。
For the sake of completeness, here's its full HTML code:
<html>
<head>
<base href='http://example.com/' />
<title>Example website</title>
</head>
<body>
<div id='images'>
<a href='image1.html'>Name: My image 1 <br /><img src='image1_thumb.jpg' /></a>
<a href='image2.html'>Name: My image 2 <br /><img src='image2_thumb.jpg' /></a>
<a href='image3.html'>Name: My image 3 <br /><img src='image3_thumb.jpg' /></a>
<a href='image4.html'>Name: My image 4 <br /><img src='image4_thumb.jpg' /></a>
<a href='image5.html'>Name: My image 5 <br /><img src='image5_thumb.jpg' /></a>
</div>
</body>
</html>
まず、シェルを開きましょう。
scrapy shell https://docs.scrapy.org/en/latest/_static/selectors-sample1.html
シェルがロードされた後に、レスポンスとして利用可能な response
シェル変数と、セレクタとして利用可能な response.selector
属性を使うことができます。
HTMLを扱うので、セレクタは自動的にHTMLパーサーになります。
そのページの HTMLコード を見て、titleタグ内のテキストを選択するためのXPathを作成しましょう。
>>> response.xpath('//title/text()')
[<Selector xpath='//title/text()' data='Example website'>]
To actually extract the textual data, you must call the selector .get()
or .getall()
methods, as follows:
>>> response.xpath('//title/text()').getall()
['Example website']
>>> response.xpath('//title/text()').get()
'Example website'
.get()
always returns a single result; if there are several matches,
content of a first match is returned; if there are no matches, None
is returned. .getall()
returns a list with all results.
CSSセレクタの場合は、CSS3の疑似要素を使用してテキストまたは属性ノードを選択できることに注意してください。
>>> response.css('title::text').get()
'Example website'
ご覧のように、 .xpath()
および .css()
メソッドは、新しいセレクタのリストである SelectorList
インスタンスを返します。このAPIはネストしたデータを素早く選択するために使用できます。
>>> response.css('img').xpath('@src').getall()
['image1_thumb.jpg',
'image2_thumb.jpg',
'image3_thumb.jpg',
'image4_thumb.jpg',
'image5_thumb.jpg']
If you want to extract only the first matched element, you can call the
selector .get()
(or its alias .extract_first()
commonly used in
previous Scrapy versions):
>>> response.xpath('//div[@id="images"]/a/text()').get()
'Name: My image 1 '
It returns None
if no element was found:
>>> response.xpath('//div[@id="not-exists"]/text()').get() is None
True
デフォルトの戻り値を引数として渡し、 None
の代わりに使用することもできます。
>>> response.xpath('//div[@id="not-exists"]/text()').get(default='not-found')
'not-found'
Instead of using e.g. '@src'
XPath it is possible to query for attributes
using .attrib
property of a Selector
:
>>> [img.attrib['src'] for img in response.css('img')]
['image1_thumb.jpg',
'image2_thumb.jpg',
'image3_thumb.jpg',
'image4_thumb.jpg',
'image5_thumb.jpg']
As a shortcut, .attrib
is also available on SelectorList directly;
it returns attributes for the first matching element:
>>> response.css('img').attrib['src']
'image1_thumb.jpg'
This is most useful when only a single result is expected, e.g. when selecting by id, or selecting unique elements on a web page:
>>> response.css('base').attrib['href']
'http://example.com/'
ベースURLと画像リンクを取得します。
>>> response.xpath('//base/@href').get()
'http://example.com/'
>>> response.css('base::attr(href)').get()
'http://example.com/'
>>> response.css('base').attrib['href']
'http://example.com/'
>>> response.xpath('//a[contains(@href, "image")]/@href').getall()
['image1.html',
'image2.html',
'image3.html',
'image4.html',
'image5.html']
>>> response.css('a[href*=image]::attr(href)').getall()
['image1.html',
'image2.html',
'image3.html',
'image4.html',
'image5.html']
>>> response.xpath('//a[contains(@href, "image")]/img/@src').getall()
['image1_thumb.jpg',
'image2_thumb.jpg',
'image3_thumb.jpg',
'image4_thumb.jpg',
'image5_thumb.jpg']
>>> response.css('a[href*=image] img::attr(src)').getall()
['image1_thumb.jpg',
'image2_thumb.jpg',
'image3_thumb.jpg',
'image4_thumb.jpg',
'image5_thumb.jpg']
Extensions to CSS Selectors¶
Per W3C standards, CSS selectors do not support selecting text nodes or attribute values. But selecting these is so essential in a web scraping context that Scrapy (parsel) implements a couple of non-standard pseudo-elements:
- to select text nodes, use
::text
- to select attribute values, use
::attr(name)
where name is the name of the attribute that you want the value of
警告
These pseudo-elements are Scrapy-/Parsel-specific. They will most probably not work with other libraries like lxml or PyQuery.
Examples:
title::text
selects children text nodes of a descendant<title>
element:>>> response.css('title::text').get() 'Example website'
*::text
selects all descendant text nodes of the current selector context:>>> response.css('#images *::text').getall() ['\n ', 'Name: My image 1 ', '\n ', 'Name: My image 2 ', '\n ', 'Name: My image 3 ', '\n ', 'Name: My image 4 ', '\n ', 'Name: My image 5 ', '\n ']
foo::text
returns no results iffoo
element exists, but contains no text (i.e. text is empty):>>> response.css('img::text').getall() []
This means
.css('foo::text').get()
could return None even if an element exists. Usedefault=''
if you always want a string:>>> response.css('img::text').get() >>> response.css('img::text').get(default='') ''
a::attr(href)
selects the href attribute value of descendant links:>>> response.css('a::attr(href)').getall() ['image1.html', 'image2.html', 'image3.html', 'image4.html', 'image5.html']
注釈
See also: Selecting element attributes.
注釈
You cannot chain these pseudo-elements. But in practice it would not make much sense: text nodes do not have attributes, and attribute values are string values already and do not have children nodes.
ネストされたセレクタ¶
選択メソッド( .xpath()
または .css()
)は同じタイプのセレクタのリストを返すので、それらのセレクタでさらに選択メソッドを呼び出すことができます。次に例を示します。
>>> links = response.xpath('//a[contains(@href, "image")]')
>>> links.getall()
['<a href="image1.html">Name: My image 1 <br><img src="image1_thumb.jpg"></a>',
'<a href="image2.html">Name: My image 2 <br><img src="image2_thumb.jpg"></a>',
'<a href="image3.html">Name: My image 3 <br><img src="image3_thumb.jpg"></a>',
'<a href="image4.html">Name: My image 4 <br><img src="image4_thumb.jpg"></a>',
'<a href="image5.html">Name: My image 5 <br><img src="image5_thumb.jpg"></a>']
>>> for index, link in enumerate(links):
... args = (index, link.xpath('@href').get(), link.xpath('img/@src').get())
... print('Link number %d points to url %r and image %r' % args)
Link number 0 points to url 'image1.html' and image 'image1_thumb.jpg'
Link number 1 points to url 'image2.html' and image 'image2_thumb.jpg'
Link number 2 points to url 'image3.html' and image 'image3_thumb.jpg'
Link number 3 points to url 'image4.html' and image 'image4_thumb.jpg'
Link number 4 points to url 'image5.html' and image 'image5_thumb.jpg'
Selecting element attributes¶
There are several ways to get a value of an attribute. First, one can use XPath syntax:
>>> response.xpath("//a/@href").getall()
['image1.html', 'image2.html', 'image3.html', 'image4.html', 'image5.html']
XPath syntax has a few advantages: it is a standard XPath feature, and
@attributes
can be used in other parts of an XPath expression - e.g.
it is possible to filter by attribute value.
Scrapy also provides an extension to CSS selectors (::attr(...)
)
which allows to get attribute values:
>>> response.css('a::attr(href)').getall()
['image1.html', 'image2.html', 'image3.html', 'image4.html', 'image5.html']
In addition to that, there is a .attrib
property of Selector.
You can use it if you prefer to lookup attributes in Python
code, without using XPaths or CSS extensions:
>>> [a.attrib['href'] for a in response.css('a')]
['image1.html', 'image2.html', 'image3.html', 'image4.html', 'image5.html']
This property is also available on SelectorList; it returns a dictionary with attributes of a first matching element. It is convenient to use when a selector is expected to give a single result (e.g. when selecting by element ID, or when selecting an unique element on a page):
>>> response.css('base').attrib
{'href': 'http://example.com/'}
>>> response.css('base').attrib['href']
'http://example.com/'
.attrib
property of an empty SelectorList is empty:
>>> response.css('foo').attrib
{}
セレクタで正規表現を使う¶
Selector
には、正規表現を使用してデータを抽出するための .re()
メソッドもあります。ただし、 .xpath()
や .css()
メソッドを使用するのとは異なり、 .re()
はUnicode文字列のリストを返します。そのため、 .re()
呼び出しを入れ子にすることはできません。
上記の HTMLコード からイメージ名を抽出するのに使用される例は、次のとおりです。
>>> response.xpath('//a[contains(@href, "image")]/text()').re(r'Name:\s*(.*)')
['My image 1',
'My image 2',
'My image 3',
'My image 4',
'My image 5']
There's an additional helper reciprocating .get()
(and its
alias .extract_first()
) for .re()
, named .re_first()
.
Use it to extract just the first matching string:
>>> response.xpath('//a[contains(@href, "image")]/text()').re_first(r'Name:\s*(.*)')
'My image 1'
extract() and extract_first()¶
If you're a long-time Scrapy user, you're probably familiar
with .extract()
and .extract_first()
selector methods. Many blog posts
and tutorials are using them as well. These methods are still supported
by Scrapy, there are no plans to deprecate them.
However, Scrapy usage docs are now written using .get()
and
.getall()
methods. We feel that these new methods result in a more concise
and readable code.
The following examples show how these methods map to each other.
SelectorList.get()
is the same asSelectorList.extract_first()
:>>> response.css('a::attr(href)').get() 'image1.html' >>> response.css('a::attr(href)').extract_first() 'image1.html'
SelectorList.getall()
is the same asSelectorList.extract()
:>>> response.css('a::attr(href)').getall() ['image1.html', 'image2.html', 'image3.html', 'image4.html', 'image5.html'] >>> response.css('a::attr(href)').extract() ['image1.html', 'image2.html', 'image3.html', 'image4.html', 'image5.html']
Selector.get()
is the same asSelector.extract()
:>>> response.css('a::attr(href)')[0].get() 'image1.html' >>> response.css('a::attr(href)')[0].extract() 'image1.html'
For consistency, there is also
Selector.getall()
, which returns a list:>>> response.css('a::attr(href)')[0].getall() ['image1.html']
So, the main difference is that output of .get()
and .getall()
methods
is more predictable: .get()
always returns a single result, .getall()
always returns a list of all extracted results. With .extract()
method
it was not always obvious if a result is a list or not; to get a single
result either .extract()
or .extract_first()
should be called.
Working with XPaths¶
Here are some tips which may help you to use XPath with Scrapy selectors effectively. If you are not much familiar with XPath yet, you may want to take a look first at this XPath tutorial.
注釈
Some of the tips are based on this post from ScrapingHub's blog.
XPathを相対的に操作する¶
セレクタを入れ子にして /
で始まるXPathを使用する場合、そのXPathはドキュメントに対して絶対的なものであり、呼び出し元の Selector
に対して相対的なものではないことに留意してください。
たとえば、 <div>
要素内のすべての <p>
要素を抽出するとします。まず、すべての <div>
要素を取得します。
>>> divs = response.xpath('//div')
最初は以下のようなアプローチを取りがちですが、これは、実際には <div>
要素の内部だけでなく、すべての <p>
要素を文書から抽出するため、間違っています。
>>> for p in divs.xpath('//p'): # this is wrong - gets all <p> from the whole document
... print(p.get())
こちらは適切な方法です(XPath .//p
にドットプレフィックスがあります)。
>>> for p in divs.xpath('.//p'): # extracts all <p> inside
... print(p.get())
もうひとつの一般的なケースは、直接の子である <p>
を抽出することです。
>>> for p in divs.xpath('p'):
... print(p.get())
相対XPathの詳細については、XPath仕様の Location Paths セクションを参照してください。
クラスで検索するときに、CSSの使用を検討する¶
要素には複数のCSSクラスを含めることができるので、クラスで要素を選択するXPathの方法はかなり冗長です。
*[contains(concat(' ', normalize-space(@class), ' '), ' someclass ')]
@class='someclass'
を使用すると、要素が他のクラスも持っていると取りこぼすことがあります。そのために contains(@class, 'someclass')
を使用しても、文字列 someclass
を含む異なるクラス名がある場合は、そちらも取得してしまいます。
結局のところ、Scrapyセレクタを使うとセレクタをチェーンさせることができるので、ほとんどの場合はCSSを使用してクラスで選択し、必要に応じてXPathに切り替えることができます。
>>> from scrapy import Selector
>>> sel = Selector(text='<div class="hero shout"><time datetime="2014-07-23 19:00">Special date</time></div>')
>>> sel.css('.shout').xpath('./time/@datetime').getall()
['2014-07-23 19:00']
これは、上記の冗長なXPathのトリックを使用するよりもクリーンです。続くXPath式で .
を使用することを忘れないでください。
//node[1] と (//node)[1] の違いに注意¶
//node[1]
は、それぞれの親の下に最初に出現するすべてのノードを選択します。
(//node)[1]
はドキュメント内のすべてのノードを選択し、それらのうち最初のノードだけを取得します。
例:
>>> from scrapy import Selector
>>> sel = Selector(text="""
....: <ul class="list">
....: <li>1</li>
....: <li>2</li>
....: <li>3</li>
....: </ul>
....: <ul class="list">
....: <li>4</li>
....: <li>5</li>
....: <li>6</li>
....: </ul>""")
>>> xp = lambda x: sel.xpath(x).getall()
こちらは、親であるものが何であれ、最初の <li>
要素をすべて取得します。
>>> xp("//li[1]")
['<li>1</li>', '<li>4</li>']
そしてこちらは、文書全体の最初の <li>
要素を取得します。
>>> xp("(//li)[1]")
['<li>1</li>']
こちらは、親が <ul>
である最初の <li>
要素をすべて取得します。
>>> xp("//ul/li[1]")
['<li>1</li>', '<li>4</li>']
そしてこちらは、文書全体の最初の、親が <ul>
である <li>
要素を取得します。
>>> xp("(//ul/li)[1]")
['<li>1</li>']
条件内でのテキストノードの使用¶
XPath文字列関数 の引数としてテキストコンテンツを使う必要がある場合は、 .//text()
を使用せず、代わりに .
を使用してください。
これは、式 .//text()
がテキスト要素の集合、つまり ノードセット を生成するためです。そして、ノードセットが contains()
や starts-with()
のような文字列関数に引数として渡され文字列に変換されるとき、最初の要素のみのテキストを返します。
例:
>>> from scrapy import Selector
>>> sel = Selector(text='<a href="#">Click here to go to the <strong>Next Page</strong></a>')
ノードセットを文字列に変換します。
>>> sel.xpath('//a//text()').getall() # take a peek at the node-set
['Click here to go to the ', 'Next Page']
>>> sel.xpath("string(//a[1]//text())").getall() # convert it to string
['Click here to go to the ']
ただし、文字列に変換された ノード は、それ自体のテキストとそのすべての子孫のテキストをまとめたものです。
>>> sel.xpath("//a[1]").getall() # select the first node
['<a href="#">Click here to go to the <strong>Next Page</strong></a>']
>>> sel.xpath("string(//a[1])").getall() # convert it to string
['Click here to go to the Next Page']
したがって、 .//text()
ノードセットを使用しても、この場合は何も選択されません。
>>> sel.xpath("//a[contains(.//text(), 'Next Page')]").getall()
[]
しかし、ノードを意味する .
を使用すると、うまくいきます。
>>> sel.xpath("//a[contains(., 'Next Page')]").getall()
['<a href="#">Click here to go to the <strong>Next Page</strong></a>']
XPath式の中の変数¶
XPathでは、 $somevariable
構文を使用して、XPath式内の変数を参照できます。 これは、クエリの引数の一部を ?
などのプレースホルダに置き換えたSQLの世界でのパラメータ化クエリやプリペアドステートメントとやや似ています。これらはクエリに渡された値に置き換えられます。
次の例は、ハードコーディングをせずに、その「id」属性値に基づいて要素を照合します(以前に示したものです)。
>>> # `$val` used in the expression, a `val` argument needs to be passed
>>> response.xpath('//div[@id=$val]/a/text()', val='images').get()
'Name: My image 1 '
別の例として、5つの <a>
の子を含む <div>
タグの「id」属性を見つけるものを示します(ここでは値 5
を整数として渡します)。
>>> response.xpath('//div[count(a)=$cnt]/@id', cnt=5).get()
'images'
すべての変数参照は、 .xpath()
を呼び出すときにバインド値を持つ必要があります(そうでなければ、 ValueError: XPath error:
例外が発生します)。これは、必要な数の名前付き引数を渡すことによって行われます。
ネームスペースを削除する¶
スクレイピングプロジェクトを扱うときに、ネームスペースを完全に取り除くと、要素名を扱うだけで、より単純で便利なXPathを書くことが非常に便になります。これには Selector.remove_namespaces()
メソッドを使用できます。
Let's show an example that illustrates this with the Python Insider blog atom feed.
まず、抽出したいURLでシェルを開きます。
$ scrapy shell https://feeds.feedburner.com/PythonInsider
This is how the file starts:
<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet ...
<feed xmlns="http://www.w3.org/2005/Atom"
xmlns:openSearch="http://a9.com/-/spec/opensearchrss/1.0/"
xmlns:blogger="http://schemas.google.com/blogger/2008"
xmlns:georss="http://www.georss.org/georss"
xmlns:gd="http://schemas.google.com/g/2005"
xmlns:thr="http://purl.org/syndication/thread/1.0"
xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0">
...
You can see several namespace declarations including a default "http://www.w3.org/2005/Atom" and another one using the "gd:" prefix for "http://schemas.google.com/g/2005".
シェルに入ったら、すべての <link>
オブジェクトを選択しても機能しないことを確認します(Atom XMLネームスペースがこれらのノードを見づらくしているため)。
>>> response.xpath("//link")
[]
しかし、 Selector.remove_namespaces()
メソッドを呼び出すと、すべてのノードにそれらの名前で直接アクセスできます。
>>> response.selector.remove_namespaces()
>>> response.xpath("//link")
[<Selector xpath='//link' data='<link rel="alternate" type="text/html" h'>,
<Selector xpath='//link' data='<link rel="next" type="application/atom+'>,
...
ネームスペースの削除を手動で呼び出すのではなく、デフォルトで常に呼び出されない理由は、以下の2つです。
- Removing namespaces requires to iterate and modify all nodes in the document, which is a reasonably expensive operation to perform by default for all documents crawled by Scrapy
- 非常にまれなケースですが、いくつかの要素名がネームスペースの間で衝突する場合のために、ネームスペースの使用が実際に必要とされる場合があるかもしれません。
EXSLT拡張機能を使う¶
Being built atop lxml, Scrapy selectors support some EXSLT extensions and come with these pre-registered namespaces to use in XPath expressions:
プレフィクス | ネームスペース | 使用法 |
---|---|---|
re | http://exslt.org/regular-expressions | regular expressions |
set | http://exslt.org/sets | set manipulation |
正規表現¶
たとえば、 test()
関数は、XPathの starts-with()
や contains()
だけでは不十分な場合に非常に便利です。
数字で終わる「class」属性を持つリスト項目内のリンクを選択する例:
>>> from scrapy import Selector
>>> doc = u"""
... <div>
... <ul>
... <li class="item-0"><a href="link1.html">first item</a></li>
... <li class="item-1"><a href="link2.html">second item</a></li>
... <li class="item-inactive"><a href="link3.html">third item</a></li>
... <li class="item-1"><a href="link4.html">fourth item</a></li>
... <li class="item-0"><a href="link5.html">fifth item</a></li>
... </ul>
... </div>
... """
>>> sel = Selector(text=doc, type="html")
>>> sel.xpath('//li//@href').getall()
['link1.html', 'link2.html', 'link3.html', 'link4.html', 'link5.html']
>>> sel.xpath('//li[re:test(@class, "item-\d$")]//@href').getall()
['link1.html', 'link2.html', 'link4.html', 'link5.html']
>>>
警告
Cライブラリ libxslt
はEXSLT正規表現をネイティブサポートしていないため、 lxml の実装はPythonの re
モジュールへのフックを使用します。そのため、XPath式で正規表現関数を使用すると、パフォーマンスが若干低下する可能性があります。
集合演算¶
これらは、例えばテキスト要素を抽出する前に文書ツリーの一部を除外するのに便利です。
itemscopeとそれに対応するitempropのグループを含むmicrodataの抽出例( http://schema.org/Product から取得したサンプルコンテンツ):
>>> doc = u"""
... <div itemscope itemtype="http://schema.org/Product">
... <span itemprop="name">Kenmore White 17" Microwave</span>
... <img src="kenmore-microwave-17in.jpg" alt='Kenmore 17" Microwave' />
... <div itemprop="aggregateRating"
... itemscope itemtype="http://schema.org/AggregateRating">
... Rated <span itemprop="ratingValue">3.5</span>/5
... based on <span itemprop="reviewCount">11</span> customer reviews
... </div>
...
... <div itemprop="offers" itemscope itemtype="http://schema.org/Offer">
... <span itemprop="price">$55.00</span>
... <link itemprop="availability" href="http://schema.org/InStock" />In stock
... </div>
...
... Product description:
... <span itemprop="description">0.7 cubic feet countertop microwave.
... Has six preset cooking categories and convenience features like
... Add-A-Minute and Child Lock.</span>
...
... Customer reviews:
...
... <div itemprop="review" itemscope itemtype="http://schema.org/Review">
... <span itemprop="name">Not a happy camper</span> -
... by <span itemprop="author">Ellie</span>,
... <meta itemprop="datePublished" content="2011-04-01">April 1, 2011
... <div itemprop="reviewRating" itemscope itemtype="http://schema.org/Rating">
... <meta itemprop="worstRating" content = "1">
... <span itemprop="ratingValue">1</span>/
... <span itemprop="bestRating">5</span>stars
... </div>
... <span itemprop="description">The lamp burned out and now I have to replace
... it. </span>
... </div>
...
... <div itemprop="review" itemscope itemtype="http://schema.org/Review">
... <span itemprop="name">Value purchase</span> -
... by <span itemprop="author">Lucas</span>,
... <meta itemprop="datePublished" content="2011-03-25">March 25, 2011
... <div itemprop="reviewRating" itemscope itemtype="http://schema.org/Rating">
... <meta itemprop="worstRating" content = "1"/>
... <span itemprop="ratingValue">4</span>/
... <span itemprop="bestRating">5</span>stars
... </div>
... <span itemprop="description">Great microwave for the price. It is small and
... fits in my apartment.</span>
... </div>
... ...
... </div>
... """
>>> sel = Selector(text=doc, type="html")
>>> for scope in sel.xpath('//div[@itemscope]'):
... print("current scope:", scope.xpath('@itemtype').getall())
... props = scope.xpath('''
... set:difference(./descendant::*/@itemprop,
... .//*[@itemscope]/*/@itemprop)''')
... print(" properties: %s" % (props.getall()))
... print("")
current scope: ['http://schema.org/Product']
properties: ['name', 'aggregateRating', 'offers', 'description', 'review', 'review']
current scope: ['http://schema.org/AggregateRating']
properties: ['ratingValue', 'reviewCount']
current scope: ['http://schema.org/Offer']
properties: ['price', 'availability']
current scope: ['http://schema.org/Review']
properties: ['name', 'author', 'datePublished', 'reviewRating', 'description']
current scope: ['http://schema.org/Rating']
properties: ['worstRating', 'ratingValue', 'bestRating']
current scope: ['http://schema.org/Review']
properties: ['name', 'author', 'datePublished', 'reviewRating', 'description']
current scope: ['http://schema.org/Rating']
properties: ['worstRating', 'ratingValue', 'bestRating']
>>>
ここではまず itemscope
要素を反復し、それぞれに対して itemprops
要素をすべて探し、別の itemscope
内にあるものを除外します。
Other XPath extensions¶
Scrapy selectors also provide a sorely missed XPath extension function
has-class
that returns True
for nodes that have all of the specified
HTML classes.
For the following HTML:
<p class="foo bar-baz">First</p>
<p class="foo">Second</p>
<p class="bar">Third</p>
<p>Fourth</p>
You can use it like this:
>>> response.xpath('//p[has-class("foo")]')
[<Selector xpath='//p[has-class("foo")]' data='<p class="foo bar-baz">First</p>'>,
<Selector xpath='//p[has-class("foo")]' data='<p class="foo">Second</p>'>]
>>> response.xpath('//p[has-class("foo", "bar-baz")]')
[<Selector xpath='//p[has-class("foo", "bar-baz")]' data='<p class="foo bar-baz">First</p>'>]
>>> response.xpath('//p[has-class("foo", "bar")]')
[]
So XPath //p[has-class("foo", "bar-baz")]
is roughly equivalent to CSS
p.foo.bar-baz
. Please note, that it is slower in most of the cases,
because it's a pure-Python function that's invoked for every node in question
whereas the CSS lookup is translated into XPath and thus runs more efficiently,
so performance-wise its uses are limited to situations that are not easily
described with CSS selectors.
Parsel also simplifies adding your own XPath extensions.
Examples¶
HTMLレスポンスに関するセレクタの例¶
Here are some Selector
examples to illustrate several concepts.
In all cases, we assume there is already a Selector
instantiated with
a HtmlResponse
object like this:
sel = Selector(html_response)
HTMLのレスポンスボディからすべての
<h1>
要素を選択し、Selector
オブジェクトのリスト(つまりSelectorList
オブジェクト)を返します。sel.xpath("//h1")
HTMLのレスポンスボディからすべての
<h1>
要素のテキストを抽出し、Unicode文字列のリストを返します。sel.xpath("//h1").getall() # this includes the h1 tag sel.xpath("//h1/text()").getall() # this excludes the h1 tag
すべての
<p>
タグを繰り返し処理し、それらのclass属性を出力します。for node in sel.xpath("//p"): print(node.attrib['class'])
XMLレスポンスに関するセレクタの例¶
Here are some examples to illustrate concepts for Selector
objects
instantiated with an XmlResponse
object:
sel = Selector(xml_response)
XMLのレスポンスボディからすべての
<product>
要素を選択し、Selector
オブジェクトのリスト(つまりSelectorList
オブジェクト)を返します。sel.xpath("//product")
ネームスペースを登録する必要がある Google Base XML feed フィードからすべての価格を抽出します。
sel.register_namespace("g", "http://base.google.com/ns/1.0") sel.xpath("//g:price").getall()