mirror of https://github.com/scrapy/scrapy.git
moved scrapy.xpath to scrapy.selector
--HG-- rename : scrapy/xpath/__init__.py => scrapy/selector/__init__.py rename : scrapy/xpath/document.py => scrapy/selector/document.py rename : scrapy/xpath/factories.py => scrapy/selector/factories.py
This commit is contained in:
parent
dcc90fc196
commit
33b53c59d5
|
|
@ -192,9 +192,9 @@ These are just a couple of simple examples of what you can do with XPath, but
|
|||
XPath expression are indeed much more powerful. To learn more about XPath we
|
||||
recommend `this XPath tutorial <http://www.w3schools.com/XPath/default.asp>`_.
|
||||
|
||||
For working with XPaths, Scrapy provides a :class:`~scrapy.xpath.XPathSelector`
|
||||
class, which comes in two flavours, :class:`~scrapy.xpath.HtmlXPatSelector`
|
||||
(for HTML data) and :class:`~scrapy.xpath.XmlXPathSelector` (for XML data). In
|
||||
For working with XPaths, Scrapy provides a :class:`~scrapy.selector.XPathSelector`
|
||||
class, which comes in two flavours, :class:`~scrapy.selector.HtmlXPatSelector`
|
||||
(for HTML data) and :class:`~scrapy.selector.XmlXPathSelector` (for XML data). In
|
||||
order to use them you must instantiate the desired class with a
|
||||
:class:`~scrapy.http.Response` object.
|
||||
|
||||
|
|
@ -205,14 +205,14 @@ node, or the entire document.
|
|||
Selectors have three methods (click on the method to see the complete API
|
||||
documentation).
|
||||
|
||||
* :meth:`~scrapy.xpath.XPathSelector.x`: returns a list of selectors, each of
|
||||
* :meth:`~scrapy.selector.XPathSelector.x`: returns a list of selectors, each of
|
||||
them representing the nodes selected by the xpath expression given as
|
||||
argument.
|
||||
|
||||
* :meth:`~scrapy.xpath.XPathSelector.extract`: returns a unicode string with
|
||||
* :meth:`~scrapy.selector.XPathSelector.extract`: returns a unicode string with
|
||||
the data selected by the XPath selector.
|
||||
|
||||
* :meth:`~scrapy.xpath.XPathSelector.re`: returns a list unicode strings
|
||||
* :meth:`~scrapy.selector.XPathSelector.re`: returns a list unicode strings
|
||||
extracted by applying the regular expression given as argument.
|
||||
|
||||
|
||||
|
|
@ -235,10 +235,10 @@ This is what the shell looks like::
|
|||
|
||||
------------------------------------------------------------------------------
|
||||
Available Scrapy variables:
|
||||
xxs: <class 'scrapy.xpath.selector.XmlXPathSelector'>
|
||||
xxs: <class 'scrapy.selector.XmlXPathSelector'>
|
||||
url: http://www.dmoz.org/Computers/Programming/Languages/Python/Books/
|
||||
spider: <class 'dmoz.spiders.dmoz.OpenDirectorySpider'>
|
||||
hxs: <class 'scrapy.xpath.selector.HtmlXPathSelector'>
|
||||
hxs: <class 'scrapy.selector.HtmlXPathSelector'>
|
||||
item: <class 'scrapy.item.Item'>
|
||||
response: <class 'scrapy.http.response.html.HtmlResponse'>
|
||||
Available commands:
|
||||
|
|
@ -331,7 +331,7 @@ that property here, so::
|
|||
Let's add this code to our spider::
|
||||
|
||||
from scrapy.spider import BaseSpider
|
||||
from scrapy.xpath.selector import HtmlXPathSelector
|
||||
from scrapy.selector import HtmlXPathSelector
|
||||
|
||||
class DmozSpider(BaseSpider):
|
||||
domain_name = "dmoz.org"
|
||||
|
|
@ -362,7 +362,7 @@ Spiders are expected to return their scraped data inside
|
|||
scraped so far, the code for our Spider should be like this::
|
||||
|
||||
from scrapy.spider import BaseSpider
|
||||
from scrapy.xpath.selector import HtmlXPathSelector
|
||||
from scrapy.selector import HtmlXPathSelector
|
||||
|
||||
from dmoz.items import DmozItem
|
||||
|
||||
|
|
|
|||
|
|
@ -333,7 +333,7 @@ ItemLoader objects
|
|||
|
||||
:param selector: The selector to extract data from, when using the
|
||||
:meth:`add_xpath` or :meth:`replace_xpath` method.
|
||||
:type selector: :class:`~scrapy.xpath.XPathSelector` object
|
||||
:type selector: :class:`~scrapy.selector.XPathSelector` object
|
||||
|
||||
:param response: The response used to construct the selector using the
|
||||
:attr:`default_selector_class`, unless the selector argument is given,
|
||||
|
|
@ -346,7 +346,7 @@ ItemLoader objects
|
|||
value, which is used to extract a list of unicode strings from the
|
||||
selector associated with this :class:`XPathItemLoader`. If the ``re``
|
||||
argument is given, it's used for extrating data from the selector using
|
||||
the :meth:`~scrapy.xpath.XPathSelector.re` method.
|
||||
the :meth:`~scrapy.selector.XPathSelector.re` method.
|
||||
|
||||
:param xpath: the XPath to extract data from
|
||||
:type xpath: str
|
||||
|
|
@ -376,7 +376,7 @@ ItemLoader objects
|
|||
|
||||
.. attribute:: selector
|
||||
|
||||
The :class:`~scrapy.xpath.XPathSelector` object to extract data from.
|
||||
The :class:`~scrapy.selector.XPathSelector` object to extract data from.
|
||||
It's either the selector given in the constructor or one created from
|
||||
the response given in the constructor using the
|
||||
:attr:`default_selector_class`. This attribute is meant to be
|
||||
|
|
@ -547,7 +547,7 @@ Here is a list of all built-in processors:
|
|||
work with single values (instead of iterables). For this reason the
|
||||
:class:`MapCompose` processor is typically used as input processor, since
|
||||
data is often extracted using the
|
||||
:meth:`~scrapy.xpath.XPathSelector.extract` method of :ref:`selectors
|
||||
:meth:`~scrapy.selector.XPathSelector.extract` method of :ref:`selectors
|
||||
<topics-selectors>`, which returns a list of unicode strings.
|
||||
|
||||
The example below should clarify how it works::
|
||||
|
|
|
|||
|
|
@ -49,9 +49,9 @@ Constructing selectors
|
|||
|
||||
There are two types of selectors bundled with Scrapy. Those are:
|
||||
|
||||
* :class:`~scrapy.xpath.HtmlXPathSelector` - for working with HTML documents
|
||||
* :class:`~scrapy.selector.HtmlXPathSelector` - for working with HTML documents
|
||||
|
||||
* :class:`~scrapy.xpath.XmlXPathSelector` - for working with XML documents
|
||||
* :class:`~scrapy.selector.XmlXPathSelector` - for working with XML documents
|
||||
|
||||
.. highlight:: python
|
||||
|
||||
|
|
@ -89,7 +89,7 @@ Then, after the shell loads, you'll have some selectors already instanced and
|
|||
ready to use.
|
||||
|
||||
Since we're dealing with HTML we'll be using the
|
||||
:class:`~scrapy.xpath.HtmlXPathSelector` object which is found, by default, in
|
||||
:class:`~scrapy.selector.HtmlXPathSelector` object which is found, by default, in
|
||||
the ``hxs`` shell variable.
|
||||
|
||||
.. highlight:: python
|
||||
|
|
@ -135,7 +135,7 @@ Using selectors with regular expressions
|
|||
|
||||
Selectors also have a ``re()`` method for extracting data using regular
|
||||
expressions. However, unlike using the ``select()`` method, the ``re()`` method
|
||||
does not return a list of :class:`~scrapy.xpath.XPathSelector` objects, so you
|
||||
does not return a list of :class:`~scrapy.selector.XPathSelector` objects, so you
|
||||
can't construct nested ``.re()`` calls.
|
||||
|
||||
Here's an example used to extract images names from the :ref:`HTML code
|
||||
|
|
@ -216,7 +216,7 @@ XPath specification.
|
|||
Built-in XPath Selectors reference
|
||||
==================================
|
||||
|
||||
.. module:: scrapy.xpath
|
||||
.. module:: scrapy.selector
|
||||
:synopsis: XPath selectors classes
|
||||
|
||||
There are two types of selectors bundled with Scrapy:
|
||||
|
|
|
|||
|
|
@ -66,7 +66,7 @@ Custom Shell Objects
|
|||
|
||||
The console automatically creates some useful Scrapy objects for the downloaded
|
||||
page, like the :class:`~scrapy.http.Response` object and the
|
||||
:class:`~scrapy.xpath.XPathSelector` objects (for both HTML and XML content).
|
||||
:class:`~scrapy.selector.XPathSelector` objects (for both HTML and XML content).
|
||||
|
||||
Those objects are:
|
||||
|
||||
|
|
@ -83,10 +83,10 @@ Those objects are:
|
|||
* ``response`` - a :class:`~scrapy.http.Response` object of the last fetched
|
||||
page
|
||||
|
||||
* ``hxs`` - a :class:`~scrapy.xpath.HtmlXPathSelector` object for the Response
|
||||
* ``hxs`` - a :class:`~scrapy.selector.HtmlXPathSelector` object for the Response
|
||||
of the downloaded page
|
||||
|
||||
* ``xxs`` - a :class:`~scrapy.xpath.XmlXPathSelector` object for the Response
|
||||
* ``xxs`` - a :class:`~scrapy.selector.XmlXPathSelector` object for the Response
|
||||
of the downloaded page
|
||||
|
||||
* ``get <url>``- download a new response from the given URL and update all
|
||||
|
|
|
|||
|
|
@ -219,7 +219,7 @@ Let's now take a look at an example CrawlSpider with rules::
|
|||
|
||||
from scrapy.contrib.spiders import CrawlSpider, Rule
|
||||
from scrapy.contrib.linkextractors.sgml import SgmlLinkExtractor
|
||||
from scrapy.xpath.selector import HtmlXPathSelector
|
||||
from scrapy.selector import HtmlXPathSelector
|
||||
from scrapy.item import Item
|
||||
|
||||
class MySpider(CrawlSpider):
|
||||
|
|
@ -297,7 +297,7 @@ XMLFeedSpider
|
|||
available in that document that will be processed with this spider. The
|
||||
``prefix`` and ``uri`` will be used to automatically register
|
||||
namespaces using the
|
||||
:meth:`~scrapy.xpath.XPathSelector.register_namespace` method.
|
||||
:meth:`~scrapy.selector.XPathSelector.register_namespace` method.
|
||||
|
||||
You can then specify nodes with namespaces in the :attr:`itertag`
|
||||
attribute.
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ from twisted.internet import reactor, threads
|
|||
|
||||
from scrapy.command import ScrapyCommand
|
||||
from scrapy.spider import spiders
|
||||
from scrapy.xpath import XmlXPathSelector, HtmlXPathSelector
|
||||
from scrapy.selector import XmlXPathSelector, HtmlXPathSelector
|
||||
from scrapy.utils.misc import load_object
|
||||
from scrapy.conf import settings
|
||||
from scrapy.core.manager import scrapymanager
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ import urlparse
|
|||
from scrapy.link import Link
|
||||
from scrapy.utils.url import canonicalize_url, urljoin_rfc
|
||||
from scrapy.utils.python import unicode_to_str, flatten
|
||||
from scrapy.xpath.selector import XPathSelectorList, HtmlXPathSelector
|
||||
from scrapy.selector import XPathSelectorList, HtmlXPathSelector
|
||||
|
||||
class HTMLImageLinkExtractor(object):
|
||||
'''HTMLImageLinkExtractor objects are intended to extract image links from HTML pages
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ SGMLParser-based Link extractors
|
|||
|
||||
import re
|
||||
|
||||
from scrapy.xpath import HtmlXPathSelector
|
||||
from scrapy.selector import HtmlXPathSelector
|
||||
from scrapy.link import Link
|
||||
from scrapy.utils.misc import arg_to_iter
|
||||
from scrapy.utils.python import FixedSGMLParser, unique as unique_list, str_to_unicode
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ See documentation in docs/topics/loaders.rst
|
|||
from collections import defaultdict
|
||||
|
||||
from scrapy.item import Item
|
||||
from scrapy.xpath import HtmlXPathSelector
|
||||
from scrapy.selector import HtmlXPathSelector
|
||||
from scrapy.utils.misc import arg_to_iter
|
||||
from .common import wrap_loader_context
|
||||
from .processor import Identity
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ from scrapy.contrib.spiders.init import InitSpider
|
|||
from scrapy.item import BaseItem
|
||||
from scrapy.http import Request
|
||||
from scrapy.utils.iterators import xmliter, csviter
|
||||
from scrapy.xpath.selector import XmlXPathSelector, HtmlXPathSelector
|
||||
from scrapy.selector import XmlXPathSelector, HtmlXPathSelector
|
||||
from scrapy.core.exceptions import NotConfigured, NotSupported
|
||||
|
||||
class XMLFeedSpider(InitSpider):
|
||||
|
|
|
|||
|
|
@ -0,0 +1,153 @@
|
|||
"""
|
||||
XPath selectors
|
||||
|
||||
See documentation in docs/ref/selectors.rst
|
||||
"""
|
||||
|
||||
import libxml2
|
||||
|
||||
from scrapy.http import TextResponse
|
||||
from scrapy.utils.python import flatten, unicode_to_str
|
||||
from scrapy.utils.misc import extract_regex
|
||||
from scrapy.utils.trackref import object_ref
|
||||
from scrapy.utils.decorator import deprecated
|
||||
from .factories import xmlDoc_from_html, xmlDoc_from_xml
|
||||
from .document import Libxml2Document
|
||||
|
||||
__all__ = ['HtmlXPathSelector', 'XmlXPathSelector', 'XPathSelector', \
|
||||
'XPathSelectorList']
|
||||
|
||||
class XPathSelector(object_ref):
|
||||
|
||||
__slots__ = ['doc', 'xmlNode', 'expr', '__weakref__']
|
||||
|
||||
def __init__(self, response=None, text=None, node=None, parent=None, expr=None):
|
||||
if parent:
|
||||
self.doc = parent.doc
|
||||
self.xmlNode = node
|
||||
elif response:
|
||||
self.doc = Libxml2Document(response, factory=self._get_libxml2_doc)
|
||||
self.xmlNode = self.doc.xmlDoc
|
||||
elif text:
|
||||
response = TextResponse(url=None, body=unicode_to_str(text), \
|
||||
encoding='utf-8')
|
||||
self.doc = Libxml2Document(response, factory=self._get_libxml2_doc)
|
||||
self.xmlNode = self.doc.xmlDoc
|
||||
self.expr = expr
|
||||
|
||||
def select(self, xpath):
|
||||
"""Perform the given XPath query on the current XPathSelector and
|
||||
return a XPathSelectorList of the result"""
|
||||
if hasattr(self.xmlNode, 'xpathEval'):
|
||||
self.doc.xpathContext.setContextNode(self.xmlNode)
|
||||
try:
|
||||
xpath_result = self.doc.xpathContext.xpathEval(xpath)
|
||||
except libxml2.xpathError:
|
||||
raise ValueError("Invalid XPath: %s" % xpath)
|
||||
if hasattr(xpath_result, '__iter__'):
|
||||
return XPathSelectorList([self.__class__(node=node, parent=self, \
|
||||
expr=xpath) for node in xpath_result])
|
||||
else:
|
||||
return XPathSelectorList([self.__class__(node=xpath_result, \
|
||||
parent=self, expr=xpath)])
|
||||
else:
|
||||
return XPathSelectorList([])
|
||||
|
||||
def re(self, regex):
|
||||
"""Return a list of unicode strings by applying the regex over all
|
||||
current XPath selections, and flattening the results"""
|
||||
return extract_regex(regex, self.extract(), 'utf-8')
|
||||
|
||||
def extract(self):
|
||||
"""Return a unicode string of the content referenced by the XPathSelector"""
|
||||
if isinstance(self.xmlNode, basestring):
|
||||
text = unicode(self.xmlNode, 'utf-8', errors='ignore')
|
||||
elif hasattr(self.xmlNode, 'serialize'):
|
||||
if isinstance(self.xmlNode, libxml2.xmlDoc):
|
||||
data = self.xmlNode.getRootElement().serialize('utf-8')
|
||||
text = unicode(data, 'utf-8', errors='ignore') if data else u''
|
||||
elif isinstance(self.xmlNode, libxml2.xmlAttr):
|
||||
# serialization doesn't work sometimes for xmlAttr types
|
||||
text = unicode(self.xmlNode.content, 'utf-8', errors='ignore')
|
||||
else:
|
||||
data = self.xmlNode.serialize('utf-8')
|
||||
text = unicode(data, 'utf-8', errors='ignore') if data else u''
|
||||
else:
|
||||
try:
|
||||
text = unicode(self.xmlNode, 'utf-8', errors='ignore')
|
||||
except TypeError: # catched when self.xmlNode is a float - see tests
|
||||
text = unicode(self.xmlNode)
|
||||
return text
|
||||
|
||||
def extract_unquoted(self):
|
||||
"""Get unescaped contents from the text node (no entities, no CDATA)"""
|
||||
if self.select('self::text()'):
|
||||
return unicode(self.xmlNode.getContent(), 'utf-8', errors='ignore')
|
||||
else:
|
||||
return u''
|
||||
|
||||
def register_namespace(self, prefix, uri):
|
||||
"""Register namespace so that it can be used in XPath queries"""
|
||||
self.doc.xpathContext.xpathRegisterNs(prefix, uri)
|
||||
|
||||
def _get_libxml2_doc(self, response):
|
||||
"""Return libxml2 document (xmlDoc) from response"""
|
||||
return xmlDoc_from_html(response)
|
||||
|
||||
def __nonzero__(self):
|
||||
return bool(self.extract())
|
||||
|
||||
def __str__(self):
|
||||
return "<%s (%s) xpath=%s>" % (type(self).__name__, getattr(self.xmlNode, \
|
||||
'name', type(self.xmlNode).__name__), self.expr)
|
||||
|
||||
__repr__ = __str__
|
||||
|
||||
@deprecated(use_instead='XPathSelector.select')
|
||||
def __call__(self, xpath):
|
||||
return self.select(xpath)
|
||||
|
||||
@deprecated(use_instead='XPathSelector.select')
|
||||
def x(self, xpath):
|
||||
return self.select(xpath)
|
||||
|
||||
|
||||
class XPathSelectorList(list):
|
||||
"""List of XPathSelector objects"""
|
||||
|
||||
def __getslice__(self, i, j):
|
||||
return XPathSelectorList(list.__getslice__(self, i, j))
|
||||
|
||||
def select(self, xpath):
|
||||
"""Perform the given XPath query on each XPathSelector of the list and
|
||||
return a new (flattened) XPathSelectorList of the results"""
|
||||
return XPathSelectorList(flatten([x.select(xpath) for x in self]))
|
||||
|
||||
def re(self, regex):
|
||||
"""Perform the re() method on each XPathSelector of the list, and
|
||||
return the result as a flattened list of unicode strings"""
|
||||
return flatten([x.re(regex) for x in self])
|
||||
|
||||
def extract(self):
|
||||
"""Return a list of unicode strings with the content referenced by each
|
||||
XPathSelector of the list"""
|
||||
return [x.extract() if isinstance(x, XPathSelector) else x for x in self]
|
||||
|
||||
def extract_unquoted(self):
|
||||
return [x.extract_unquoted() if isinstance(x, XPathSelector) else x for x in self]
|
||||
|
||||
@deprecated(use_instead='XPathSelectorList.select')
|
||||
def x(self, xpath):
|
||||
return self.select(xpath)
|
||||
|
||||
|
||||
class XmlXPathSelector(XPathSelector):
|
||||
"""XPathSelector for XML content"""
|
||||
__slots__ = ()
|
||||
_get_libxml2_doc = staticmethod(xmlDoc_from_xml)
|
||||
|
||||
|
||||
class HtmlXPathSelector(XPathSelector):
|
||||
"""XPathSelector for HTML content"""
|
||||
__slots__ = ()
|
||||
_get_libxml2_doc = staticmethod(xmlDoc_from_html)
|
||||
|
|
@ -5,7 +5,7 @@ garbage collection to libxml2 documents (xmlDoc).
|
|||
|
||||
import weakref
|
||||
|
||||
from scrapy.xpath.factories import xmlDoc_from_html
|
||||
from .factories import xmlDoc_from_html
|
||||
|
||||
class Libxml2Document(object):
|
||||
|
||||
|
|
@ -4,7 +4,7 @@ from scrapy.contrib.loader import ItemLoader, XPathItemLoader
|
|||
from scrapy.contrib.loader.processor import Join, Identity, TakeFirst, \
|
||||
Compose, MapCompose
|
||||
from scrapy.item import Item, Field
|
||||
from scrapy.xpath import HtmlXPathSelector
|
||||
from scrapy.selector import HtmlXPathSelector
|
||||
from scrapy.http import HtmlResponse
|
||||
|
||||
# test items
|
||||
|
|
|
|||
|
|
@ -5,9 +5,9 @@ import weakref
|
|||
import libxml2
|
||||
|
||||
from scrapy.http import TextResponse, HtmlResponse, XmlResponse
|
||||
from scrapy.xpath.selector import XmlXPathSelector, HtmlXPathSelector, \
|
||||
from scrapy.selector import XmlXPathSelector, HtmlXPathSelector, \
|
||||
XPathSelector
|
||||
from scrapy.xpath.document import Libxml2Document
|
||||
from scrapy.selector.document import Libxml2Document
|
||||
from scrapy.utils.test import libxml2debug
|
||||
|
||||
class XPathSelectorTestCase(unittest.TestCase):
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import re, csv
|
||||
|
||||
from scrapy.http import Response
|
||||
from scrapy.xpath import XmlXPathSelector
|
||||
from scrapy.selector import XmlXPathSelector
|
||||
from scrapy import log
|
||||
from scrapy.utils.python import re_rsearch, str_to_unicode
|
||||
from scrapy.utils.response import body_or_str
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import os
|
|||
|
||||
import libxml2
|
||||
|
||||
from scrapy.xpath.document import Libxml2Document
|
||||
from scrapy.selector.document import Libxml2Document
|
||||
|
||||
def libxml2debug(testfunction):
|
||||
"""Decorator for debugging libxml2 memory leaks inside a function.
|
||||
|
|
|
|||
|
|
@ -1,9 +1,5 @@
|
|||
"""
|
||||
The scrapy.xpath module provides useful classes for selecting HTML and XML
|
||||
documents using XPath. It requires libxml2 and its python bindings.
|
||||
from scrapy.selector import *
|
||||
|
||||
This parent module exports the classes most commonly used when building
|
||||
spiders, for convenience.
|
||||
"""
|
||||
|
||||
from scrapy.xpath.selector import XPathSelector, XmlXPathSelector, HtmlXPathSelector
|
||||
import warnings
|
||||
warnings.warn("scrapy.xpath module is deprecated, use scrapy.selector instead",
|
||||
DeprecationWarning, stacklevel=2)
|
||||
|
|
|
|||
|
|
@ -1,149 +1,5 @@
|
|||
"""
|
||||
XPath selectors
|
||||
from scrapy.selector import *
|
||||
|
||||
See documentation in docs/ref/selectors.rst
|
||||
"""
|
||||
|
||||
import libxml2
|
||||
|
||||
from scrapy.http import TextResponse
|
||||
from scrapy.xpath.factories import xmlDoc_from_html, xmlDoc_from_xml
|
||||
from scrapy.xpath.document import Libxml2Document
|
||||
from scrapy.utils.python import flatten, unicode_to_str
|
||||
from scrapy.utils.misc import extract_regex
|
||||
from scrapy.utils.trackref import object_ref
|
||||
from scrapy.utils.decorator import deprecated
|
||||
|
||||
class XPathSelector(object_ref):
|
||||
|
||||
__slots__ = ['doc', 'xmlNode', 'expr', '__weakref__']
|
||||
|
||||
def __init__(self, response=None, text=None, node=None, parent=None, expr=None):
|
||||
if parent:
|
||||
self.doc = parent.doc
|
||||
self.xmlNode = node
|
||||
elif response:
|
||||
self.doc = Libxml2Document(response, factory=self._get_libxml2_doc)
|
||||
self.xmlNode = self.doc.xmlDoc
|
||||
elif text:
|
||||
response = TextResponse(url=None, body=unicode_to_str(text), \
|
||||
encoding='utf-8')
|
||||
self.doc = Libxml2Document(response, factory=self._get_libxml2_doc)
|
||||
self.xmlNode = self.doc.xmlDoc
|
||||
self.expr = expr
|
||||
|
||||
def select(self, xpath):
|
||||
"""Perform the given XPath query on the current XPathSelector and
|
||||
return a XPathSelectorList of the result"""
|
||||
if hasattr(self.xmlNode, 'xpathEval'):
|
||||
self.doc.xpathContext.setContextNode(self.xmlNode)
|
||||
try:
|
||||
xpath_result = self.doc.xpathContext.xpathEval(xpath)
|
||||
except libxml2.xpathError:
|
||||
raise ValueError("Invalid XPath: %s" % xpath)
|
||||
if hasattr(xpath_result, '__iter__'):
|
||||
return XPathSelectorList([self.__class__(node=node, parent=self, \
|
||||
expr=xpath) for node in xpath_result])
|
||||
else:
|
||||
return XPathSelectorList([self.__class__(node=xpath_result, \
|
||||
parent=self, expr=xpath)])
|
||||
else:
|
||||
return XPathSelectorList([])
|
||||
|
||||
def re(self, regex):
|
||||
"""Return a list of unicode strings by applying the regex over all
|
||||
current XPath selections, and flattening the results"""
|
||||
return extract_regex(regex, self.extract(), 'utf-8')
|
||||
|
||||
def extract(self):
|
||||
"""Return a unicode string of the content referenced by the XPathSelector"""
|
||||
if isinstance(self.xmlNode, basestring):
|
||||
text = unicode(self.xmlNode, 'utf-8', errors='ignore')
|
||||
elif hasattr(self.xmlNode, 'serialize'):
|
||||
if isinstance(self.xmlNode, libxml2.xmlDoc):
|
||||
data = self.xmlNode.getRootElement().serialize('utf-8')
|
||||
text = unicode(data, 'utf-8', errors='ignore') if data else u''
|
||||
elif isinstance(self.xmlNode, libxml2.xmlAttr):
|
||||
# serialization doesn't work sometimes for xmlAttr types
|
||||
text = unicode(self.xmlNode.content, 'utf-8', errors='ignore')
|
||||
else:
|
||||
data = self.xmlNode.serialize('utf-8')
|
||||
text = unicode(data, 'utf-8', errors='ignore') if data else u''
|
||||
else:
|
||||
try:
|
||||
text = unicode(self.xmlNode, 'utf-8', errors='ignore')
|
||||
except TypeError: # catched when self.xmlNode is a float - see tests
|
||||
text = unicode(self.xmlNode)
|
||||
return text
|
||||
|
||||
def extract_unquoted(self):
|
||||
"""Get unescaped contents from the text node (no entities, no CDATA)"""
|
||||
if self.select('self::text()'):
|
||||
return unicode(self.xmlNode.getContent(), 'utf-8', errors='ignore')
|
||||
else:
|
||||
return u''
|
||||
|
||||
def register_namespace(self, prefix, uri):
|
||||
"""Register namespace so that it can be used in XPath queries"""
|
||||
self.doc.xpathContext.xpathRegisterNs(prefix, uri)
|
||||
|
||||
def _get_libxml2_doc(self, response):
|
||||
"""Return libxml2 document (xmlDoc) from response"""
|
||||
return xmlDoc_from_html(response)
|
||||
|
||||
def __nonzero__(self):
|
||||
return bool(self.extract())
|
||||
|
||||
def __str__(self):
|
||||
return "<%s (%s) xpath=%s>" % (type(self).__name__, getattr(self.xmlNode, \
|
||||
'name', type(self.xmlNode).__name__), self.expr)
|
||||
|
||||
__repr__ = __str__
|
||||
|
||||
@deprecated(use_instead='XPathSelector.select')
|
||||
def __call__(self, xpath):
|
||||
return self.select(xpath)
|
||||
|
||||
@deprecated(use_instead='XPathSelector.select')
|
||||
def x(self, xpath):
|
||||
return self.select(xpath)
|
||||
|
||||
|
||||
class XPathSelectorList(list):
|
||||
"""List of XPathSelector objects"""
|
||||
|
||||
def __getslice__(self, i, j):
|
||||
return XPathSelectorList(list.__getslice__(self, i, j))
|
||||
|
||||
def select(self, xpath):
|
||||
"""Perform the given XPath query on each XPathSelector of the list and
|
||||
return a new (flattened) XPathSelectorList of the results"""
|
||||
return XPathSelectorList(flatten([x.select(xpath) for x in self]))
|
||||
|
||||
def re(self, regex):
|
||||
"""Perform the re() method on each XPathSelector of the list, and
|
||||
return the result as a flattened list of unicode strings"""
|
||||
return flatten([x.re(regex) for x in self])
|
||||
|
||||
def extract(self):
|
||||
"""Return a list of unicode strings with the content referenced by each
|
||||
XPathSelector of the list"""
|
||||
return [x.extract() if isinstance(x, XPathSelector) else x for x in self]
|
||||
|
||||
def extract_unquoted(self):
|
||||
return [x.extract_unquoted() if isinstance(x, XPathSelector) else x for x in self]
|
||||
|
||||
@deprecated(use_instead='XPathSelectorList.select')
|
||||
def x(self, xpath):
|
||||
return self.select(xpath)
|
||||
|
||||
|
||||
class XmlXPathSelector(XPathSelector):
|
||||
"""XPathSelector for XML content"""
|
||||
__slots__ = ()
|
||||
_get_libxml2_doc = staticmethod(xmlDoc_from_xml)
|
||||
|
||||
class HtmlXPathSelector(XPathSelector):
|
||||
"""XPathSelector for HTML content"""
|
||||
__slots__ = ()
|
||||
_get_libxml2_doc = staticmethod(xmlDoc_from_html)
|
||||
import warnings
|
||||
warnings.warn("scrapy.xpath.selector module is deprecated, use scrapy.selector instead",
|
||||
DeprecationWarning, stacklevel=2)
|
||||
|
|
|
|||
Loading…
Reference in New Issue