diff --git a/docs/topics/selectors.rst b/docs/topics/selectors.rst index 25c1f0aab..00158ecf1 100644 --- a/docs/topics/selectors.rst +++ b/docs/topics/selectors.rst @@ -6,7 +6,7 @@ Selectors 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: +achieve this, such as: * `BeautifulSoup`_ is a very popular web scraping library among Python programmers which constructs a Python object based on the structure of the @@ -25,8 +25,9 @@ either by `XPath`_ or `CSS`_ expressions. used with HTML. `CSS`_ is a language for applying styles to HTML documents. It defines selectors to associate those styles with specific HTML elements. -Scrapy selectors are built over the `lxml`_ library, which means they're very -similar in speed and parsing accuracy. +Scrapy selectors are powered by `parsel`_ library, which uses `lxml`_ library +under the hood. It means Scrapy selectors are very similar in speed and +parsing accuracy to lxml. This page explains how selectors work and describes their API which is very small and simple, unlike the `lxml`_ API which is much bigger because the @@ -42,7 +43,7 @@ For a complete reference of the selectors API see .. _cssselect: https://pypi.python.org/pypi/cssselect/ .. _XPath: https://www.w3.org/TR/xpath .. _CSS: https://www.w3.org/TR/selectors - +.. _parsel: https://parsel.readthedocs.io/ Using selectors =============== @@ -63,21 +64,32 @@ input type:: Constructing from text:: >>> body = '
good' - >>> Selector(text=body).xpath('//span/text()').extract() - [u'good'] + >>> Selector(text=body).xpath('//span/text()').get() + 'good' Constructing from response:: >>> response = HtmlResponse(url='http://example.com', body=body) - >>> Selector(response=response).xpath('//span/text()').extract() - [u'good'] + >>> Selector(response=response).xpath('//span/text()').get() + 'good' For convenience, response objects expose a selector on `.selector` attribute, -it's totally OK to use this shortcut when possible:: +it's totally OK to use this shortcut when possible. By using it you can +ensure the response body is parsed only once:: - >>> response.selector.xpath('//span/text()').extract() - [u'good'] + >>> 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' + +Usually there is no need to construct Scrapy selectors manually because of +these shortcuts. Using selectors --------------- @@ -90,7 +102,7 @@ documentation server: .. _topics-selectors-htmlcode: -Here's its HTML code: +For the sake of completeness, here's its full HTML code: .. literalinclude:: ../_static/selectors-sample1.html :language: html @@ -111,90 +123,179 @@ Since we're dealing with HTML, the selector will automatically use an HTML parse So, by looking at the :ref:`HTML code`` elements from the document, not only those inside ``
from the whole document - ... print p.extract() + ... print(p.get()) This is the proper way to do it (note the dot prefixing the ``.//p`` XPath):: >>> for p in divs.xpath('.//p'): # extracts all
inside - ... print p.extract() + ... print(p.get()) Another common case would be to extract all direct ``
`` children:: >>> for p in divs.xpath('p'): - ... print p.extract() + ... print(p.get()) For more details about relative XPaths see the `Location Paths`_ section in the XPath specification. @@ -298,14 +443,14 @@ Here's an example to match an element based on its "id" attribute value, without hard-coding it (that was shown previously):: >>> # `$val` used in the expression, a `val` argument needs to be passed - >>> response.xpath('//div[@id=$val]/a/text()', val='images').extract_first() - u'Name: My image 1 ' + >>> response.xpath('//div[@id=$val]/a/text()', val='images').get() + 'Name: My image 1 ' Here's another example, to find the "id" attribute of a ``
@@ -424,12 +568,12 @@ with groups of itemscopes and corresponding itemprops::
... """
>>> sel = Selector(text=doc, type="html")
>>> for scope in sel.xpath('//div[@itemscope]'):
- ... print "current scope:", scope.xpath('@itemtype').extract()
+ ... print("current scope:", scope.xpath('@itemtype').getall())
... props = scope.xpath('''
... set:difference(./descendant::*/@itemprop,
... .//*[@itemscope]/*/@itemprop)''')
- ... print " properties:", props.extract()
- ... print
+ ... print(" properties: %s" % (props.getall()))
+ ... print("")
current scope: ['http://schema.org/Product']
properties: ['name', 'aggregateRating', 'offers', 'description', 'review', 'review']
@@ -493,27 +637,27 @@ Example::
Converting a *node-set* to string::
- >>> sel.xpath('//a//text()').extract() # take a peek at the node-set
- [u'Click here to go to the ', u'Next Page']
- >>> sel.xpath("string(//a[1]//text())").extract() # convert it to string
- [u'Click here to go to the ']
+ >>> 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 ']
A *node* converted to a string, however, puts together the text of itself plus of all its descendants::
- >>> sel.xpath("//a[1]").extract() # select the first node
- [u'Click here to go to the Next Page']
- >>> sel.xpath("string(//a[1])").extract() # convert it to string
- [u'Click here to go to the Next Page']
+ >>> sel.xpath("//a[1]").getall() # select the first node
+ ['Click here to go to the Next Page']
+ >>> sel.xpath("string(//a[1])").getall() # convert it to string
+ ['Click here to go to the Next Page']
So, using the ``.//text()`` node-set won't select anything in this case::
- >>> sel.xpath("//a[contains(.//text(), 'Next Page')]").extract()
+ >>> sel.xpath("//a[contains(.//text(), 'Next Page')]").getall()
[]
But using the ``.`` to mean the node, works::
- >>> sel.xpath("//a[contains(., 'Next Page')]").extract()
- [u'Click here to go to the Next Page']
+ >>> sel.xpath("//a[contains(., 'Next Page')]").getall()
+ ['Click here to go to the Next Page']
.. _`XPath string function`: https://www.w3.org/TR/xpath/#section-String-Functions
@@ -538,7 +682,7 @@ Example::
....: `` tags and print their class attribute::
for node in sel.xpath("//p"):
- print node.xpath("@class").extract()
+ print(node.attrib['class'])
+
+
+.. _selector-examples-xml:
Selector examples on XML response
---------------------------------
-Here's a couple of examples to illustrate several concepts. In both cases we
-assume there is already a :class:`Selector` instantiated with an
-:class:`~scrapy.http.XmlResponse` object like this::
+Here are some examples to illustrate concepts for :class:`Selector` objects
+instantiated with an :class:`~scrapy.http.XmlResponse` object::
sel = Selector(xml_response)
@@ -761,7 +956,7 @@ assume there is already a :class:`Selector` instantiated with an
a namespace::
sel.register_namespace("g", "http://base.google.com/ns/1.0")
- sel.xpath("//g:price").extract()
+ sel.xpath("//g:price").getall()
.. _removing-namespaces:
@@ -781,6 +976,20 @@ First, we open the shell with the url we want to scrape::
$ scrapy shell https://github.com/blog.atom
+.. highlight:: xml
+
+This is how the file starts::
+
+
+